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 GList *compose_list = NULL;
187 static GSList *extra_headers = NULL;
189 static Compose *compose_generic_new (PrefsAccount *account,
193 GList *listAddress );
195 static Compose *compose_create (PrefsAccount *account,
200 static void compose_entry_mark_default_to (Compose *compose,
201 const gchar *address);
202 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
203 ComposeQuoteMode quote_mode,
207 static Compose *compose_forward_multiple (PrefsAccount *account,
208 GSList *msginfo_list);
209 static Compose *compose_reply (MsgInfo *msginfo,
210 ComposeQuoteMode quote_mode,
215 static Compose *compose_reply_mode (ComposeMode mode,
216 GSList *msginfo_list,
218 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
219 static void compose_update_privacy_systems_menu(Compose *compose);
221 static GtkWidget *compose_account_option_menu_create
223 static void compose_set_out_encoding (Compose *compose);
224 static void compose_set_template_menu (Compose *compose);
225 static void compose_destroy (Compose *compose);
227 static MailField compose_entries_set (Compose *compose,
229 ComposeEntryType to_type);
230 static gint compose_parse_header (Compose *compose,
232 static gint compose_parse_manual_headers (Compose *compose,
234 HeaderEntry *entries);
235 static gchar *compose_parse_references (const gchar *ref,
238 static gchar *compose_quote_fmt (Compose *compose,
244 gboolean need_unescape,
245 const gchar *err_msg);
247 static void compose_reply_set_entry (Compose *compose,
253 followup_and_reply_to);
254 static void compose_reedit_set_entry (Compose *compose,
257 static void compose_insert_sig (Compose *compose,
259 static ComposeInsertResult compose_insert_file (Compose *compose,
262 static gboolean compose_attach_append (Compose *compose,
265 const gchar *content_type,
266 const gchar *charset);
267 static void compose_attach_parts (Compose *compose,
270 static gboolean compose_beautify_paragraph (Compose *compose,
271 GtkTextIter *par_iter,
273 static void compose_wrap_all (Compose *compose);
274 static void compose_wrap_all_full (Compose *compose,
277 static void compose_set_title (Compose *compose);
278 static void compose_select_account (Compose *compose,
279 PrefsAccount *account,
282 static PrefsAccount *compose_current_mail_account(void);
283 /* static gint compose_send (Compose *compose); */
284 static gboolean compose_check_for_valid_recipient
286 static gboolean compose_check_entries (Compose *compose,
287 gboolean check_everything);
288 static gint compose_write_to_file (Compose *compose,
291 gboolean attach_parts);
292 static gint compose_write_body_to_file (Compose *compose,
294 static gint compose_remove_reedit_target (Compose *compose,
296 static void compose_remove_draft (Compose *compose);
297 static gint compose_queue_sub (Compose *compose,
301 gboolean check_subject,
302 gboolean remove_reedit_target);
303 static int compose_add_attachments (Compose *compose,
305 static gchar *compose_get_header (Compose *compose);
306 static gchar *compose_get_manual_headers_info (Compose *compose);
308 static void compose_convert_header (Compose *compose,
313 gboolean addr_field);
315 static void compose_attach_info_free (AttachInfo *ainfo);
316 static void compose_attach_remove_selected (GtkAction *action,
319 static void compose_template_apply (Compose *compose,
322 static void compose_attach_property (GtkAction *action,
324 static void compose_attach_property_create (gboolean *cancelled);
325 static void attach_property_ok (GtkWidget *widget,
326 gboolean *cancelled);
327 static void attach_property_cancel (GtkWidget *widget,
328 gboolean *cancelled);
329 static gint attach_property_delete_event (GtkWidget *widget,
331 gboolean *cancelled);
332 static gboolean attach_property_key_pressed (GtkWidget *widget,
334 gboolean *cancelled);
336 static void compose_exec_ext_editor (Compose *compose);
338 static gint compose_exec_ext_editor_real (const gchar *file,
339 GdkNativeWindow socket_wid);
340 static gboolean compose_ext_editor_kill (Compose *compose);
341 static gboolean compose_input_cb (GIOChannel *source,
342 GIOCondition condition,
344 static void compose_set_ext_editor_sensitive (Compose *compose,
346 static gboolean compose_get_ext_editor_cmd_valid();
347 static gboolean compose_get_ext_editor_uses_socket();
348 static gboolean compose_ext_editor_plug_removed_cb
351 #endif /* G_OS_UNIX */
353 static void compose_undo_state_changed (UndoMain *undostruct,
358 static void compose_create_header_entry (Compose *compose);
359 static void compose_add_header_entry (Compose *compose, const gchar *header,
360 gchar *text, ComposePrefType pref_type);
361 static void compose_remove_header_entries(Compose *compose);
363 static void compose_update_priority_menu_item(Compose * compose);
365 static void compose_spell_menu_changed (void *data);
366 static void compose_dict_changed (void *data);
368 static void compose_add_field_list ( Compose *compose,
369 GList *listAddress );
371 /* callback functions */
373 static void compose_notebook_size_alloc (GtkNotebook *notebook,
374 GtkAllocation *allocation,
376 static gboolean compose_edit_size_alloc (GtkEditable *widget,
377 GtkAllocation *allocation,
378 GtkSHRuler *shruler);
379 static void attach_selected (GtkTreeView *tree_view,
380 GtkTreePath *tree_path,
381 GtkTreeViewColumn *column,
383 static gboolean attach_button_pressed (GtkWidget *widget,
384 GdkEventButton *event,
386 static gboolean attach_key_pressed (GtkWidget *widget,
389 static void compose_send_cb (GtkAction *action, gpointer data);
390 static void compose_send_later_cb (GtkAction *action, gpointer data);
392 static void compose_save_cb (GtkAction *action,
395 static void compose_attach_cb (GtkAction *action,
397 static void compose_insert_file_cb (GtkAction *action,
399 static void compose_insert_sig_cb (GtkAction *action,
401 static void compose_replace_sig_cb (GtkAction *action,
404 static void compose_close_cb (GtkAction *action,
406 static void compose_print_cb (GtkAction *action,
409 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
411 static void compose_address_cb (GtkAction *action,
413 static void about_show_cb (GtkAction *action,
415 static void compose_template_activate_cb(GtkWidget *widget,
418 static void compose_ext_editor_cb (GtkAction *action,
421 static gint compose_delete_cb (GtkWidget *widget,
425 static void compose_undo_cb (GtkAction *action,
427 static void compose_redo_cb (GtkAction *action,
429 static void compose_cut_cb (GtkAction *action,
431 static void compose_copy_cb (GtkAction *action,
433 static void compose_paste_cb (GtkAction *action,
435 static void compose_paste_as_quote_cb (GtkAction *action,
437 static void compose_paste_no_wrap_cb (GtkAction *action,
439 static void compose_paste_wrap_cb (GtkAction *action,
441 static void compose_allsel_cb (GtkAction *action,
444 static void compose_advanced_action_cb (GtkAction *action,
447 static void compose_grab_focus_cb (GtkWidget *widget,
450 static void compose_changed_cb (GtkTextBuffer *textbuf,
453 static void compose_wrap_cb (GtkAction *action,
455 static void compose_wrap_all_cb (GtkAction *action,
457 static void compose_find_cb (GtkAction *action,
459 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
461 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
464 static void compose_toggle_ruler_cb (GtkToggleAction *action,
466 static void compose_toggle_sign_cb (GtkToggleAction *action,
468 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
470 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
471 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
472 static void activate_privacy_system (Compose *compose,
473 PrefsAccount *account,
475 static void compose_use_signing(Compose *compose, gboolean use_signing);
476 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
477 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
479 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
481 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
482 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
483 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
485 static void compose_attach_drag_received_cb (GtkWidget *widget,
486 GdkDragContext *drag_context,
489 GtkSelectionData *data,
493 static void compose_insert_drag_received_cb (GtkWidget *widget,
494 GdkDragContext *drag_context,
497 GtkSelectionData *data,
501 static void compose_header_drag_received_cb (GtkWidget *widget,
502 GdkDragContext *drag_context,
505 GtkSelectionData *data,
510 static gboolean compose_drag_drop (GtkWidget *widget,
511 GdkDragContext *drag_context,
513 guint time, gpointer user_data);
514 static gboolean completion_set_focus_to_subject
519 static void text_inserted (GtkTextBuffer *buffer,
524 static Compose *compose_generic_reply(MsgInfo *msginfo,
525 ComposeQuoteMode quote_mode,
529 gboolean followup_and_reply_to,
532 static void compose_headerentry_changed_cb (GtkWidget *entry,
533 ComposeHeaderEntry *headerentry);
534 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
536 ComposeHeaderEntry *headerentry);
537 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
538 ComposeHeaderEntry *headerentry);
540 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
542 static void compose_allow_user_actions (Compose *compose, gboolean allow);
544 static void compose_nothing_cb (GtkAction *action, gpointer data)
550 static void compose_check_all (GtkAction *action, gpointer data);
551 static void compose_highlight_all (GtkAction *action, gpointer data);
552 static void compose_check_backwards (GtkAction *action, gpointer data);
553 static void compose_check_forwards_go (GtkAction *action, gpointer data);
556 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
558 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
561 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
562 FolderItem *folder_item);
564 static void compose_attach_update_label(Compose *compose);
565 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
566 gboolean respect_default_to);
567 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
568 static void from_name_activate_cb(GtkWidget *widget, gpointer data);
570 static GtkActionEntry compose_popup_entries[] =
572 {"Compose", NULL, "Compose" },
573 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
574 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
575 {"Compose/---", NULL, "---", NULL, NULL, NULL },
576 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
579 static GtkActionEntry compose_entries[] =
581 {"Menu", NULL, "Menu" },
583 {"Message", NULL, N_("_Message") },
584 {"Edit", NULL, N_("_Edit") },
586 {"Spelling", NULL, N_("_Spelling") },
588 {"Options", NULL, N_("_Options") },
589 {"Tools", NULL, N_("_Tools") },
590 {"Help", NULL, N_("_Help") },
592 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
593 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
594 {"Message/---", NULL, "---" },
596 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
597 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
598 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
599 {"Message/ReplaceSig", NULL, N_("_Replace signature"), NULL, NULL, G_CALLBACK(compose_replace_sig_cb) },
600 /* {"Message/---", NULL, "---" }, */
601 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
602 /* {"Message/---", NULL, "---" }, */
603 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
604 /* {"Message/---", NULL, "---" }, */
605 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
608 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
609 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
610 {"Edit/---", NULL, "---" },
612 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
613 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
614 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
616 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
617 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
618 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
619 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
621 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
623 {"Edit/Advanced", NULL, N_("A_dvanced") },
624 {"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*/
625 {"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*/
626 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
627 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
628 {"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*/
629 {"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*/
630 {"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*/
631 {"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*/
632 {"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*/
633 {"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*/
634 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
635 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
636 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
637 {"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*/
639 /* {"Edit/---", NULL, "---" }, */
640 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
642 /* {"Edit/---", NULL, "---" }, */
643 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
644 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
645 /* {"Edit/---", NULL, "---" }, */
646 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
649 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
650 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
651 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
652 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
654 {"Spelling/---", NULL, "---" },
655 {"Spelling/Options", NULL, N_("_Options") },
660 {"Options/ReplyMode", NULL, N_("Reply _mode") },
661 {"Options/---", NULL, "---" },
662 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
663 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
665 /* {"Options/---", NULL, "---" }, */
667 {"Options/Priority", NULL, N_("_Priority") },
669 {"Options/Encoding", NULL, N_("Character _encoding") },
670 {"Options/Encoding/---", NULL, "---" },
671 #define ENC_ACTION(cs_char,c_char,string) \
672 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
674 {"Options/Encoding/Western", NULL, N_("Western European") },
675 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
676 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
677 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
678 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
679 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
680 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
681 {"Options/Encoding/Korean", NULL, N_("Korean") },
682 {"Options/Encoding/Thai", NULL, N_("Thai") },
685 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
687 {"Tools/Template", NULL, N_("_Template") },
688 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
689 {"Tools/Actions", NULL, N_("Actio_ns") },
690 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
693 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
696 static GtkToggleActionEntry compose_toggle_entries[] =
698 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
699 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
700 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
701 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
702 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
703 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
704 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
707 static GtkRadioActionEntry compose_radio_rm_entries[] =
709 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
710 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
711 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
712 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
715 static GtkRadioActionEntry compose_radio_prio_entries[] =
717 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
718 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
719 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
720 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
721 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
724 static GtkRadioActionEntry compose_radio_enc_entries[] =
726 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
729 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
730 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
731 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
732 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
733 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
734 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
735 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
736 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
737 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
738 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
739 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
740 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
741 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
742 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
743 ENC_ACTION("Cyrillic/"CS_MACCYR, C_MACCYR, "_Mac-Cyrillic"), /* RADIO compose_set_encoding_cb */
744 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
745 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
746 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
747 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
748 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
749 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
750 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
751 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
752 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
753 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
754 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
755 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
756 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
757 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
758 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
761 static GtkTargetEntry compose_mime_types[] =
763 {"text/uri-list", 0, 0},
764 {"UTF8_STRING", 0, 0},
768 static gboolean compose_put_existing_to_front(MsgInfo *info)
770 const GList *compose_list = compose_get_compose_list();
771 const GList *elem = NULL;
774 for (elem = compose_list; elem != NULL && elem->data != NULL;
776 Compose *c = (Compose*)elem->data;
778 if (!c->targetinfo || !c->targetinfo->msgid ||
782 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
783 gtkut_window_popup(c->window);
791 static GdkColor quote_color1 =
792 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
793 static GdkColor quote_color2 =
794 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
795 static GdkColor quote_color3 =
796 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
798 static GdkColor quote_bgcolor1 =
799 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
800 static GdkColor quote_bgcolor2 =
801 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
802 static GdkColor quote_bgcolor3 =
803 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
805 static GdkColor signature_color = {
812 static GdkColor uri_color = {
819 static void compose_create_tags(GtkTextView *text, Compose *compose)
821 GtkTextBuffer *buffer;
822 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
823 #if !GTK_CHECK_VERSION(2, 24, 0)
830 buffer = gtk_text_view_get_buffer(text);
832 if (prefs_common.enable_color) {
833 /* grab the quote colors, converting from an int to a GdkColor */
834 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
836 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
838 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
840 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
842 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
844 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
846 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
848 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
851 signature_color = quote_color1 = quote_color2 = quote_color3 =
852 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
855 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
856 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
857 "foreground-gdk", "e_color1,
858 "paragraph-background-gdk", "e_bgcolor1,
860 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
861 "foreground-gdk", "e_color2,
862 "paragraph-background-gdk", "e_bgcolor2,
864 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
865 "foreground-gdk", "e_color3,
866 "paragraph-background-gdk", "e_bgcolor3,
869 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
870 "foreground-gdk", "e_color1,
872 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
873 "foreground-gdk", "e_color2,
875 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
876 "foreground-gdk", "e_color3,
880 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
881 "foreground-gdk", &signature_color,
884 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
885 "foreground-gdk", &uri_color,
887 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
888 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
890 #if !GTK_CHECK_VERSION(2, 24, 0)
891 color[0] = quote_color1;
892 color[1] = quote_color2;
893 color[2] = quote_color3;
894 color[3] = quote_bgcolor1;
895 color[4] = quote_bgcolor2;
896 color[5] = quote_bgcolor3;
897 color[6] = signature_color;
898 color[7] = uri_color;
900 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
901 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
903 for (i = 0; i < 8; i++) {
904 if (success[i] == FALSE) {
905 g_warning("Compose: color allocation failed.");
906 quote_color1 = quote_color2 = quote_color3 =
907 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
908 signature_color = uri_color = black;
914 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
917 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
920 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
922 return compose_generic_new(account, mailto, item, NULL, NULL);
925 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
927 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
930 #define SCROLL_TO_CURSOR(compose) { \
931 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
932 gtk_text_view_get_buffer( \
933 GTK_TEXT_VIEW(compose->text))); \
934 gtk_text_view_scroll_mark_onscreen( \
935 GTK_TEXT_VIEW(compose->text), \
939 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
942 if (folderidentifier) {
943 #if !GTK_CHECK_VERSION(2, 24, 0)
944 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
946 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
948 prefs_common.compose_save_to_history = add_history(
949 prefs_common.compose_save_to_history, folderidentifier);
950 #if !GTK_CHECK_VERSION(2, 24, 0)
951 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
952 prefs_common.compose_save_to_history);
954 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
955 prefs_common.compose_save_to_history);
959 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
960 if (folderidentifier)
961 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
963 gtk_entry_set_text(GTK_ENTRY(entry), "");
966 static gchar *compose_get_save_to(Compose *compose)
969 gchar *result = NULL;
970 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
971 result = gtk_editable_get_chars(entry, 0, -1);
974 #if !GTK_CHECK_VERSION(2, 24, 0)
975 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
977 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
979 prefs_common.compose_save_to_history = add_history(
980 prefs_common.compose_save_to_history, result);
981 #if !GTK_CHECK_VERSION(2, 24, 0)
982 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
983 prefs_common.compose_save_to_history);
985 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
986 prefs_common.compose_save_to_history);
992 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
993 GList *attach_files, GList *listAddress )
996 GtkTextView *textview;
997 GtkTextBuffer *textbuf;
999 const gchar *subject_format = NULL;
1000 const gchar *body_format = NULL;
1001 gchar *mailto_from = NULL;
1002 PrefsAccount *mailto_account = NULL;
1003 MsgInfo* dummyinfo = NULL;
1004 gint cursor_pos = -1;
1005 MailField mfield = NO_FIELD_PRESENT;
1009 /* check if mailto defines a from */
1010 if (mailto && *mailto != '\0') {
1011 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1012 /* mailto defines a from, check if we can get account prefs from it,
1013 if not, the account prefs will be guessed using other ways, but we'll keep
1016 mailto_account = account_find_from_address(mailto_from, TRUE);
1017 if (mailto_account == NULL) {
1019 Xstrdup_a(tmp_from, mailto_from, return NULL);
1020 extract_address(tmp_from);
1021 mailto_account = account_find_from_address(tmp_from, TRUE);
1025 account = mailto_account;
1028 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1029 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
1030 account = account_find_from_id(item->prefs->default_account);
1032 /* if no account prefs set, fallback to the current one */
1033 if (!account) account = cur_account;
1034 cm_return_val_if_fail(account != NULL, NULL);
1036 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1038 /* override from name if mailto asked for it */
1040 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1041 g_free(mailto_from);
1043 /* override from name according to folder properties */
1044 if (item && item->prefs &&
1045 item->prefs->compose_with_format &&
1046 item->prefs->compose_override_from_format &&
1047 *item->prefs->compose_override_from_format != '\0') {
1052 dummyinfo = compose_msginfo_new_from_compose(compose);
1054 /* decode \-escape sequences in the internal representation of the quote format */
1055 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1056 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1059 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1060 compose->gtkaspell);
1062 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1064 quote_fmt_scan_string(tmp);
1067 buf = quote_fmt_get_buffer();
1069 alertpanel_error(_("New message From format error."));
1071 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1072 quote_fmt_reset_vartable();
1077 compose->replyinfo = NULL;
1078 compose->fwdinfo = NULL;
1080 textview = GTK_TEXT_VIEW(compose->text);
1081 textbuf = gtk_text_view_get_buffer(textview);
1082 compose_create_tags(textview, compose);
1084 undo_block(compose->undostruct);
1086 compose_set_dictionaries_from_folder_prefs(compose, item);
1089 if (account->auto_sig)
1090 compose_insert_sig(compose, FALSE);
1091 gtk_text_buffer_get_start_iter(textbuf, &iter);
1092 gtk_text_buffer_place_cursor(textbuf, &iter);
1094 if (account->protocol != A_NNTP) {
1095 if (mailto && *mailto != '\0') {
1096 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1099 compose_set_folder_prefs(compose, item, TRUE);
1101 if (item && item->ret_rcpt) {
1102 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1105 if (mailto && *mailto != '\0') {
1106 if (!strchr(mailto, '@'))
1107 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1109 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1110 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1111 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1112 mfield = TO_FIELD_PRESENT;
1115 * CLAWS: just don't allow return receipt request, even if the user
1116 * may want to send an email. simple but foolproof.
1118 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1120 compose_add_field_list( compose, listAddress );
1122 if (item && item->prefs && item->prefs->compose_with_format) {
1123 subject_format = item->prefs->compose_subject_format;
1124 body_format = item->prefs->compose_body_format;
1125 } else if (account->compose_with_format) {
1126 subject_format = account->compose_subject_format;
1127 body_format = account->compose_body_format;
1128 } else if (prefs_common.compose_with_format) {
1129 subject_format = prefs_common.compose_subject_format;
1130 body_format = prefs_common.compose_body_format;
1133 if (subject_format || body_format) {
1136 && *subject_format != '\0' )
1138 gchar *subject = NULL;
1143 dummyinfo = compose_msginfo_new_from_compose(compose);
1145 /* decode \-escape sequences in the internal representation of the quote format */
1146 tmp = g_malloc(strlen(subject_format)+1);
1147 pref_get_unescaped_pref(tmp, subject_format);
1149 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1151 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1152 compose->gtkaspell);
1154 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1156 quote_fmt_scan_string(tmp);
1159 buf = quote_fmt_get_buffer();
1161 alertpanel_error(_("New message subject format error."));
1163 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1164 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1165 quote_fmt_reset_vartable();
1169 mfield = SUBJECT_FIELD_PRESENT;
1173 && *body_format != '\0' )
1176 GtkTextBuffer *buffer;
1177 GtkTextIter start, end;
1181 dummyinfo = compose_msginfo_new_from_compose(compose);
1183 text = GTK_TEXT_VIEW(compose->text);
1184 buffer = gtk_text_view_get_buffer(text);
1185 gtk_text_buffer_get_start_iter(buffer, &start);
1186 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1187 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1189 compose_quote_fmt(compose, dummyinfo,
1191 NULL, tmp, FALSE, TRUE,
1192 _("The body of the \"New message\" template has an error at line %d."));
1193 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1194 quote_fmt_reset_vartable();
1198 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1199 gtkaspell_highlight_all(compose->gtkaspell);
1201 mfield = BODY_FIELD_PRESENT;
1205 procmsg_msginfo_free( &dummyinfo );
1211 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1212 ainfo = (AttachInfo *) curr->data;
1213 compose_attach_append(compose, ainfo->file, ainfo->file,
1214 ainfo->content_type, ainfo->charset);
1218 compose_show_first_last_header(compose, TRUE);
1220 /* Set save folder */
1221 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1222 gchar *folderidentifier;
1224 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1225 folderidentifier = folder_item_get_identifier(item);
1226 compose_set_save_to(compose, folderidentifier);
1227 g_free(folderidentifier);
1230 /* Place cursor according to provided input (mfield) */
1232 case NO_FIELD_PRESENT:
1233 if (compose->header_last)
1234 gtk_widget_grab_focus(compose->header_last->entry);
1236 case TO_FIELD_PRESENT:
1237 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1239 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1242 gtk_widget_grab_focus(compose->subject_entry);
1244 case SUBJECT_FIELD_PRESENT:
1245 textview = GTK_TEXT_VIEW(compose->text);
1248 textbuf = gtk_text_view_get_buffer(textview);
1251 mark = gtk_text_buffer_get_insert(textbuf);
1252 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1253 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1255 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1256 * only defers where it comes to the variable body
1257 * is not null. If no body is present compose->text
1258 * will be null in which case you cannot place the
1259 * cursor inside the component so. An empty component
1260 * is therefore created before placing the cursor
1262 case BODY_FIELD_PRESENT:
1263 cursor_pos = quote_fmt_get_cursor_pos();
1264 if (cursor_pos == -1)
1265 gtk_widget_grab_focus(compose->header_last->entry);
1267 gtk_widget_grab_focus(compose->text);
1271 undo_unblock(compose->undostruct);
1273 if (prefs_common.auto_exteditor)
1274 compose_exec_ext_editor(compose);
1276 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
1278 SCROLL_TO_CURSOR(compose);
1280 compose->modified = FALSE;
1281 compose_set_title(compose);
1283 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1288 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1289 gboolean override_pref, const gchar *system)
1291 const gchar *privacy = NULL;
1293 cm_return_if_fail(compose != NULL);
1294 cm_return_if_fail(account != NULL);
1296 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1299 if (account->default_privacy_system && strlen(account->default_privacy_system))
1300 privacy = account->default_privacy_system;
1304 GSList *privacy_avail = privacy_get_system_ids();
1305 if (privacy_avail && g_slist_length(privacy_avail)) {
1306 privacy = (gchar *)(privacy_avail->data);
1309 if (privacy != NULL) {
1311 g_free(compose->privacy_system);
1312 compose->privacy_system = NULL;
1313 g_free(compose->encdata);
1314 compose->encdata = NULL;
1316 if (compose->privacy_system == NULL)
1317 compose->privacy_system = g_strdup(privacy);
1318 else if (*(compose->privacy_system) == '\0') {
1319 g_free(compose->privacy_system);
1320 g_free(compose->encdata);
1321 compose->encdata = NULL;
1322 compose->privacy_system = g_strdup(privacy);
1324 compose_update_privacy_system_menu_item(compose, FALSE);
1325 compose_use_encryption(compose, TRUE);
1329 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1331 const gchar *privacy = NULL;
1333 if (account->default_privacy_system && strlen(account->default_privacy_system))
1334 privacy = account->default_privacy_system;
1338 GSList *privacy_avail = privacy_get_system_ids();
1339 if (privacy_avail && g_slist_length(privacy_avail)) {
1340 privacy = (gchar *)(privacy_avail->data);
1344 if (privacy != NULL) {
1346 g_free(compose->privacy_system);
1347 compose->privacy_system = NULL;
1348 g_free(compose->encdata);
1349 compose->encdata = NULL;
1351 if (compose->privacy_system == NULL)
1352 compose->privacy_system = g_strdup(privacy);
1353 compose_update_privacy_system_menu_item(compose, FALSE);
1354 compose_use_signing(compose, TRUE);
1358 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1362 Compose *compose = NULL;
1364 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1366 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1367 cm_return_val_if_fail(msginfo != NULL, NULL);
1369 list_len = g_slist_length(msginfo_list);
1373 case COMPOSE_REPLY_TO_ADDRESS:
1374 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1375 FALSE, prefs_common.default_reply_list, FALSE, body);
1377 case COMPOSE_REPLY_WITH_QUOTE:
1378 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1379 FALSE, prefs_common.default_reply_list, FALSE, body);
1381 case COMPOSE_REPLY_WITHOUT_QUOTE:
1382 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1383 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1385 case COMPOSE_REPLY_TO_SENDER:
1386 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1387 FALSE, FALSE, TRUE, body);
1389 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1390 compose = compose_followup_and_reply_to(msginfo,
1391 COMPOSE_QUOTE_CHECK,
1392 FALSE, FALSE, body);
1394 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1395 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1396 FALSE, FALSE, TRUE, body);
1398 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1399 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1400 FALSE, FALSE, TRUE, NULL);
1402 case COMPOSE_REPLY_TO_ALL:
1403 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1404 TRUE, FALSE, FALSE, body);
1406 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1407 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1408 TRUE, FALSE, FALSE, body);
1410 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1411 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1412 TRUE, FALSE, FALSE, NULL);
1414 case COMPOSE_REPLY_TO_LIST:
1415 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1416 FALSE, TRUE, FALSE, body);
1418 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1419 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1420 FALSE, TRUE, FALSE, body);
1422 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1423 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1424 FALSE, TRUE, FALSE, NULL);
1426 case COMPOSE_FORWARD:
1427 if (prefs_common.forward_as_attachment) {
1428 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1431 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1435 case COMPOSE_FORWARD_INLINE:
1436 /* check if we reply to more than one Message */
1437 if (list_len == 1) {
1438 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1441 /* more messages FALL THROUGH */
1442 case COMPOSE_FORWARD_AS_ATTACH:
1443 compose = compose_forward_multiple(NULL, msginfo_list);
1445 case COMPOSE_REDIRECT:
1446 compose = compose_redirect(NULL, msginfo, FALSE);
1449 g_warning("compose_reply_mode(): invalid Compose Mode: %d", mode);
1452 if (compose == NULL) {
1453 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1457 compose->rmode = mode;
1458 switch (compose->rmode) {
1460 case COMPOSE_REPLY_WITH_QUOTE:
1461 case COMPOSE_REPLY_WITHOUT_QUOTE:
1462 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1463 debug_print("reply mode Normal\n");
1464 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1465 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1467 case COMPOSE_REPLY_TO_SENDER:
1468 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1469 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1470 debug_print("reply mode Sender\n");
1471 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1473 case COMPOSE_REPLY_TO_ALL:
1474 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1475 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1476 debug_print("reply mode All\n");
1477 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1479 case COMPOSE_REPLY_TO_LIST:
1480 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1481 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1482 debug_print("reply mode List\n");
1483 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1485 case COMPOSE_REPLY_TO_ADDRESS:
1486 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1494 static Compose *compose_reply(MsgInfo *msginfo,
1495 ComposeQuoteMode quote_mode,
1501 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1502 to_sender, FALSE, body);
1505 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1506 ComposeQuoteMode quote_mode,
1511 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1512 to_sender, TRUE, body);
1515 static void compose_extract_original_charset(Compose *compose)
1517 MsgInfo *info = NULL;
1518 if (compose->replyinfo) {
1519 info = compose->replyinfo;
1520 } else if (compose->fwdinfo) {
1521 info = compose->fwdinfo;
1522 } else if (compose->targetinfo) {
1523 info = compose->targetinfo;
1526 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1527 MimeInfo *partinfo = mimeinfo;
1528 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1529 partinfo = procmime_mimeinfo_next(partinfo);
1531 compose->orig_charset =
1532 g_strdup(procmime_mimeinfo_get_parameter(
1533 partinfo, "charset"));
1535 procmime_mimeinfo_free_all(&mimeinfo);
1539 #define SIGNAL_BLOCK(buffer) { \
1540 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1541 G_CALLBACK(compose_changed_cb), \
1543 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1544 G_CALLBACK(text_inserted), \
1548 #define SIGNAL_UNBLOCK(buffer) { \
1549 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1550 G_CALLBACK(compose_changed_cb), \
1552 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1553 G_CALLBACK(text_inserted), \
1557 static Compose *compose_generic_reply(MsgInfo *msginfo,
1558 ComposeQuoteMode quote_mode,
1559 gboolean to_all, gboolean to_ml,
1561 gboolean followup_and_reply_to,
1565 PrefsAccount *account = NULL;
1566 GtkTextView *textview;
1567 GtkTextBuffer *textbuf;
1568 gboolean quote = FALSE;
1569 const gchar *qmark = NULL;
1570 const gchar *body_fmt = NULL;
1571 gchar *s_system = NULL;
1573 cm_return_val_if_fail(msginfo != NULL, NULL);
1574 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1576 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1578 cm_return_val_if_fail(account != NULL, NULL);
1580 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1582 compose->updating = TRUE;
1584 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1585 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1587 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1588 if (!compose->replyinfo)
1589 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1591 compose_extract_original_charset(compose);
1593 if (msginfo->folder && msginfo->folder->ret_rcpt)
1594 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1596 /* Set save folder */
1597 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1598 gchar *folderidentifier;
1600 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1601 folderidentifier = folder_item_get_identifier(msginfo->folder);
1602 compose_set_save_to(compose, folderidentifier);
1603 g_free(folderidentifier);
1606 if (compose_parse_header(compose, msginfo) < 0) {
1607 compose->updating = FALSE;
1608 compose_destroy(compose);
1612 /* override from name according to folder properties */
1613 if (msginfo->folder && msginfo->folder->prefs &&
1614 msginfo->folder->prefs->reply_with_format &&
1615 msginfo->folder->prefs->reply_override_from_format &&
1616 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1621 /* decode \-escape sequences in the internal representation of the quote format */
1622 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1623 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1626 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1627 compose->gtkaspell);
1629 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1631 quote_fmt_scan_string(tmp);
1634 buf = quote_fmt_get_buffer();
1636 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1638 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1639 quote_fmt_reset_vartable();
1644 textview = (GTK_TEXT_VIEW(compose->text));
1645 textbuf = gtk_text_view_get_buffer(textview);
1646 compose_create_tags(textview, compose);
1648 undo_block(compose->undostruct);
1650 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1651 gtkaspell_block_check(compose->gtkaspell);
1654 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1655 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1656 /* use the reply format of folder (if enabled), or the account's one
1657 (if enabled) or fallback to the global reply format, which is always
1658 enabled (even if empty), and use the relevant quotemark */
1660 if (msginfo->folder && msginfo->folder->prefs &&
1661 msginfo->folder->prefs->reply_with_format) {
1662 qmark = msginfo->folder->prefs->reply_quotemark;
1663 body_fmt = msginfo->folder->prefs->reply_body_format;
1665 } else if (account->reply_with_format) {
1666 qmark = account->reply_quotemark;
1667 body_fmt = account->reply_body_format;
1670 qmark = prefs_common.quotemark;
1671 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1672 body_fmt = gettext(prefs_common.quotefmt);
1679 /* empty quotemark is not allowed */
1680 if (qmark == NULL || *qmark == '\0')
1682 compose_quote_fmt(compose, compose->replyinfo,
1683 body_fmt, qmark, body, FALSE, TRUE,
1684 _("The body of the \"Reply\" template has an error at line %d."));
1685 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1686 quote_fmt_reset_vartable();
1689 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1690 compose_force_encryption(compose, account, FALSE, s_system);
1693 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1694 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1695 compose_force_signing(compose, account, s_system);
1699 SIGNAL_BLOCK(textbuf);
1701 if (account->auto_sig)
1702 compose_insert_sig(compose, FALSE);
1704 compose_wrap_all(compose);
1707 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1708 gtkaspell_highlight_all(compose->gtkaspell);
1709 gtkaspell_unblock_check(compose->gtkaspell);
1711 SIGNAL_UNBLOCK(textbuf);
1713 gtk_widget_grab_focus(compose->text);
1715 undo_unblock(compose->undostruct);
1717 if (prefs_common.auto_exteditor)
1718 compose_exec_ext_editor(compose);
1720 compose->modified = FALSE;
1721 compose_set_title(compose);
1723 compose->updating = FALSE;
1724 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1725 SCROLL_TO_CURSOR(compose);
1727 if (compose->deferred_destroy) {
1728 compose_destroy(compose);
1736 #define INSERT_FW_HEADER(var, hdr) \
1737 if (msginfo->var && *msginfo->var) { \
1738 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1739 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1740 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1743 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1744 gboolean as_attach, const gchar *body,
1745 gboolean no_extedit,
1749 GtkTextView *textview;
1750 GtkTextBuffer *textbuf;
1751 gint cursor_pos = -1;
1754 cm_return_val_if_fail(msginfo != NULL, NULL);
1755 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1758 !(account = compose_guess_forward_account_from_msginfo
1760 account = cur_account;
1762 if (!prefs_common.forward_as_attachment)
1763 mode = COMPOSE_FORWARD_INLINE;
1765 mode = COMPOSE_FORWARD;
1766 compose = compose_create(account, msginfo->folder, mode, batch);
1768 compose->updating = TRUE;
1769 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1770 if (!compose->fwdinfo)
1771 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1773 compose_extract_original_charset(compose);
1775 if (msginfo->subject && *msginfo->subject) {
1776 gchar *buf, *buf2, *p;
1778 buf = p = g_strdup(msginfo->subject);
1779 p += subject_get_prefix_length(p);
1780 memmove(buf, p, strlen(p) + 1);
1782 buf2 = g_strdup_printf("Fw: %s", buf);
1783 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1789 /* override from name according to folder properties */
1790 if (msginfo->folder && msginfo->folder->prefs &&
1791 msginfo->folder->prefs->forward_with_format &&
1792 msginfo->folder->prefs->forward_override_from_format &&
1793 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1797 MsgInfo *full_msginfo = NULL;
1800 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1802 full_msginfo = procmsg_msginfo_copy(msginfo);
1804 /* decode \-escape sequences in the internal representation of the quote format */
1805 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1806 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1809 gtkaspell_block_check(compose->gtkaspell);
1810 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1811 compose->gtkaspell);
1813 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1815 quote_fmt_scan_string(tmp);
1818 buf = quote_fmt_get_buffer();
1820 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1822 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1823 quote_fmt_reset_vartable();
1826 procmsg_msginfo_free(&full_msginfo);
1829 textview = GTK_TEXT_VIEW(compose->text);
1830 textbuf = gtk_text_view_get_buffer(textview);
1831 compose_create_tags(textview, compose);
1833 undo_block(compose->undostruct);
1837 msgfile = procmsg_get_message_file(msginfo);
1838 if (!is_file_exist(msgfile))
1839 g_warning("%s: file does not exist", msgfile);
1841 compose_attach_append(compose, msgfile, msgfile,
1842 "message/rfc822", NULL);
1846 const gchar *qmark = NULL;
1847 const gchar *body_fmt = NULL;
1848 MsgInfo *full_msginfo;
1850 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1852 full_msginfo = procmsg_msginfo_copy(msginfo);
1854 /* use the forward format of folder (if enabled), or the account's one
1855 (if enabled) or fallback to the global forward format, which is always
1856 enabled (even if empty), and use the relevant quotemark */
1857 if (msginfo->folder && msginfo->folder->prefs &&
1858 msginfo->folder->prefs->forward_with_format) {
1859 qmark = msginfo->folder->prefs->forward_quotemark;
1860 body_fmt = msginfo->folder->prefs->forward_body_format;
1862 } else if (account->forward_with_format) {
1863 qmark = account->forward_quotemark;
1864 body_fmt = account->forward_body_format;
1867 qmark = prefs_common.fw_quotemark;
1868 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1869 body_fmt = gettext(prefs_common.fw_quotefmt);
1874 /* empty quotemark is not allowed */
1875 if (qmark == NULL || *qmark == '\0')
1878 compose_quote_fmt(compose, full_msginfo,
1879 body_fmt, qmark, body, FALSE, TRUE,
1880 _("The body of the \"Forward\" template has an error at line %d."));
1881 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1882 quote_fmt_reset_vartable();
1883 compose_attach_parts(compose, msginfo);
1885 procmsg_msginfo_free(&full_msginfo);
1888 SIGNAL_BLOCK(textbuf);
1890 if (account->auto_sig)
1891 compose_insert_sig(compose, FALSE);
1893 compose_wrap_all(compose);
1896 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1897 gtkaspell_highlight_all(compose->gtkaspell);
1898 gtkaspell_unblock_check(compose->gtkaspell);
1900 SIGNAL_UNBLOCK(textbuf);
1902 cursor_pos = quote_fmt_get_cursor_pos();
1903 if (cursor_pos == -1)
1904 gtk_widget_grab_focus(compose->header_last->entry);
1906 gtk_widget_grab_focus(compose->text);
1908 if (!no_extedit && prefs_common.auto_exteditor)
1909 compose_exec_ext_editor(compose);
1912 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1913 gchar *folderidentifier;
1915 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1916 folderidentifier = folder_item_get_identifier(msginfo->folder);
1917 compose_set_save_to(compose, folderidentifier);
1918 g_free(folderidentifier);
1921 undo_unblock(compose->undostruct);
1923 compose->modified = FALSE;
1924 compose_set_title(compose);
1926 compose->updating = FALSE;
1927 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1928 SCROLL_TO_CURSOR(compose);
1930 if (compose->deferred_destroy) {
1931 compose_destroy(compose);
1935 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1940 #undef INSERT_FW_HEADER
1942 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1945 GtkTextView *textview;
1946 GtkTextBuffer *textbuf;
1950 gboolean single_mail = TRUE;
1952 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1954 if (g_slist_length(msginfo_list) > 1)
1955 single_mail = FALSE;
1957 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1958 if (((MsgInfo *)msginfo->data)->folder == NULL)
1961 /* guess account from first selected message */
1963 !(account = compose_guess_forward_account_from_msginfo
1964 (msginfo_list->data)))
1965 account = cur_account;
1967 cm_return_val_if_fail(account != NULL, NULL);
1969 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1970 if (msginfo->data) {
1971 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1972 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1976 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1977 g_warning("no msginfo_list");
1981 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1983 compose->updating = TRUE;
1985 /* override from name according to folder properties */
1986 if (msginfo_list->data) {
1987 MsgInfo *msginfo = msginfo_list->data;
1989 if (msginfo->folder && msginfo->folder->prefs &&
1990 msginfo->folder->prefs->forward_with_format &&
1991 msginfo->folder->prefs->forward_override_from_format &&
1992 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1997 /* decode \-escape sequences in the internal representation of the quote format */
1998 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1999 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
2002 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2003 compose->gtkaspell);
2005 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2007 quote_fmt_scan_string(tmp);
2010 buf = quote_fmt_get_buffer();
2012 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2014 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
2015 quote_fmt_reset_vartable();
2021 textview = GTK_TEXT_VIEW(compose->text);
2022 textbuf = gtk_text_view_get_buffer(textview);
2023 compose_create_tags(textview, compose);
2025 undo_block(compose->undostruct);
2026 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
2027 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
2029 if (!is_file_exist(msgfile))
2030 g_warning("%s: file does not exist", msgfile);
2032 compose_attach_append(compose, msgfile, msgfile,
2033 "message/rfc822", NULL);
2038 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2039 if (info->subject && *info->subject) {
2040 gchar *buf, *buf2, *p;
2042 buf = p = g_strdup(info->subject);
2043 p += subject_get_prefix_length(p);
2044 memmove(buf, p, strlen(p) + 1);
2046 buf2 = g_strdup_printf("Fw: %s", buf);
2047 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2053 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2054 _("Fw: multiple emails"));
2057 SIGNAL_BLOCK(textbuf);
2059 if (account->auto_sig)
2060 compose_insert_sig(compose, FALSE);
2062 compose_wrap_all(compose);
2064 SIGNAL_UNBLOCK(textbuf);
2066 gtk_text_buffer_get_start_iter(textbuf, &iter);
2067 gtk_text_buffer_place_cursor(textbuf, &iter);
2069 if (prefs_common.auto_exteditor)
2070 compose_exec_ext_editor(compose);
2072 gtk_widget_grab_focus(compose->header_last->entry);
2073 undo_unblock(compose->undostruct);
2074 compose->modified = FALSE;
2075 compose_set_title(compose);
2077 compose->updating = FALSE;
2078 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2079 SCROLL_TO_CURSOR(compose);
2081 if (compose->deferred_destroy) {
2082 compose_destroy(compose);
2086 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2091 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2093 GtkTextIter start = *iter;
2094 GtkTextIter end_iter;
2095 int start_pos = gtk_text_iter_get_offset(&start);
2097 if (!compose->account->sig_sep)
2100 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2101 start_pos+strlen(compose->account->sig_sep));
2103 /* check sig separator */
2104 str = gtk_text_iter_get_text(&start, &end_iter);
2105 if (!strcmp(str, compose->account->sig_sep)) {
2107 /* check end of line (\n) */
2108 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2109 start_pos+strlen(compose->account->sig_sep));
2110 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2111 start_pos+strlen(compose->account->sig_sep)+1);
2112 tmp = gtk_text_iter_get_text(&start, &end_iter);
2113 if (!strcmp(tmp,"\n")) {
2125 static gboolean compose_update_folder_hook(gpointer source, gpointer data)
2127 FolderUpdateData *hookdata = (FolderUpdateData *)source;
2128 Compose *compose = (Compose *)data;
2129 FolderItem *old_item = NULL;
2130 FolderItem *new_item = NULL;
2131 gchar *old_id, *new_id;
2133 if (!(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
2134 && !(hookdata->update_flags & FOLDER_MOVE_FOLDERITEM))
2137 old_item = hookdata->item;
2138 new_item = hookdata->item2;
2140 old_id = folder_item_get_identifier(old_item);
2141 new_id = new_item ? folder_item_get_identifier(new_item) : g_strdup("NULL");
2143 if (compose->targetinfo && compose->targetinfo->folder == old_item) {
2144 debug_print("updating targetinfo folder: %s -> %s\n", old_id, new_id);
2145 compose->targetinfo->folder = new_item;
2148 if (compose->replyinfo && compose->replyinfo->folder == old_item) {
2149 debug_print("updating replyinfo folder: %s -> %s\n", old_id, new_id);
2150 compose->replyinfo->folder = new_item;
2153 if (compose->fwdinfo && compose->fwdinfo->folder == old_item) {
2154 debug_print("updating fwdinfo folder: %s -> %s\n", old_id, new_id);
2155 compose->fwdinfo->folder = new_item;
2163 static void compose_colorize_signature(Compose *compose)
2165 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2167 GtkTextIter end_iter;
2168 gtk_text_buffer_get_start_iter(buffer, &iter);
2169 while (gtk_text_iter_forward_line(&iter))
2170 if (compose_is_sig_separator(compose, buffer, &iter)) {
2171 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2172 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2176 #define BLOCK_WRAP() { \
2177 prev_autowrap = compose->autowrap; \
2178 buffer = gtk_text_view_get_buffer( \
2179 GTK_TEXT_VIEW(compose->text)); \
2180 compose->autowrap = FALSE; \
2182 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2183 G_CALLBACK(compose_changed_cb), \
2185 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2186 G_CALLBACK(text_inserted), \
2189 #define UNBLOCK_WRAP() { \
2190 compose->autowrap = prev_autowrap; \
2191 if (compose->autowrap) { \
2192 gint old = compose->draft_timeout_tag; \
2193 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; \
2194 compose_wrap_all(compose); \
2195 compose->draft_timeout_tag = old; \
2198 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2199 G_CALLBACK(compose_changed_cb), \
2201 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2202 G_CALLBACK(text_inserted), \
2206 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2208 Compose *compose = NULL;
2209 PrefsAccount *account = NULL;
2210 GtkTextView *textview;
2211 GtkTextBuffer *textbuf;
2215 gchar buf[BUFFSIZE];
2216 gboolean use_signing = FALSE;
2217 gboolean use_encryption = FALSE;
2218 gchar *privacy_system = NULL;
2219 int priority = PRIORITY_NORMAL;
2220 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2221 gboolean autowrap = prefs_common.autowrap;
2222 gboolean autoindent = prefs_common.auto_indent;
2223 HeaderEntry *manual_headers = NULL;
2225 cm_return_val_if_fail(msginfo != NULL, NULL);
2226 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2228 if (compose_put_existing_to_front(msginfo)) {
2232 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2233 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2234 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2235 gchar queueheader_buf[BUFFSIZE];
2238 /* Select Account from queue headers */
2239 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2240 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2241 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2242 account = account_find_from_id(id);
2244 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2245 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2246 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2247 account = account_find_from_id(id);
2249 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2250 sizeof(queueheader_buf), "NAID:")) {
2251 id = atoi(&queueheader_buf[strlen("NAID:")]);
2252 account = account_find_from_id(id);
2254 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2255 sizeof(queueheader_buf), "MAID:")) {
2256 id = atoi(&queueheader_buf[strlen("MAID:")]);
2257 account = account_find_from_id(id);
2259 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2260 sizeof(queueheader_buf), "S:")) {
2261 account = account_find_from_address(queueheader_buf, FALSE);
2263 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2264 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2265 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2266 use_signing = param;
2269 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2270 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2271 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2272 use_signing = param;
2275 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2276 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2277 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2278 use_encryption = param;
2280 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2281 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2282 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2283 use_encryption = param;
2285 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2286 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2287 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2290 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2291 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2292 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2295 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2296 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2297 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2299 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2300 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2301 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2303 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2304 sizeof(queueheader_buf), "X-Priority: ")) {
2305 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2308 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2309 sizeof(queueheader_buf), "RMID:")) {
2310 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2311 if (tokens[0] && tokens[1] && tokens[2]) {
2312 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2313 if (orig_item != NULL) {
2314 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2319 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2320 sizeof(queueheader_buf), "FMID:")) {
2321 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2322 if (tokens[0] && tokens[1] && tokens[2]) {
2323 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2324 if (orig_item != NULL) {
2325 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2330 /* Get manual headers */
2331 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2332 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2333 if (*listmh != '\0') {
2334 debug_print("Got manual headers: %s\n", listmh);
2335 manual_headers = procheader_entries_from_str(listmh);
2340 account = msginfo->folder->folder->account;
2343 if (!account && prefs_common.reedit_account_autosel) {
2344 gchar from[BUFFSIZE];
2345 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2346 extract_address(from);
2347 account = account_find_from_address(from, FALSE);
2351 account = cur_account;
2353 cm_return_val_if_fail(account != NULL, NULL);
2355 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2357 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2358 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2359 compose->autowrap = autowrap;
2360 compose->replyinfo = replyinfo;
2361 compose->fwdinfo = fwdinfo;
2363 compose->updating = TRUE;
2364 compose->priority = priority;
2366 if (privacy_system != NULL) {
2367 compose->privacy_system = privacy_system;
2368 compose_use_signing(compose, use_signing);
2369 compose_use_encryption(compose, use_encryption);
2370 compose_update_privacy_system_menu_item(compose, FALSE);
2372 activate_privacy_system(compose, account, FALSE);
2375 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2377 compose_extract_original_charset(compose);
2379 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2380 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2381 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2382 gchar queueheader_buf[BUFFSIZE];
2384 /* Set message save folder */
2385 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2386 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2387 compose_set_save_to(compose, &queueheader_buf[4]);
2389 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2390 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2392 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2397 if (compose_parse_header(compose, msginfo) < 0) {
2398 compose->updating = FALSE;
2399 compose_destroy(compose);
2402 compose_reedit_set_entry(compose, msginfo);
2404 textview = GTK_TEXT_VIEW(compose->text);
2405 textbuf = gtk_text_view_get_buffer(textview);
2406 compose_create_tags(textview, compose);
2408 mark = gtk_text_buffer_get_insert(textbuf);
2409 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2411 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2412 G_CALLBACK(compose_changed_cb),
2415 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2416 fp = procmime_get_first_encrypted_text_content(msginfo);
2418 compose_force_encryption(compose, account, TRUE, NULL);
2421 fp = procmime_get_first_text_content(msginfo);
2424 g_warning("Can't get text part");
2428 gboolean prev_autowrap;
2429 GtkTextBuffer *buffer;
2431 while (fgets(buf, sizeof(buf), fp) != NULL) {
2433 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2439 compose_attach_parts(compose, msginfo);
2441 compose_colorize_signature(compose);
2443 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2444 G_CALLBACK(compose_changed_cb),
2447 if (manual_headers != NULL) {
2448 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2449 procheader_entries_free(manual_headers);
2450 compose->updating = FALSE;
2451 compose_destroy(compose);
2454 procheader_entries_free(manual_headers);
2457 gtk_widget_grab_focus(compose->text);
2459 if (prefs_common.auto_exteditor) {
2460 compose_exec_ext_editor(compose);
2462 compose->modified = FALSE;
2463 compose_set_title(compose);
2465 compose->updating = FALSE;
2466 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2467 SCROLL_TO_CURSOR(compose);
2469 if (compose->deferred_destroy) {
2470 compose_destroy(compose);
2474 compose->sig_str = account_get_signature_str(compose->account);
2476 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2481 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2488 cm_return_val_if_fail(msginfo != NULL, NULL);
2491 account = account_get_reply_account(msginfo,
2492 prefs_common.reply_account_autosel);
2493 cm_return_val_if_fail(account != NULL, NULL);
2495 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2497 compose->updating = TRUE;
2499 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2500 compose->replyinfo = NULL;
2501 compose->fwdinfo = NULL;
2503 compose_show_first_last_header(compose, TRUE);
2505 gtk_widget_grab_focus(compose->header_last->entry);
2507 filename = procmsg_get_message_file(msginfo);
2509 if (filename == NULL) {
2510 compose->updating = FALSE;
2511 compose_destroy(compose);
2516 compose->redirect_filename = filename;
2518 /* Set save folder */
2519 item = msginfo->folder;
2520 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2521 gchar *folderidentifier;
2523 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2524 folderidentifier = folder_item_get_identifier(item);
2525 compose_set_save_to(compose, folderidentifier);
2526 g_free(folderidentifier);
2529 compose_attach_parts(compose, msginfo);
2531 if (msginfo->subject)
2532 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2534 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2536 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2537 _("The body of the \"Redirect\" template has an error at line %d."));
2538 quote_fmt_reset_vartable();
2539 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2541 compose_colorize_signature(compose);
2544 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2545 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2546 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2548 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2549 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2550 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2551 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2552 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", FALSE);
2553 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2554 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2555 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2556 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2558 if (compose->toolbar->draft_btn)
2559 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2560 if (compose->toolbar->insert_btn)
2561 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2562 if (compose->toolbar->attach_btn)
2563 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2564 if (compose->toolbar->sig_btn)
2565 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2566 if (compose->toolbar->exteditor_btn)
2567 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2568 if (compose->toolbar->linewrap_current_btn)
2569 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2570 if (compose->toolbar->linewrap_all_btn)
2571 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2573 compose->modified = FALSE;
2574 compose_set_title(compose);
2575 compose->updating = FALSE;
2576 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2577 SCROLL_TO_CURSOR(compose);
2579 if (compose->deferred_destroy) {
2580 compose_destroy(compose);
2584 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2589 const GList *compose_get_compose_list(void)
2591 return compose_list;
2594 void compose_entry_append(Compose *compose, const gchar *address,
2595 ComposeEntryType type, ComposePrefType pref_type)
2597 const gchar *header;
2599 gboolean in_quote = FALSE;
2600 if (!address || *address == '\0') return;
2607 header = N_("Bcc:");
2609 case COMPOSE_REPLYTO:
2610 header = N_("Reply-To:");
2612 case COMPOSE_NEWSGROUPS:
2613 header = N_("Newsgroups:");
2615 case COMPOSE_FOLLOWUPTO:
2616 header = N_( "Followup-To:");
2618 case COMPOSE_INREPLYTO:
2619 header = N_( "In-Reply-To:");
2626 header = prefs_common_translated_header_name(header);
2628 cur = begin = (gchar *)address;
2630 /* we separate the line by commas, but not if we're inside a quoted
2632 while (*cur != '\0') {
2634 in_quote = !in_quote;
2635 if (*cur == ',' && !in_quote) {
2636 gchar *tmp = g_strdup(begin);
2638 tmp[cur-begin]='\0';
2641 while (*tmp == ' ' || *tmp == '\t')
2643 compose_add_header_entry(compose, header, tmp, pref_type);
2650 gchar *tmp = g_strdup(begin);
2652 tmp[cur-begin]='\0';
2653 while (*tmp == ' ' || *tmp == '\t')
2655 compose_add_header_entry(compose, header, tmp, pref_type);
2660 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2662 #if !GTK_CHECK_VERSION(3, 0, 0)
2663 static GdkColor yellow;
2664 static GdkColor black;
2665 static gboolean yellow_initialised = FALSE;
2667 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2668 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2673 #if !GTK_CHECK_VERSION(3, 0, 0)
2674 if (!yellow_initialised) {
2675 gdk_color_parse("#f5f6be", &yellow);
2676 gdk_color_parse("#000000", &black);
2677 yellow_initialised = gdk_colormap_alloc_color(
2678 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2679 yellow_initialised &= gdk_colormap_alloc_color(
2680 gdk_colormap_get_system(), &black, FALSE, TRUE);
2684 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2685 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2686 if (gtk_entry_get_text(entry) &&
2687 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2688 #if !GTK_CHECK_VERSION(3, 0, 0)
2689 if (yellow_initialised) {
2691 gtk_widget_modify_base(
2692 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2693 GTK_STATE_NORMAL, &yellow);
2694 gtk_widget_modify_text(
2695 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2696 GTK_STATE_NORMAL, &black);
2697 #if !GTK_CHECK_VERSION(3, 0, 0)
2704 void compose_toolbar_cb(gint action, gpointer data)
2706 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2707 Compose *compose = (Compose*)toolbar_item->parent;
2709 cm_return_if_fail(compose != NULL);
2713 compose_send_cb(NULL, compose);
2716 compose_send_later_cb(NULL, compose);
2719 compose_draft(compose, COMPOSE_QUIT_EDITING);
2722 compose_insert_file_cb(NULL, compose);
2725 compose_attach_cb(NULL, compose);
2728 compose_insert_sig(compose, FALSE);
2731 compose_insert_sig(compose, TRUE);
2734 compose_ext_editor_cb(NULL, compose);
2736 case A_LINEWRAP_CURRENT:
2737 compose_beautify_paragraph(compose, NULL, TRUE);
2739 case A_LINEWRAP_ALL:
2740 compose_wrap_all_full(compose, TRUE);
2743 compose_address_cb(NULL, compose);
2746 case A_CHECK_SPELLING:
2747 compose_check_all(NULL, compose);
2755 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2760 gchar *subject = NULL;
2764 gchar **attach = NULL;
2765 gchar *inreplyto = NULL;
2766 MailField mfield = NO_FIELD_PRESENT;
2768 /* get mailto parts but skip from */
2769 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2772 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2773 mfield = TO_FIELD_PRESENT;
2776 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2778 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2780 if (!g_utf8_validate (subject, -1, NULL)) {
2781 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2782 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2785 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2787 mfield = SUBJECT_FIELD_PRESENT;
2790 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2791 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2794 gboolean prev_autowrap = compose->autowrap;
2796 compose->autowrap = FALSE;
2798 mark = gtk_text_buffer_get_insert(buffer);
2799 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2801 if (!g_utf8_validate (body, -1, NULL)) {
2802 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2803 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2806 gtk_text_buffer_insert(buffer, &iter, body, -1);
2808 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2810 compose->autowrap = prev_autowrap;
2811 if (compose->autowrap)
2812 compose_wrap_all(compose);
2813 mfield = BODY_FIELD_PRESENT;
2817 gint i = 0, att = 0;
2818 gchar *warn_files = NULL;
2819 while (attach[i] != NULL) {
2820 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2821 if (utf8_filename) {
2822 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2823 gchar *tmp = g_strdup_printf("%s%s\n",
2824 warn_files?warn_files:"",
2830 g_free(utf8_filename);
2832 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2837 alertpanel_notice(ngettext(
2838 "The following file has been attached: \n%s",
2839 "The following files have been attached: \n%s", att), warn_files);
2844 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2857 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2859 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2860 {"Cc:", NULL, TRUE},
2861 {"References:", NULL, FALSE},
2862 {"Bcc:", NULL, TRUE},
2863 {"Newsgroups:", NULL, TRUE},
2864 {"Followup-To:", NULL, TRUE},
2865 {"List-Post:", NULL, FALSE},
2866 {"X-Priority:", NULL, FALSE},
2867 {NULL, NULL, FALSE}};
2883 cm_return_val_if_fail(msginfo != NULL, -1);
2885 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2886 procheader_get_header_fields(fp, hentry);
2889 if (hentry[H_REPLY_TO].body != NULL) {
2890 if (hentry[H_REPLY_TO].body[0] != '\0') {
2892 conv_unmime_header(hentry[H_REPLY_TO].body,
2895 g_free(hentry[H_REPLY_TO].body);
2896 hentry[H_REPLY_TO].body = NULL;
2898 if (hentry[H_CC].body != NULL) {
2899 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2900 g_free(hentry[H_CC].body);
2901 hentry[H_CC].body = NULL;
2903 if (hentry[H_REFERENCES].body != NULL) {
2904 if (compose->mode == COMPOSE_REEDIT)
2905 compose->references = hentry[H_REFERENCES].body;
2907 compose->references = compose_parse_references
2908 (hentry[H_REFERENCES].body, msginfo->msgid);
2909 g_free(hentry[H_REFERENCES].body);
2911 hentry[H_REFERENCES].body = NULL;
2913 if (hentry[H_BCC].body != NULL) {
2914 if (compose->mode == COMPOSE_REEDIT)
2916 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2917 g_free(hentry[H_BCC].body);
2918 hentry[H_BCC].body = NULL;
2920 if (hentry[H_NEWSGROUPS].body != NULL) {
2921 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2922 hentry[H_NEWSGROUPS].body = NULL;
2924 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2925 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2926 compose->followup_to =
2927 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2930 g_free(hentry[H_FOLLOWUP_TO].body);
2931 hentry[H_FOLLOWUP_TO].body = NULL;
2933 if (hentry[H_LIST_POST].body != NULL) {
2934 gchar *to = NULL, *start = NULL;
2936 extract_address(hentry[H_LIST_POST].body);
2937 if (hentry[H_LIST_POST].body[0] != '\0') {
2938 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2940 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2941 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2944 g_free(compose->ml_post);
2945 compose->ml_post = to;
2948 g_free(hentry[H_LIST_POST].body);
2949 hentry[H_LIST_POST].body = NULL;
2952 /* CLAWS - X-Priority */
2953 if (compose->mode == COMPOSE_REEDIT)
2954 if (hentry[H_X_PRIORITY].body != NULL) {
2957 priority = atoi(hentry[H_X_PRIORITY].body);
2958 g_free(hentry[H_X_PRIORITY].body);
2960 hentry[H_X_PRIORITY].body = NULL;
2962 if (priority < PRIORITY_HIGHEST ||
2963 priority > PRIORITY_LOWEST)
2964 priority = PRIORITY_NORMAL;
2966 compose->priority = priority;
2969 if (compose->mode == COMPOSE_REEDIT) {
2970 if (msginfo->inreplyto && *msginfo->inreplyto)
2971 compose->inreplyto = g_strdup(msginfo->inreplyto);
2973 if (msginfo->msgid && *msginfo->msgid)
2974 compose->msgid = g_strdup(msginfo->msgid);
2976 if (msginfo->msgid && *msginfo->msgid)
2977 compose->inreplyto = g_strdup(msginfo->msgid);
2979 if (!compose->references) {
2980 if (msginfo->msgid && *msginfo->msgid) {
2981 if (msginfo->inreplyto && *msginfo->inreplyto)
2982 compose->references =
2983 g_strdup_printf("<%s>\n\t<%s>",
2987 compose->references =
2988 g_strconcat("<", msginfo->msgid, ">",
2990 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2991 compose->references =
2992 g_strconcat("<", msginfo->inreplyto, ">",
3001 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
3006 cm_return_val_if_fail(msginfo != NULL, -1);
3008 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
3009 procheader_get_header_fields(fp, entries);
3013 while (he != NULL && he->name != NULL) {
3015 GtkListStore *model = NULL;
3017 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
3018 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
3019 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
3020 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
3021 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
3028 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
3030 GSList *ref_id_list, *cur;
3034 ref_id_list = references_list_append(NULL, ref);
3035 if (!ref_id_list) return NULL;
3036 if (msgid && *msgid)
3037 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
3042 for (cur = ref_id_list; cur != NULL; cur = cur->next)
3043 /* "<" + Message-ID + ">" + CR+LF+TAB */
3044 len += strlen((gchar *)cur->data) + 5;
3046 if (len > MAX_REFERENCES_LEN) {
3047 /* remove second message-ID */
3048 if (ref_id_list && ref_id_list->next &&
3049 ref_id_list->next->next) {
3050 g_free(ref_id_list->next->data);
3051 ref_id_list = g_slist_remove
3052 (ref_id_list, ref_id_list->next->data);
3054 slist_free_strings_full(ref_id_list);
3061 new_ref = g_string_new("");
3062 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3063 if (new_ref->len > 0)
3064 g_string_append(new_ref, "\n\t");
3065 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3068 slist_free_strings_full(ref_id_list);
3070 new_ref_str = new_ref->str;
3071 g_string_free(new_ref, FALSE);
3076 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3077 const gchar *fmt, const gchar *qmark,
3078 const gchar *body, gboolean rewrap,
3079 gboolean need_unescape,
3080 const gchar *err_msg)
3082 MsgInfo* dummyinfo = NULL;
3083 gchar *quote_str = NULL;
3085 gboolean prev_autowrap;
3086 const gchar *trimmed_body = body;
3087 gint cursor_pos = -1;
3088 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3089 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3094 SIGNAL_BLOCK(buffer);
3097 dummyinfo = compose_msginfo_new_from_compose(compose);
3098 msginfo = dummyinfo;
3101 if (qmark != NULL) {
3103 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3104 compose->gtkaspell);
3106 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3108 quote_fmt_scan_string(qmark);
3111 buf = quote_fmt_get_buffer();
3113 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3115 Xstrdup_a(quote_str, buf, goto error)
3118 if (fmt && *fmt != '\0') {
3121 while (*trimmed_body == '\n')
3125 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3126 compose->gtkaspell);
3128 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3130 if (need_unescape) {
3133 /* decode \-escape sequences in the internal representation of the quote format */
3134 tmp = g_malloc(strlen(fmt)+1);
3135 pref_get_unescaped_pref(tmp, fmt);
3136 quote_fmt_scan_string(tmp);
3140 quote_fmt_scan_string(fmt);
3144 buf = quote_fmt_get_buffer();
3146 gint line = quote_fmt_get_line();
3147 alertpanel_error(err_msg, line);
3153 prev_autowrap = compose->autowrap;
3154 compose->autowrap = FALSE;
3156 mark = gtk_text_buffer_get_insert(buffer);
3157 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3158 if (g_utf8_validate(buf, -1, NULL)) {
3159 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3161 gchar *tmpout = NULL;
3162 tmpout = conv_codeset_strdup
3163 (buf, conv_get_locale_charset_str_no_utf8(),
3165 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3167 tmpout = g_malloc(strlen(buf)*2+1);
3168 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3170 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3174 cursor_pos = quote_fmt_get_cursor_pos();
3175 if (cursor_pos == -1)
3176 cursor_pos = gtk_text_iter_get_offset(&iter);
3177 compose->set_cursor_pos = cursor_pos;
3179 gtk_text_buffer_get_start_iter(buffer, &iter);
3180 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3181 gtk_text_buffer_place_cursor(buffer, &iter);
3183 compose->autowrap = prev_autowrap;
3184 if (compose->autowrap && rewrap)
3185 compose_wrap_all(compose);
3192 SIGNAL_UNBLOCK(buffer);
3194 procmsg_msginfo_free( &dummyinfo );
3199 /* if ml_post is of type addr@host and from is of type
3200 * addr-anything@host, return TRUE
3202 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3204 gchar *left_ml = NULL;
3205 gchar *right_ml = NULL;
3206 gchar *left_from = NULL;
3207 gchar *right_from = NULL;
3208 gboolean result = FALSE;
3210 if (!ml_post || !from)
3213 left_ml = g_strdup(ml_post);
3214 if (strstr(left_ml, "@")) {
3215 right_ml = strstr(left_ml, "@")+1;
3216 *(strstr(left_ml, "@")) = '\0';
3219 left_from = g_strdup(from);
3220 if (strstr(left_from, "@")) {
3221 right_from = strstr(left_from, "@")+1;
3222 *(strstr(left_from, "@")) = '\0';
3225 if (right_ml && right_from
3226 && !strncmp(left_from, left_ml, strlen(left_ml))
3227 && !strcmp(right_from, right_ml)) {
3236 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3237 gboolean respect_default_to)
3241 if (!folder || !folder->prefs)
3244 if (respect_default_to && folder->prefs->enable_default_to) {
3245 compose_entry_append(compose, folder->prefs->default_to,
3246 COMPOSE_TO, PREF_FOLDER);
3247 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3249 if (folder->prefs->enable_default_cc)
3250 compose_entry_append(compose, folder->prefs->default_cc,
3251 COMPOSE_CC, PREF_FOLDER);
3252 if (folder->prefs->enable_default_bcc)
3253 compose_entry_append(compose, folder->prefs->default_bcc,
3254 COMPOSE_BCC, PREF_FOLDER);
3255 if (folder->prefs->enable_default_replyto)
3256 compose_entry_append(compose, folder->prefs->default_replyto,
3257 COMPOSE_REPLYTO, PREF_FOLDER);
3260 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3265 if (!compose || !msginfo)
3268 if (msginfo->subject && *msginfo->subject) {
3269 buf = p = g_strdup(msginfo->subject);
3270 p += subject_get_prefix_length(p);
3271 memmove(buf, p, strlen(p) + 1);
3273 buf2 = g_strdup_printf("Re: %s", buf);
3274 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3279 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3282 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3283 gboolean to_all, gboolean to_ml,
3285 gboolean followup_and_reply_to)
3287 GSList *cc_list = NULL;
3290 gchar *replyto = NULL;
3291 gchar *ac_email = NULL;
3293 gboolean reply_to_ml = FALSE;
3294 gboolean default_reply_to = FALSE;
3296 cm_return_if_fail(compose->account != NULL);
3297 cm_return_if_fail(msginfo != NULL);
3299 reply_to_ml = to_ml && compose->ml_post;
3301 default_reply_to = msginfo->folder &&
3302 msginfo->folder->prefs->enable_default_reply_to;
3304 if (compose->account->protocol != A_NNTP) {
3305 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3307 if (reply_to_ml && !default_reply_to) {
3309 gboolean is_subscr = is_subscription(compose->ml_post,
3312 /* normal answer to ml post with a reply-to */
3313 compose_entry_append(compose,
3315 COMPOSE_TO, PREF_ML);
3316 if (compose->replyto)
3317 compose_entry_append(compose,
3319 COMPOSE_CC, PREF_ML);
3321 /* answer to subscription confirmation */
3322 if (compose->replyto)
3323 compose_entry_append(compose,
3325 COMPOSE_TO, PREF_ML);
3326 else if (msginfo->from)
3327 compose_entry_append(compose,
3329 COMPOSE_TO, PREF_ML);
3332 else if (!(to_all || to_sender) && default_reply_to) {
3333 compose_entry_append(compose,
3334 msginfo->folder->prefs->default_reply_to,
3335 COMPOSE_TO, PREF_FOLDER);
3336 compose_entry_mark_default_to(compose,
3337 msginfo->folder->prefs->default_reply_to);
3343 compose_entry_append(compose, msginfo->from,
3344 COMPOSE_TO, PREF_NONE);
3346 Xstrdup_a(tmp1, msginfo->from, return);
3347 extract_address(tmp1);
3348 compose_entry_append(compose,
3349 (!account_find_from_address(tmp1, FALSE))
3352 COMPOSE_TO, PREF_NONE);
3353 if (compose->replyto)
3354 compose_entry_append(compose,
3356 COMPOSE_CC, PREF_NONE);
3358 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3359 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3360 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3361 if (compose->replyto) {
3362 compose_entry_append(compose,
3364 COMPOSE_TO, PREF_NONE);
3366 compose_entry_append(compose,
3367 msginfo->from ? msginfo->from : "",
3368 COMPOSE_TO, PREF_NONE);
3371 /* replying to own mail, use original recp */
3372 compose_entry_append(compose,
3373 msginfo->to ? msginfo->to : "",
3374 COMPOSE_TO, PREF_NONE);
3375 compose_entry_append(compose,
3376 msginfo->cc ? msginfo->cc : "",
3377 COMPOSE_CC, PREF_NONE);
3382 if (to_sender || (compose->followup_to &&
3383 !strncmp(compose->followup_to, "poster", 6)))
3384 compose_entry_append
3386 (compose->replyto ? compose->replyto :
3387 msginfo->from ? msginfo->from : ""),
3388 COMPOSE_TO, PREF_NONE);
3390 else if (followup_and_reply_to || to_all) {
3391 compose_entry_append
3393 (compose->replyto ? compose->replyto :
3394 msginfo->from ? msginfo->from : ""),
3395 COMPOSE_TO, PREF_NONE);
3397 compose_entry_append
3399 compose->followup_to ? compose->followup_to :
3400 compose->newsgroups ? compose->newsgroups : "",
3401 COMPOSE_NEWSGROUPS, PREF_NONE);
3403 compose_entry_append
3405 msginfo->cc ? msginfo->cc : "",
3406 COMPOSE_CC, PREF_NONE);
3409 compose_entry_append
3411 compose->followup_to ? compose->followup_to :
3412 compose->newsgroups ? compose->newsgroups : "",
3413 COMPOSE_NEWSGROUPS, PREF_NONE);
3415 compose_reply_set_subject(compose, msginfo);
3417 if (to_ml && compose->ml_post) return;
3418 if (!to_all || compose->account->protocol == A_NNTP) return;
3420 if (compose->replyto) {
3421 Xstrdup_a(replyto, compose->replyto, return);
3422 extract_address(replyto);
3424 if (msginfo->from) {
3425 Xstrdup_a(from, msginfo->from, return);
3426 extract_address(from);
3429 if (replyto && from)
3430 cc_list = address_list_append_with_comments(cc_list, from);
3431 if (to_all && msginfo->folder &&
3432 msginfo->folder->prefs->enable_default_reply_to)
3433 cc_list = address_list_append_with_comments(cc_list,
3434 msginfo->folder->prefs->default_reply_to);
3435 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3436 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3438 ac_email = g_utf8_strdown(compose->account->address, -1);
3441 for (cur = cc_list; cur != NULL; cur = cur->next) {
3442 gchar *addr = g_utf8_strdown(cur->data, -1);
3443 extract_address(addr);
3445 if (strcmp(ac_email, addr))
3446 compose_entry_append(compose, (gchar *)cur->data,
3447 COMPOSE_CC, PREF_NONE);
3449 debug_print("Cc address same as compose account's, ignoring\n");
3454 slist_free_strings_full(cc_list);
3460 #define SET_ENTRY(entry, str) \
3463 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3466 #define SET_ADDRESS(type, str) \
3469 compose_entry_append(compose, str, type, PREF_NONE); \
3472 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3474 cm_return_if_fail(msginfo != NULL);
3476 SET_ENTRY(subject_entry, msginfo->subject);
3477 SET_ENTRY(from_name, msginfo->from);
3478 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3479 SET_ADDRESS(COMPOSE_CC, compose->cc);
3480 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3481 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3482 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3483 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3485 compose_update_priority_menu_item(compose);
3486 compose_update_privacy_system_menu_item(compose, FALSE);
3487 compose_show_first_last_header(compose, TRUE);
3493 static void compose_insert_sig(Compose *compose, gboolean replace)
3495 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3496 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3498 GtkTextIter iter, iter_end;
3499 gint cur_pos, ins_pos;
3500 gboolean prev_autowrap;
3501 gboolean found = FALSE;
3502 gboolean exists = FALSE;
3504 cm_return_if_fail(compose->account != NULL);
3508 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3509 G_CALLBACK(compose_changed_cb),
3512 mark = gtk_text_buffer_get_insert(buffer);
3513 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3514 cur_pos = gtk_text_iter_get_offset (&iter);
3517 gtk_text_buffer_get_end_iter(buffer, &iter);
3519 exists = (compose->sig_str != NULL);
3522 GtkTextIter first_iter, start_iter, end_iter;
3524 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3526 if (!exists || compose->sig_str[0] == '\0')
3529 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3530 compose->signature_tag);
3533 /* include previous \n\n */
3534 gtk_text_iter_backward_chars(&first_iter, 1);
3535 start_iter = first_iter;
3536 end_iter = first_iter;
3538 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3539 compose->signature_tag);
3540 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3541 compose->signature_tag);
3543 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3549 g_free(compose->sig_str);
3550 compose->sig_str = account_get_signature_str(compose->account);
3552 cur_pos = gtk_text_iter_get_offset(&iter);
3554 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3555 g_free(compose->sig_str);
3556 compose->sig_str = NULL;
3558 if (compose->sig_inserted == FALSE)
3559 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3560 compose->sig_inserted = TRUE;
3562 cur_pos = gtk_text_iter_get_offset(&iter);
3563 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3565 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3566 gtk_text_iter_forward_chars(&iter, 1);
3567 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3568 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3570 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3571 cur_pos = gtk_text_buffer_get_char_count (buffer);
3574 /* put the cursor where it should be
3575 * either where the quote_fmt says, either where it was */
3576 if (compose->set_cursor_pos < 0)
3577 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3579 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3580 compose->set_cursor_pos);
3582 compose->set_cursor_pos = -1;
3583 gtk_text_buffer_place_cursor(buffer, &iter);
3584 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3585 G_CALLBACK(compose_changed_cb),
3591 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3594 GtkTextBuffer *buffer;
3597 const gchar *cur_encoding;
3598 gchar buf[BUFFSIZE];
3601 gboolean prev_autowrap;
3604 GString *file_contents = NULL;
3605 ComposeInsertResult result = COMPOSE_INSERT_SUCCESS;
3607 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3609 /* get the size of the file we are about to insert */
3610 ret = g_stat(file, &file_stat);
3612 gchar *shortfile = g_path_get_basename(file);
3613 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3615 return COMPOSE_INSERT_NO_FILE;
3616 } else if (prefs_common.warn_large_insert == TRUE) {
3618 /* ask user for confirmation if the file is large */
3619 if (prefs_common.warn_large_insert_size < 0 ||
3620 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3624 msg = g_strdup_printf(_("You are about to insert a file of %s "
3625 "in the message body. Are you sure you want to do that?"),
3626 to_human_readable(file_stat.st_size));
3627 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3628 g_strconcat("+", _("_Insert"), NULL), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3631 /* do we ask for confirmation next time? */
3632 if (aval & G_ALERTDISABLE) {
3633 /* no confirmation next time, disable feature in preferences */
3634 aval &= ~G_ALERTDISABLE;
3635 prefs_common.warn_large_insert = FALSE;
3638 /* abort file insertion if user canceled action */
3639 if (aval != G_ALERTALTERNATE) {
3640 return COMPOSE_INSERT_NO_FILE;
3646 if ((fp = g_fopen(file, "rb")) == NULL) {
3647 FILE_OP_ERROR(file, "fopen");
3648 return COMPOSE_INSERT_READ_ERROR;
3651 prev_autowrap = compose->autowrap;
3652 compose->autowrap = FALSE;
3654 text = GTK_TEXT_VIEW(compose->text);
3655 buffer = gtk_text_view_get_buffer(text);
3656 mark = gtk_text_buffer_get_insert(buffer);
3657 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3659 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3660 G_CALLBACK(text_inserted),
3663 cur_encoding = conv_get_locale_charset_str_no_utf8();