2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2012 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/>.
22 #include "claws-features.h"
27 #ifndef PANGO_ENABLE_ENGINE
28 # define PANGO_ENABLE_ENGINE
32 #include <glib/gi18n.h>
33 #include <gdk/gdkkeysyms.h>
36 #include <pango/pango-break.h>
41 #include <sys/types.h>
47 # include <sys/wait.h>
51 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
55 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
62 #include "mainwindow.h"
64 #ifndef USE_NEW_ADDRBOOK
65 #include "addressbook.h"
67 #include "addressbook-dbus.h"
68 #include "addressadd.h"
70 #include "folderview.h"
73 #include "stock_pixmap.h"
74 #include "send_message.h"
77 #include "customheader.h"
78 #include "prefs_common.h"
79 #include "prefs_account.h"
83 #include "procheader.h"
85 #include "statusbar.h"
88 #include "quoted-printable.h"
92 #include "gtkshruler.h"
94 #include "alertpanel.h"
95 #include "manage_window.h"
97 #include "addr_compl.h"
98 #include "quote_fmt.h"
100 #include "foldersel.h"
103 #include "message_search.h"
104 #include "combobox.h"
108 #include "autofaces.h"
109 #include "spell_entry.h"
122 #define N_ATTACH_COLS (N_COL_COLUMNS)
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
132 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
134 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
135 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
136 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
137 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
138 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
139 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
140 } ComposeCallAdvancedAction;
144 PRIORITY_HIGHEST = 1,
153 COMPOSE_INSERT_SUCCESS,
154 COMPOSE_INSERT_READ_ERROR,
155 COMPOSE_INSERT_INVALID_CHARACTER,
156 COMPOSE_INSERT_NO_FILE
157 } ComposeInsertResult;
161 COMPOSE_WRITE_FOR_SEND,
162 COMPOSE_WRITE_FOR_STORE
167 COMPOSE_QUOTE_FORCED,
174 SUBJECT_FIELD_PRESENT,
179 #define B64_LINE_SIZE 57
180 #define B64_BUFFSIZE 77
182 #define MAX_REFERENCES_LEN 999
184 static GList *compose_list = NULL;
186 static Compose *compose_generic_new (PrefsAccount *account,
190 GList *listAddress );
192 static Compose *compose_create (PrefsAccount *account,
197 static void compose_entry_mark_default_to (Compose *compose,
198 const gchar *address);
199 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
200 ComposeQuoteMode quote_mode,
204 static Compose *compose_forward_multiple (PrefsAccount *account,
205 GSList *msginfo_list);
206 static Compose *compose_reply (MsgInfo *msginfo,
207 ComposeQuoteMode quote_mode,
212 static Compose *compose_reply_mode (ComposeMode mode,
213 GSList *msginfo_list,
215 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
216 static void compose_update_privacy_systems_menu(Compose *compose);
218 static GtkWidget *compose_account_option_menu_create
220 static void compose_set_out_encoding (Compose *compose);
221 static void compose_set_template_menu (Compose *compose);
222 static void compose_destroy (Compose *compose);
224 static MailField compose_entries_set (Compose *compose,
226 ComposeEntryType to_type);
227 static gint compose_parse_header (Compose *compose,
229 static gint compose_parse_manual_headers (Compose *compose,
231 HeaderEntry *entries);
232 static gchar *compose_parse_references (const gchar *ref,
235 static gchar *compose_quote_fmt (Compose *compose,
241 gboolean need_unescape,
242 const gchar *err_msg);
244 static void compose_reply_set_entry (Compose *compose,
250 followup_and_reply_to);
251 static void compose_reedit_set_entry (Compose *compose,
254 static void compose_insert_sig (Compose *compose,
256 static ComposeInsertResult compose_insert_file (Compose *compose,
259 static gboolean compose_attach_append (Compose *compose,
262 const gchar *content_type,
263 const gchar *charset);
264 static void compose_attach_parts (Compose *compose,
267 static gboolean compose_beautify_paragraph (Compose *compose,
268 GtkTextIter *par_iter,
270 static void compose_wrap_all (Compose *compose);
271 static void compose_wrap_all_full (Compose *compose,
274 static void compose_set_title (Compose *compose);
275 static void compose_select_account (Compose *compose,
276 PrefsAccount *account,
279 static PrefsAccount *compose_current_mail_account(void);
280 /* static gint compose_send (Compose *compose); */
281 static gboolean compose_check_for_valid_recipient
283 static gboolean compose_check_entries (Compose *compose,
284 gboolean check_everything);
285 static gint compose_write_to_file (Compose *compose,
288 gboolean attach_parts);
289 static gint compose_write_body_to_file (Compose *compose,
291 static gint compose_remove_reedit_target (Compose *compose,
293 static void compose_remove_draft (Compose *compose);
294 static gint compose_queue_sub (Compose *compose,
298 gboolean check_subject,
299 gboolean remove_reedit_target);
300 static int compose_add_attachments (Compose *compose,
302 static gchar *compose_get_header (Compose *compose);
303 static gchar *compose_get_manual_headers_info (Compose *compose);
305 static void compose_convert_header (Compose *compose,
310 gboolean addr_field);
312 static void compose_attach_info_free (AttachInfo *ainfo);
313 static void compose_attach_remove_selected (GtkAction *action,
316 static void compose_template_apply (Compose *compose,
319 static void compose_attach_property (GtkAction *action,
321 static void compose_attach_property_create (gboolean *cancelled);
322 static void attach_property_ok (GtkWidget *widget,
323 gboolean *cancelled);
324 static void attach_property_cancel (GtkWidget *widget,
325 gboolean *cancelled);
326 static gint attach_property_delete_event (GtkWidget *widget,
328 gboolean *cancelled);
329 static gboolean attach_property_key_pressed (GtkWidget *widget,
331 gboolean *cancelled);
333 static void compose_exec_ext_editor (Compose *compose);
335 static gint compose_exec_ext_editor_real (const gchar *file);
336 static gboolean compose_ext_editor_kill (Compose *compose);
337 static gboolean compose_input_cb (GIOChannel *source,
338 GIOCondition condition,
340 static void compose_set_ext_editor_sensitive (Compose *compose,
342 #endif /* G_OS_UNIX */
344 static void compose_undo_state_changed (UndoMain *undostruct,
349 static void compose_create_header_entry (Compose *compose);
350 static void compose_add_header_entry (Compose *compose, const gchar *header,
351 gchar *text, ComposePrefType pref_type);
352 static void compose_remove_header_entries(Compose *compose);
354 static void compose_update_priority_menu_item(Compose * compose);
356 static void compose_spell_menu_changed (void *data);
357 static void compose_dict_changed (void *data);
359 static void compose_add_field_list ( Compose *compose,
360 GList *listAddress );
362 /* callback functions */
364 static void compose_notebook_size_alloc (GtkNotebook *notebook,
365 GtkAllocation *allocation,
367 static gboolean compose_edit_size_alloc (GtkEditable *widget,
368 GtkAllocation *allocation,
369 GtkSHRuler *shruler);
370 static void account_activated (GtkComboBox *optmenu,
372 static void attach_selected (GtkTreeView *tree_view,
373 GtkTreePath *tree_path,
374 GtkTreeViewColumn *column,
376 static gboolean attach_button_pressed (GtkWidget *widget,
377 GdkEventButton *event,
379 static gboolean attach_key_pressed (GtkWidget *widget,
382 static void compose_send_cb (GtkAction *action, gpointer data);
383 static void compose_send_later_cb (GtkAction *action, gpointer data);
385 static void compose_save_cb (GtkAction *action,
388 static void compose_attach_cb (GtkAction *action,
390 static void compose_insert_file_cb (GtkAction *action,
392 static void compose_insert_sig_cb (GtkAction *action,
395 static void compose_close_cb (GtkAction *action,
397 static void compose_print_cb (GtkAction *action,
400 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
402 static void compose_address_cb (GtkAction *action,
404 static void about_show_cb (GtkAction *action,
406 static void compose_template_activate_cb(GtkWidget *widget,
409 static void compose_ext_editor_cb (GtkAction *action,
412 static gint compose_delete_cb (GtkWidget *widget,
416 static void compose_undo_cb (GtkAction *action,
418 static void compose_redo_cb (GtkAction *action,
420 static void compose_cut_cb (GtkAction *action,
422 static void compose_copy_cb (GtkAction *action,
424 static void compose_paste_cb (GtkAction *action,
426 static void compose_paste_as_quote_cb (GtkAction *action,
428 static void compose_paste_no_wrap_cb (GtkAction *action,
430 static void compose_paste_wrap_cb (GtkAction *action,
432 static void compose_allsel_cb (GtkAction *action,
435 static void compose_advanced_action_cb (GtkAction *action,
438 static void compose_grab_focus_cb (GtkWidget *widget,
441 static void compose_changed_cb (GtkTextBuffer *textbuf,
444 static void compose_wrap_cb (GtkAction *action,
446 static void compose_wrap_all_cb (GtkAction *action,
448 static void compose_find_cb (GtkAction *action,
450 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
452 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
455 static void compose_toggle_ruler_cb (GtkToggleAction *action,
457 static void compose_toggle_sign_cb (GtkToggleAction *action,
459 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
461 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
462 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
463 static void activate_privacy_system (Compose *compose,
464 PrefsAccount *account,
466 static void compose_use_signing(Compose *compose, gboolean use_signing);
467 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
468 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
470 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
472 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
473 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
474 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
476 static void compose_attach_drag_received_cb (GtkWidget *widget,
477 GdkDragContext *drag_context,
480 GtkSelectionData *data,
484 static void compose_insert_drag_received_cb (GtkWidget *widget,
485 GdkDragContext *drag_context,
488 GtkSelectionData *data,
492 static void compose_header_drag_received_cb (GtkWidget *widget,
493 GdkDragContext *drag_context,
496 GtkSelectionData *data,
501 static gboolean compose_drag_drop (GtkWidget *widget,
502 GdkDragContext *drag_context,
504 guint time, gpointer user_data);
505 static gboolean completion_set_focus_to_subject
510 static void text_inserted (GtkTextBuffer *buffer,
515 static Compose *compose_generic_reply(MsgInfo *msginfo,
516 ComposeQuoteMode quote_mode,
520 gboolean followup_and_reply_to,
523 static void compose_headerentry_changed_cb (GtkWidget *entry,
524 ComposeHeaderEntry *headerentry);
525 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
527 ComposeHeaderEntry *headerentry);
528 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
529 ComposeHeaderEntry *headerentry);
531 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
533 static void compose_allow_user_actions (Compose *compose, gboolean allow);
535 static void compose_nothing_cb (GtkAction *action, gpointer data)
541 static void compose_check_all (GtkAction *action, gpointer data);
542 static void compose_highlight_all (GtkAction *action, gpointer data);
543 static void compose_check_backwards (GtkAction *action, gpointer data);
544 static void compose_check_forwards_go (GtkAction *action, gpointer data);
547 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
549 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
552 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
553 FolderItem *folder_item);
555 static void compose_attach_update_label(Compose *compose);
556 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
557 gboolean respect_default_to);
559 static GtkActionEntry compose_popup_entries[] =
561 {"Compose", NULL, "Compose" },
562 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
563 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
564 {"Compose/---", NULL, "---", NULL, NULL, NULL },
565 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
568 static GtkActionEntry compose_entries[] =
570 {"Menu", NULL, "Menu" },
572 {"Message", NULL, N_("_Message") },
573 {"Edit", NULL, N_("_Edit") },
575 {"Spelling", NULL, N_("_Spelling") },
577 {"Options", NULL, N_("_Options") },
578 {"Tools", NULL, N_("_Tools") },
579 {"Help", NULL, N_("_Help") },
581 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
582 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
583 {"Message/---", NULL, "---" },
585 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
586 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
587 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
588 /* {"Message/---", NULL, "---" }, */
589 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
590 /* {"Message/---", NULL, "---" }, */
591 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
592 /* {"Message/---", NULL, "---" }, */
593 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
596 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
597 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
598 {"Edit/---", NULL, "---" },
600 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
601 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
602 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
604 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
605 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
606 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
607 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
609 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
611 {"Edit/Advanced", NULL, N_("A_dvanced") },
612 {"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*/
613 {"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*/
614 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
615 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
616 {"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*/
617 {"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*/
618 {"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*/
619 {"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*/
620 {"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*/
621 {"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*/
622 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
623 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
624 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
625 {"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*/
627 /* {"Edit/---", NULL, "---" }, */
628 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
630 /* {"Edit/---", NULL, "---" }, */
631 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
632 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
633 /* {"Edit/---", NULL, "---" }, */
634 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
637 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
638 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
639 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
640 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
642 {"Spelling/---", NULL, "---" },
643 {"Spelling/Options", NULL, N_("_Options") },
648 {"Options/ReplyMode", NULL, N_("Reply _mode") },
649 {"Options/---", NULL, "---" },
650 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
651 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
653 /* {"Options/---", NULL, "---" }, */
655 {"Options/Priority", NULL, N_("_Priority") },
657 {"Options/Encoding", NULL, N_("Character _encoding") },
658 {"Options/Encoding/---", NULL, "---" },
659 #define ENC_ACTION(cs_char,c_char,string) \
660 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
662 {"Options/Encoding/Western", NULL, N_("Western European") },
663 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
664 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
665 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
666 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
667 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
668 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
669 {"Options/Encoding/Korean", NULL, N_("Korean") },
670 {"Options/Encoding/Thai", NULL, N_("Thai") },
673 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
675 {"Tools/Template", NULL, N_("_Template") },
676 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
677 {"Tools/Actions", NULL, N_("Actio_ns") },
678 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
681 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
684 static GtkToggleActionEntry compose_toggle_entries[] =
686 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
687 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
688 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
689 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
690 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
691 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
692 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
695 static GtkRadioActionEntry compose_radio_rm_entries[] =
697 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
698 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
699 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
700 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
703 static GtkRadioActionEntry compose_radio_prio_entries[] =
705 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
706 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
707 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
708 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
709 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
712 static GtkRadioActionEntry compose_radio_enc_entries[] =
714 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
729 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
730 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
731 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
732 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
733 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
734 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
735 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
736 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
737 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
738 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
739 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
740 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
741 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
742 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
743 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
744 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
745 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
748 static GtkTargetEntry compose_mime_types[] =
750 {"text/uri-list", 0, 0},
751 {"UTF8_STRING", 0, 0},
755 static gboolean compose_put_existing_to_front(MsgInfo *info)
757 GList *compose_list = compose_get_compose_list();
761 for (elem = compose_list; elem != NULL && elem->data != NULL;
763 Compose *c = (Compose*)elem->data;
765 if (!c->targetinfo || !c->targetinfo->msgid ||
769 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
770 gtkut_window_popup(c->window);
778 static GdkColor quote_color1 =
779 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
780 static GdkColor quote_color2 =
781 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
782 static GdkColor quote_color3 =
783 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
785 static GdkColor quote_bgcolor1 =
786 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
787 static GdkColor quote_bgcolor2 =
788 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
789 static GdkColor quote_bgcolor3 =
790 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
792 static GdkColor signature_color = {
799 static GdkColor uri_color = {
806 static void compose_create_tags(GtkTextView *text, Compose *compose)
808 GtkTextBuffer *buffer;
809 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
810 #if !GTK_CHECK_VERSION(2, 24, 0)
817 buffer = gtk_text_view_get_buffer(text);
819 if (prefs_common.enable_color) {
820 /* grab the quote colors, converting from an int to a GdkColor */
821 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
823 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
825 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
827 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
829 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
831 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
833 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
835 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
838 signature_color = quote_color1 = quote_color2 = quote_color3 =
839 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
842 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
843 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
844 "foreground-gdk", "e_color1,
845 "paragraph-background-gdk", "e_bgcolor1,
847 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
848 "foreground-gdk", "e_color2,
849 "paragraph-background-gdk", "e_bgcolor2,
851 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
852 "foreground-gdk", "e_color3,
853 "paragraph-background-gdk", "e_bgcolor3,
856 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
857 "foreground-gdk", "e_color1,
859 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
860 "foreground-gdk", "e_color2,
862 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
863 "foreground-gdk", "e_color3,
867 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
868 "foreground-gdk", &signature_color,
871 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
872 "foreground-gdk", &uri_color,
874 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
875 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
877 color[0] = quote_color1;
878 color[1] = quote_color2;
879 color[2] = quote_color3;
880 color[3] = quote_bgcolor1;
881 color[4] = quote_bgcolor2;
882 color[5] = quote_bgcolor3;
883 color[6] = signature_color;
884 color[7] = uri_color;
885 #if !GTK_CHECK_VERSION(2, 24, 0)
886 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
887 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
889 for (i = 0; i < 8; i++) {
890 if (success[i] == FALSE) {
893 g_warning("Compose: color allocation failed.\n");
894 style = gtk_widget_get_style(GTK_WIDGET(text));
895 quote_color1 = quote_color2 = quote_color3 =
896 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
897 signature_color = uri_color = black;
903 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
906 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
909 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
911 return compose_generic_new(account, mailto, item, NULL, NULL);
914 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
916 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
919 #define SCROLL_TO_CURSOR(compose) { \
920 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
921 gtk_text_view_get_buffer( \
922 GTK_TEXT_VIEW(compose->text))); \
923 gtk_text_view_scroll_mark_onscreen( \
924 GTK_TEXT_VIEW(compose->text), \
928 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
931 if (folderidentifier) {
932 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
933 prefs_common.compose_save_to_history = add_history(
934 prefs_common.compose_save_to_history, folderidentifier);
935 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
936 prefs_common.compose_save_to_history);
939 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
940 if (folderidentifier)
941 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
943 gtk_entry_set_text(GTK_ENTRY(entry), "");
946 static gchar *compose_get_save_to(Compose *compose)
949 gchar *result = NULL;
950 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
951 result = gtk_editable_get_chars(entry, 0, -1);
954 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
955 prefs_common.compose_save_to_history = add_history(
956 prefs_common.compose_save_to_history, result);
957 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
958 prefs_common.compose_save_to_history);
963 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
964 GList *attach_files, GList *listAddress )
967 GtkTextView *textview;
968 GtkTextBuffer *textbuf;
970 const gchar *subject_format = NULL;
971 const gchar *body_format = NULL;
972 gchar *mailto_from = NULL;
973 PrefsAccount *mailto_account = NULL;
974 MsgInfo* dummyinfo = NULL;
975 gint cursor_pos = -1;
976 MailField mfield = NO_FIELD_PRESENT;
980 /* check if mailto defines a from */
981 if (mailto && *mailto != '\0') {
982 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
983 /* mailto defines a from, check if we can get account prefs from it,
984 if not, the account prefs will be guessed using other ways, but we'll keep
987 mailto_account = account_find_from_address(mailto_from, TRUE);
989 account = mailto_account;
992 /* if no account prefs set from mailto, set if from folder prefs (if any) */
993 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
994 account = account_find_from_id(item->prefs->default_account);
996 /* if no account prefs set, fallback to the current one */
997 if (!account) account = cur_account;
998 cm_return_val_if_fail(account != NULL, NULL);
1000 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1002 /* override from name if mailto asked for it */
1004 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1005 g_free(mailto_from);
1007 /* override from name according to folder properties */
1008 if (item && item->prefs &&
1009 item->prefs->compose_with_format &&
1010 item->prefs->compose_override_from_format &&
1011 *item->prefs->compose_override_from_format != '\0') {
1016 dummyinfo = compose_msginfo_new_from_compose(compose);
1018 /* decode \-escape sequences in the internal representation of the quote format */
1019 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1020 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1023 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1024 compose->gtkaspell);
1026 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1028 quote_fmt_scan_string(tmp);
1031 buf = quote_fmt_get_buffer();
1033 alertpanel_error(_("New message From format error."));
1035 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1036 quote_fmt_reset_vartable();
1041 compose->replyinfo = NULL;
1042 compose->fwdinfo = NULL;
1044 textview = GTK_TEXT_VIEW(compose->text);
1045 textbuf = gtk_text_view_get_buffer(textview);
1046 compose_create_tags(textview, compose);
1048 undo_block(compose->undostruct);
1050 compose_set_dictionaries_from_folder_prefs(compose, item);
1053 if (account->auto_sig)
1054 compose_insert_sig(compose, FALSE);
1055 gtk_text_buffer_get_start_iter(textbuf, &iter);
1056 gtk_text_buffer_place_cursor(textbuf, &iter);
1058 if (account->protocol != A_NNTP) {
1059 if (mailto && *mailto != '\0') {
1060 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1063 compose_set_folder_prefs(compose, item, TRUE);
1065 if (item && item->ret_rcpt) {
1066 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1069 if (mailto && *mailto != '\0') {
1070 if (!strchr(mailto, '@'))
1071 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1073 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1074 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1075 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1076 mfield = TO_FIELD_PRESENT;
1079 * CLAWS: just don't allow return receipt request, even if the user
1080 * may want to send an email. simple but foolproof.
1082 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1084 compose_add_field_list( compose, listAddress );
1086 if (item && item->prefs && item->prefs->compose_with_format) {
1087 subject_format = item->prefs->compose_subject_format;
1088 body_format = item->prefs->compose_body_format;
1089 } else if (account->compose_with_format) {
1090 subject_format = account->compose_subject_format;
1091 body_format = account->compose_body_format;
1092 } else if (prefs_common.compose_with_format) {
1093 subject_format = prefs_common.compose_subject_format;
1094 body_format = prefs_common.compose_body_format;
1097 if (subject_format || body_format) {
1100 && *subject_format != '\0' )
1102 gchar *subject = NULL;
1107 dummyinfo = compose_msginfo_new_from_compose(compose);
1109 /* decode \-escape sequences in the internal representation of the quote format */
1110 tmp = g_malloc(strlen(subject_format)+1);
1111 pref_get_unescaped_pref(tmp, subject_format);
1113 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1115 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1116 compose->gtkaspell);
1118 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1120 quote_fmt_scan_string(tmp);
1123 buf = quote_fmt_get_buffer();
1125 alertpanel_error(_("New message subject format error."));
1127 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1128 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1129 quote_fmt_reset_vartable();
1133 mfield = SUBJECT_FIELD_PRESENT;
1137 && *body_format != '\0' )
1140 GtkTextBuffer *buffer;
1141 GtkTextIter start, end;
1145 dummyinfo = compose_msginfo_new_from_compose(compose);
1147 text = GTK_TEXT_VIEW(compose->text);
1148 buffer = gtk_text_view_get_buffer(text);
1149 gtk_text_buffer_get_start_iter(buffer, &start);
1150 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1151 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1153 compose_quote_fmt(compose, dummyinfo,
1155 NULL, tmp, FALSE, TRUE,
1156 _("The body of the \"New message\" template has an error at line %d."));
1157 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1158 quote_fmt_reset_vartable();
1162 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1163 gtkaspell_highlight_all(compose->gtkaspell);
1165 mfield = BODY_FIELD_PRESENT;
1169 procmsg_msginfo_free( dummyinfo );
1175 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1176 ainfo = (AttachInfo *) curr->data;
1177 compose_attach_append(compose, ainfo->file, ainfo->name,
1178 ainfo->content_type, ainfo->charset);
1182 compose_show_first_last_header(compose, TRUE);
1184 /* Set save folder */
1185 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1186 gchar *folderidentifier;
1188 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1189 folderidentifier = folder_item_get_identifier(item);
1190 compose_set_save_to(compose, folderidentifier);
1191 g_free(folderidentifier);
1194 /* Place cursor according to provided input (mfield) */
1196 case NO_FIELD_PRESENT:
1197 if (compose->header_last)
1198 gtk_widget_grab_focus(compose->header_last->entry);
1200 case TO_FIELD_PRESENT:
1201 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1203 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1206 gtk_widget_grab_focus(compose->subject_entry);
1208 case SUBJECT_FIELD_PRESENT:
1209 textview = GTK_TEXT_VIEW(compose->text);
1212 textbuf = gtk_text_view_get_buffer(textview);
1215 mark = gtk_text_buffer_get_insert(textbuf);
1216 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1217 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1219 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1220 * only defers where it comes to the variable body
1221 * is not null. If no body is present compose->text
1222 * will be null in which case you cannot place the
1223 * cursor inside the component so. An empty component
1224 * is therefore created before placing the cursor
1226 case BODY_FIELD_PRESENT:
1227 cursor_pos = quote_fmt_get_cursor_pos();
1228 if (cursor_pos == -1)
1229 gtk_widget_grab_focus(compose->header_last->entry);
1231 gtk_widget_grab_focus(compose->text);
1235 undo_unblock(compose->undostruct);
1237 if (prefs_common.auto_exteditor)
1238 compose_exec_ext_editor(compose);
1240 compose->draft_timeout_tag = -1;
1241 SCROLL_TO_CURSOR(compose);
1243 compose->modified = FALSE;
1244 compose_set_title(compose);
1246 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1251 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1252 gboolean override_pref, const gchar *system)
1254 const gchar *privacy = NULL;
1256 cm_return_if_fail(compose != NULL);
1257 cm_return_if_fail(account != NULL);
1259 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1264 else if (account->default_privacy_system
1265 && strlen(account->default_privacy_system)) {
1266 privacy = account->default_privacy_system;
1268 GSList *privacy_avail = privacy_get_system_ids();
1269 if (privacy_avail && g_slist_length(privacy_avail)) {
1270 privacy = (gchar *)(privacy_avail->data);
1273 if (privacy != NULL) {
1275 g_free(compose->privacy_system);
1276 compose->privacy_system = NULL;
1278 if (compose->privacy_system == NULL)
1279 compose->privacy_system = g_strdup(privacy);
1280 else if (*(compose->privacy_system) == '\0') {
1281 g_free(compose->privacy_system);
1282 compose->privacy_system = g_strdup(privacy);
1284 compose_update_privacy_system_menu_item(compose, FALSE);
1285 compose_use_encryption(compose, TRUE);
1289 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1291 const gchar *privacy = NULL;
1295 else if (account->default_privacy_system
1296 && strlen(account->default_privacy_system)) {
1297 privacy = account->default_privacy_system;
1299 GSList *privacy_avail = privacy_get_system_ids();
1300 if (privacy_avail && g_slist_length(privacy_avail)) {
1301 privacy = (gchar *)(privacy_avail->data);
1305 if (privacy != NULL) {
1307 g_free(compose->privacy_system);
1308 compose->privacy_system = NULL;
1310 if (compose->privacy_system == NULL)
1311 compose->privacy_system = g_strdup(privacy);
1312 compose_update_privacy_system_menu_item(compose, FALSE);
1313 compose_use_signing(compose, TRUE);
1317 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1321 Compose *compose = NULL;
1323 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1325 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1326 cm_return_val_if_fail(msginfo != NULL, NULL);
1328 list_len = g_slist_length(msginfo_list);
1332 case COMPOSE_REPLY_TO_ADDRESS:
1333 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1334 FALSE, prefs_common.default_reply_list, FALSE, body);
1336 case COMPOSE_REPLY_WITH_QUOTE:
1337 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1338 FALSE, prefs_common.default_reply_list, FALSE, body);
1340 case COMPOSE_REPLY_WITHOUT_QUOTE:
1341 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1342 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1344 case COMPOSE_REPLY_TO_SENDER:
1345 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1346 FALSE, FALSE, TRUE, body);
1348 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1349 compose = compose_followup_and_reply_to(msginfo,
1350 COMPOSE_QUOTE_CHECK,
1351 FALSE, FALSE, body);
1353 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1354 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1355 FALSE, FALSE, TRUE, body);
1357 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1358 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1359 FALSE, FALSE, TRUE, NULL);
1361 case COMPOSE_REPLY_TO_ALL:
1362 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1363 TRUE, FALSE, FALSE, body);
1365 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1366 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1367 TRUE, FALSE, FALSE, body);
1369 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1370 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1371 TRUE, FALSE, FALSE, NULL);
1373 case COMPOSE_REPLY_TO_LIST:
1374 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1375 FALSE, TRUE, FALSE, body);
1377 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1378 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1379 FALSE, TRUE, FALSE, body);
1381 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1382 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1383 FALSE, TRUE, FALSE, NULL);
1385 case COMPOSE_FORWARD:
1386 if (prefs_common.forward_as_attachment) {
1387 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1390 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1394 case COMPOSE_FORWARD_INLINE:
1395 /* check if we reply to more than one Message */
1396 if (list_len == 1) {
1397 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1400 /* more messages FALL THROUGH */
1401 case COMPOSE_FORWARD_AS_ATTACH:
1402 compose = compose_forward_multiple(NULL, msginfo_list);
1404 case COMPOSE_REDIRECT:
1405 compose = compose_redirect(NULL, msginfo, FALSE);
1408 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1411 if (compose == NULL) {
1412 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1416 compose->rmode = mode;
1417 switch (compose->rmode) {
1419 case COMPOSE_REPLY_WITH_QUOTE:
1420 case COMPOSE_REPLY_WITHOUT_QUOTE:
1421 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1422 debug_print("reply mode Normal\n");
1423 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1424 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1426 case COMPOSE_REPLY_TO_SENDER:
1427 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1428 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1429 debug_print("reply mode Sender\n");
1430 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1432 case COMPOSE_REPLY_TO_ALL:
1433 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1434 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1435 debug_print("reply mode All\n");
1436 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1438 case COMPOSE_REPLY_TO_LIST:
1439 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1440 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1441 debug_print("reply mode List\n");
1442 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1444 case COMPOSE_REPLY_TO_ADDRESS:
1445 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1453 static Compose *compose_reply(MsgInfo *msginfo,
1454 ComposeQuoteMode quote_mode,
1460 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1461 to_sender, FALSE, body);
1464 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1465 ComposeQuoteMode quote_mode,
1470 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1471 to_sender, TRUE, body);
1474 static void compose_extract_original_charset(Compose *compose)
1476 MsgInfo *info = NULL;
1477 if (compose->replyinfo) {
1478 info = compose->replyinfo;
1479 } else if (compose->fwdinfo) {
1480 info = compose->fwdinfo;
1481 } else if (compose->targetinfo) {
1482 info = compose->targetinfo;
1485 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1486 MimeInfo *partinfo = mimeinfo;
1487 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1488 partinfo = procmime_mimeinfo_next(partinfo);
1490 compose->orig_charset =
1491 g_strdup(procmime_mimeinfo_get_parameter(
1492 partinfo, "charset"));
1494 procmime_mimeinfo_free_all(mimeinfo);
1498 #define SIGNAL_BLOCK(buffer) { \
1499 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1500 G_CALLBACK(compose_changed_cb), \
1502 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1503 G_CALLBACK(text_inserted), \
1507 #define SIGNAL_UNBLOCK(buffer) { \
1508 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1509 G_CALLBACK(compose_changed_cb), \
1511 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1512 G_CALLBACK(text_inserted), \
1516 static Compose *compose_generic_reply(MsgInfo *msginfo,
1517 ComposeQuoteMode quote_mode,
1518 gboolean to_all, gboolean to_ml,
1520 gboolean followup_and_reply_to,
1524 PrefsAccount *account = NULL;
1525 GtkTextView *textview;
1526 GtkTextBuffer *textbuf;
1527 gboolean quote = FALSE;
1528 const gchar *qmark = NULL;
1529 const gchar *body_fmt = NULL;
1530 gchar *s_system = NULL;
1532 cm_return_val_if_fail(msginfo != NULL, NULL);
1533 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1535 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1537 cm_return_val_if_fail(account != NULL, NULL);
1539 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1541 compose->updating = TRUE;
1543 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1544 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1546 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1547 if (!compose->replyinfo)
1548 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1550 compose_extract_original_charset(compose);
1552 if (msginfo->folder && msginfo->folder->ret_rcpt)
1553 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1555 /* Set save folder */
1556 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1557 gchar *folderidentifier;
1559 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1560 folderidentifier = folder_item_get_identifier(msginfo->folder);
1561 compose_set_save_to(compose, folderidentifier);
1562 g_free(folderidentifier);
1565 if (compose_parse_header(compose, msginfo) < 0) {
1566 compose->updating = FALSE;
1567 compose_destroy(compose);
1571 /* override from name according to folder properties */
1572 if (msginfo->folder && msginfo->folder->prefs &&
1573 msginfo->folder->prefs->reply_with_format &&
1574 msginfo->folder->prefs->reply_override_from_format &&
1575 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1580 /* decode \-escape sequences in the internal representation of the quote format */
1581 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1582 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1585 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1586 compose->gtkaspell);
1588 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1590 quote_fmt_scan_string(tmp);
1593 buf = quote_fmt_get_buffer();
1595 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1597 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1598 quote_fmt_reset_vartable();
1603 textview = (GTK_TEXT_VIEW(compose->text));
1604 textbuf = gtk_text_view_get_buffer(textview);
1605 compose_create_tags(textview, compose);
1607 undo_block(compose->undostruct);
1609 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1610 gtkaspell_block_check(compose->gtkaspell);
1613 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1614 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1615 /* use the reply format of folder (if enabled), or the account's one
1616 (if enabled) or fallback to the global reply format, which is always
1617 enabled (even if empty), and use the relevant quotemark */
1619 if (msginfo->folder && msginfo->folder->prefs &&
1620 msginfo->folder->prefs->reply_with_format) {
1621 qmark = msginfo->folder->prefs->reply_quotemark;
1622 body_fmt = msginfo->folder->prefs->reply_body_format;
1624 } else if (account->reply_with_format) {
1625 qmark = account->reply_quotemark;
1626 body_fmt = account->reply_body_format;
1629 qmark = prefs_common.quotemark;
1630 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1631 body_fmt = gettext(prefs_common.quotefmt);
1638 /* empty quotemark is not allowed */
1639 if (qmark == NULL || *qmark == '\0')
1641 compose_quote_fmt(compose, compose->replyinfo,
1642 body_fmt, qmark, body, FALSE, TRUE,
1643 _("The body of the \"Reply\" template has an error at line %d."));
1644 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1645 quote_fmt_reset_vartable();
1648 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1649 compose_force_encryption(compose, account, FALSE, s_system);
1652 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1653 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1654 compose_force_signing(compose, account, s_system);
1658 SIGNAL_BLOCK(textbuf);
1660 if (account->auto_sig)
1661 compose_insert_sig(compose, FALSE);
1663 compose_wrap_all(compose);
1666 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1667 gtkaspell_highlight_all(compose->gtkaspell);
1668 gtkaspell_unblock_check(compose->gtkaspell);
1670 SIGNAL_UNBLOCK(textbuf);
1672 gtk_widget_grab_focus(compose->text);
1674 undo_unblock(compose->undostruct);
1676 if (prefs_common.auto_exteditor)
1677 compose_exec_ext_editor(compose);
1679 compose->modified = FALSE;
1680 compose_set_title(compose);
1682 compose->updating = FALSE;
1683 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1684 SCROLL_TO_CURSOR(compose);
1686 if (compose->deferred_destroy) {
1687 compose_destroy(compose);
1695 #define INSERT_FW_HEADER(var, hdr) \
1696 if (msginfo->var && *msginfo->var) { \
1697 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1698 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1699 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1702 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1703 gboolean as_attach, const gchar *body,
1704 gboolean no_extedit,
1708 GtkTextView *textview;
1709 GtkTextBuffer *textbuf;
1710 gint cursor_pos = -1;
1713 cm_return_val_if_fail(msginfo != NULL, NULL);
1714 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1717 !(account = compose_guess_forward_account_from_msginfo
1719 account = cur_account;
1721 if (!prefs_common.forward_as_attachment)
1722 mode = COMPOSE_FORWARD_INLINE;
1724 mode = COMPOSE_FORWARD;
1725 compose = compose_create(account, msginfo->folder, mode, batch);
1727 compose->updating = TRUE;
1728 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1729 if (!compose->fwdinfo)
1730 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1732 compose_extract_original_charset(compose);
1734 if (msginfo->subject && *msginfo->subject) {
1735 gchar *buf, *buf2, *p;
1737 buf = p = g_strdup(msginfo->subject);
1738 p += subject_get_prefix_length(p);
1739 memmove(buf, p, strlen(p) + 1);
1741 buf2 = g_strdup_printf("Fw: %s", buf);
1742 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1748 /* override from name according to folder properties */
1749 if (msginfo->folder && msginfo->folder->prefs &&
1750 msginfo->folder->prefs->forward_with_format &&
1751 msginfo->folder->prefs->forward_override_from_format &&
1752 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1756 MsgInfo *full_msginfo = NULL;
1759 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1761 full_msginfo = procmsg_msginfo_copy(msginfo);
1763 /* decode \-escape sequences in the internal representation of the quote format */
1764 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1765 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1768 gtkaspell_block_check(compose->gtkaspell);
1769 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1770 compose->gtkaspell);
1772 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1774 quote_fmt_scan_string(tmp);
1777 buf = quote_fmt_get_buffer();
1779 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1781 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1782 quote_fmt_reset_vartable();
1785 procmsg_msginfo_free(full_msginfo);
1788 textview = GTK_TEXT_VIEW(compose->text);
1789 textbuf = gtk_text_view_get_buffer(textview);
1790 compose_create_tags(textview, compose);
1792 undo_block(compose->undostruct);
1796 msgfile = procmsg_get_message_file(msginfo);
1797 if (!is_file_exist(msgfile))
1798 g_warning("%s: file not exist\n", msgfile);
1800 compose_attach_append(compose, msgfile, msgfile,
1801 "message/rfc822", NULL);
1805 const gchar *qmark = NULL;
1806 const gchar *body_fmt = NULL;
1807 MsgInfo *full_msginfo;
1809 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1810 body_fmt = gettext(prefs_common.fw_quotefmt);
1814 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1816 full_msginfo = procmsg_msginfo_copy(msginfo);
1818 /* use the forward format of folder (if enabled), or the account's one
1819 (if enabled) or fallback to the global forward format, which is always
1820 enabled (even if empty), and use the relevant quotemark */
1821 if (msginfo->folder && msginfo->folder->prefs &&
1822 msginfo->folder->prefs->forward_with_format) {
1823 qmark = msginfo->folder->prefs->forward_quotemark;
1824 body_fmt = msginfo->folder->prefs->forward_body_format;
1826 } else if (account->forward_with_format) {
1827 qmark = account->forward_quotemark;
1828 body_fmt = account->forward_body_format;
1831 qmark = prefs_common.fw_quotemark;
1832 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1833 body_fmt = gettext(prefs_common.fw_quotefmt);
1838 /* empty quotemark is not allowed */
1839 if (qmark == NULL || *qmark == '\0')
1842 compose_quote_fmt(compose, full_msginfo,
1843 body_fmt, qmark, body, FALSE, TRUE,
1844 _("The body of the \"Forward\" template has an error at line %d."));
1845 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1846 quote_fmt_reset_vartable();
1847 compose_attach_parts(compose, msginfo);
1849 procmsg_msginfo_free(full_msginfo);
1852 SIGNAL_BLOCK(textbuf);
1854 if (account->auto_sig)
1855 compose_insert_sig(compose, FALSE);
1857 compose_wrap_all(compose);
1860 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1861 gtkaspell_highlight_all(compose->gtkaspell);
1862 gtkaspell_unblock_check(compose->gtkaspell);
1864 SIGNAL_UNBLOCK(textbuf);
1866 cursor_pos = quote_fmt_get_cursor_pos();
1867 if (cursor_pos == -1)
1868 gtk_widget_grab_focus(compose->header_last->entry);
1870 gtk_widget_grab_focus(compose->text);
1872 if (!no_extedit && prefs_common.auto_exteditor)
1873 compose_exec_ext_editor(compose);
1876 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1877 gchar *folderidentifier;
1879 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1880 folderidentifier = folder_item_get_identifier(msginfo->folder);
1881 compose_set_save_to(compose, folderidentifier);
1882 g_free(folderidentifier);
1885 undo_unblock(compose->undostruct);
1887 compose->modified = FALSE;
1888 compose_set_title(compose);
1890 compose->updating = FALSE;
1891 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1892 SCROLL_TO_CURSOR(compose);
1894 if (compose->deferred_destroy) {
1895 compose_destroy(compose);
1899 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1904 #undef INSERT_FW_HEADER
1906 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1909 GtkTextView *textview;
1910 GtkTextBuffer *textbuf;
1914 gboolean single_mail = TRUE;
1916 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1918 if (g_slist_length(msginfo_list) > 1)
1919 single_mail = FALSE;
1921 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1922 if (((MsgInfo *)msginfo->data)->folder == NULL)
1925 /* guess account from first selected message */
1927 !(account = compose_guess_forward_account_from_msginfo
1928 (msginfo_list->data)))
1929 account = cur_account;
1931 cm_return_val_if_fail(account != NULL, NULL);
1933 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1934 if (msginfo->data) {
1935 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1936 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1940 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1941 g_warning("no msginfo_list");
1945 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1947 compose->updating = TRUE;
1949 /* override from name according to folder properties */
1950 if (msginfo_list->data) {
1951 MsgInfo *msginfo = msginfo_list->data;
1953 if (msginfo->folder && msginfo->folder->prefs &&
1954 msginfo->folder->prefs->forward_with_format &&
1955 msginfo->folder->prefs->forward_override_from_format &&
1956 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1961 /* decode \-escape sequences in the internal representation of the quote format */
1962 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1963 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1966 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1967 compose->gtkaspell);
1969 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1971 quote_fmt_scan_string(tmp);
1974 buf = quote_fmt_get_buffer();
1976 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1978 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1979 quote_fmt_reset_vartable();
1985 textview = GTK_TEXT_VIEW(compose->text);
1986 textbuf = gtk_text_view_get_buffer(textview);
1987 compose_create_tags(textview, compose);
1989 undo_block(compose->undostruct);
1990 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1991 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1993 if (!is_file_exist(msgfile))
1994 g_warning("%s: file not exist\n", msgfile);
1996 compose_attach_append(compose, msgfile, msgfile,
1997 "message/rfc822", NULL);
2002 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2003 if (info->subject && *info->subject) {
2004 gchar *buf, *buf2, *p;
2006 buf = p = g_strdup(info->subject);
2007 p += subject_get_prefix_length(p);
2008 memmove(buf, p, strlen(p) + 1);
2010 buf2 = g_strdup_printf("Fw: %s", buf);
2011 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2017 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2018 _("Fw: multiple emails"));
2021 SIGNAL_BLOCK(textbuf);
2023 if (account->auto_sig)
2024 compose_insert_sig(compose, FALSE);
2026 compose_wrap_all(compose);
2028 SIGNAL_UNBLOCK(textbuf);
2030 gtk_text_buffer_get_start_iter(textbuf, &iter);
2031 gtk_text_buffer_place_cursor(textbuf, &iter);
2033 gtk_widget_grab_focus(compose->header_last->entry);
2034 undo_unblock(compose->undostruct);
2035 compose->modified = FALSE;
2036 compose_set_title(compose);
2038 compose->updating = FALSE;
2039 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2040 SCROLL_TO_CURSOR(compose);
2042 if (compose->deferred_destroy) {
2043 compose_destroy(compose);
2047 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2052 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2054 GtkTextIter start = *iter;
2055 GtkTextIter end_iter;
2056 int start_pos = gtk_text_iter_get_offset(&start);
2058 if (!compose->account->sig_sep)
2061 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2062 start_pos+strlen(compose->account->sig_sep));
2064 /* check sig separator */
2065 str = gtk_text_iter_get_text(&start, &end_iter);
2066 if (!strcmp(str, compose->account->sig_sep)) {
2068 /* check end of line (\n) */
2069 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2070 start_pos+strlen(compose->account->sig_sep));
2071 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2072 start_pos+strlen(compose->account->sig_sep)+1);
2073 tmp = gtk_text_iter_get_text(&start, &end_iter);
2074 if (!strcmp(tmp,"\n")) {
2086 static void compose_colorize_signature(Compose *compose)
2088 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2090 GtkTextIter end_iter;
2091 gtk_text_buffer_get_start_iter(buffer, &iter);
2092 while (gtk_text_iter_forward_line(&iter))
2093 if (compose_is_sig_separator(compose, buffer, &iter)) {
2094 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2095 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2099 #define BLOCK_WRAP() { \
2100 prev_autowrap = compose->autowrap; \
2101 buffer = gtk_text_view_get_buffer( \
2102 GTK_TEXT_VIEW(compose->text)); \
2103 compose->autowrap = FALSE; \
2105 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2106 G_CALLBACK(compose_changed_cb), \
2108 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2109 G_CALLBACK(text_inserted), \
2112 #define UNBLOCK_WRAP() { \
2113 compose->autowrap = prev_autowrap; \
2114 if (compose->autowrap) { \
2115 gint old = compose->draft_timeout_tag; \
2116 compose->draft_timeout_tag = -2; \
2117 compose_wrap_all(compose); \
2118 compose->draft_timeout_tag = old; \
2121 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2122 G_CALLBACK(compose_changed_cb), \
2124 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2125 G_CALLBACK(text_inserted), \
2129 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2131 Compose *compose = NULL;
2132 PrefsAccount *account = NULL;
2133 GtkTextView *textview;
2134 GtkTextBuffer *textbuf;
2138 gchar buf[BUFFSIZE];
2139 gboolean use_signing = FALSE;
2140 gboolean use_encryption = FALSE;
2141 gchar *privacy_system = NULL;
2142 int priority = PRIORITY_NORMAL;
2143 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2144 gboolean autowrap = prefs_common.autowrap;
2145 gboolean autoindent = prefs_common.auto_indent;
2146 HeaderEntry *manual_headers = NULL;
2148 cm_return_val_if_fail(msginfo != NULL, NULL);
2149 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2151 if (compose_put_existing_to_front(msginfo)) {
2155 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2156 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2157 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2158 gchar queueheader_buf[BUFFSIZE];
2161 /* Select Account from queue headers */
2162 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2163 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2164 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2165 account = account_find_from_id(id);
2167 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2168 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2169 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2170 account = account_find_from_id(id);
2172 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2173 sizeof(queueheader_buf), "NAID:")) {
2174 id = atoi(&queueheader_buf[strlen("NAID:")]);
2175 account = account_find_from_id(id);
2177 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2178 sizeof(queueheader_buf), "MAID:")) {
2179 id = atoi(&queueheader_buf[strlen("MAID:")]);
2180 account = account_find_from_id(id);
2182 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2183 sizeof(queueheader_buf), "S:")) {
2184 account = account_find_from_address(queueheader_buf, FALSE);
2186 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2187 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2188 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2189 use_signing = param;
2192 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2193 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2194 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2195 use_signing = param;
2198 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2199 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2200 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2201 use_encryption = param;
2203 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2204 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2205 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2206 use_encryption = param;
2208 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2209 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2210 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2213 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2214 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2215 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2218 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2219 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2220 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2222 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2223 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2224 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2226 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2227 sizeof(queueheader_buf), "X-Priority: ")) {
2228 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2231 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2232 sizeof(queueheader_buf), "RMID:")) {
2233 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2234 if (tokens[0] && tokens[1] && tokens[2]) {
2235 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2236 if (orig_item != NULL) {
2237 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2242 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2243 sizeof(queueheader_buf), "FMID:")) {
2244 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2245 if (tokens[0] && tokens[1] && tokens[2]) {
2246 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2247 if (orig_item != NULL) {
2248 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2253 /* Get manual headers */
2254 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2255 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2256 if (*listmh != '\0') {
2257 debug_print("Got manual headers: %s\n", listmh);
2258 manual_headers = procheader_entries_from_str(listmh);
2263 account = msginfo->folder->folder->account;
2266 if (!account && prefs_common.reedit_account_autosel) {
2267 gchar from[BUFFSIZE];
2268 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2269 extract_address(from);
2270 account = account_find_from_address(from, FALSE);
2274 account = cur_account;
2276 cm_return_val_if_fail(account != NULL, NULL);
2278 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2280 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2281 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2282 compose->autowrap = autowrap;
2283 compose->replyinfo = replyinfo;
2284 compose->fwdinfo = fwdinfo;
2286 compose->updating = TRUE;
2287 compose->priority = priority;
2289 if (privacy_system != NULL) {
2290 compose->privacy_system = privacy_system;
2291 compose_use_signing(compose, use_signing);
2292 compose_use_encryption(compose, use_encryption);
2293 compose_update_privacy_system_menu_item(compose, FALSE);
2295 activate_privacy_system(compose, account, FALSE);
2298 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2300 compose_extract_original_charset(compose);
2302 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2303 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2304 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2305 gchar queueheader_buf[BUFFSIZE];
2307 /* Set message save folder */
2308 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2309 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2310 compose_set_save_to(compose, &queueheader_buf[4]);
2312 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2313 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2315 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2320 if (compose_parse_header(compose, msginfo) < 0) {
2321 compose->updating = FALSE;
2322 compose_destroy(compose);
2325 compose_reedit_set_entry(compose, msginfo);
2327 textview = GTK_TEXT_VIEW(compose->text);
2328 textbuf = gtk_text_view_get_buffer(textview);
2329 compose_create_tags(textview, compose);
2331 mark = gtk_text_buffer_get_insert(textbuf);
2332 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2334 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2335 G_CALLBACK(compose_changed_cb),
2338 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2339 fp = procmime_get_first_encrypted_text_content(msginfo);
2341 compose_force_encryption(compose, account, TRUE, NULL);
2344 fp = procmime_get_first_text_content(msginfo);
2347 g_warning("Can't get text part\n");
2351 gboolean prev_autowrap = compose->autowrap;
2352 GtkTextBuffer *buffer = textbuf;
2354 while (fgets(buf, sizeof(buf), fp) != NULL) {
2356 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2362 compose_attach_parts(compose, msginfo);
2364 compose_colorize_signature(compose);
2366 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2367 G_CALLBACK(compose_changed_cb),
2370 if (manual_headers != NULL) {
2371 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2372 procheader_entries_free(manual_headers);
2373 compose->updating = FALSE;
2374 compose_destroy(compose);
2377 procheader_entries_free(manual_headers);
2380 gtk_widget_grab_focus(compose->text);
2382 if (prefs_common.auto_exteditor) {
2383 compose_exec_ext_editor(compose);
2385 compose->modified = FALSE;
2386 compose_set_title(compose);
2388 compose->updating = FALSE;
2389 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2390 SCROLL_TO_CURSOR(compose);
2392 if (compose->deferred_destroy) {
2393 compose_destroy(compose);
2397 compose->sig_str = account_get_signature_str(compose->account);
2399 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2404 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2411 cm_return_val_if_fail(msginfo != NULL, NULL);
2414 account = account_get_reply_account(msginfo,
2415 prefs_common.reply_account_autosel);
2416 cm_return_val_if_fail(account != NULL, NULL);
2418 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2420 compose->updating = TRUE;
2422 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2423 compose->replyinfo = NULL;
2424 compose->fwdinfo = NULL;
2426 compose_show_first_last_header(compose, TRUE);
2428 gtk_widget_grab_focus(compose->header_last->entry);
2430 filename = procmsg_get_message_file(msginfo);
2432 if (filename == NULL) {
2433 compose->updating = FALSE;
2434 compose_destroy(compose);
2439 compose->redirect_filename = filename;
2441 /* Set save folder */
2442 item = msginfo->folder;
2443 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2444 gchar *folderidentifier;
2446 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2447 folderidentifier = folder_item_get_identifier(item);
2448 compose_set_save_to(compose, folderidentifier);
2449 g_free(folderidentifier);
2452 compose_attach_parts(compose, msginfo);
2454 if (msginfo->subject)
2455 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2457 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2459 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2460 _("The body of the \"Redirect\" template has an error at line %d."));
2461 quote_fmt_reset_vartable();
2462 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2464 compose_colorize_signature(compose);
2467 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2468 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2469 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2471 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2472 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2473 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2474 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2475 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2476 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2477 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2478 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2480 if (compose->toolbar->draft_btn)
2481 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2482 if (compose->toolbar->insert_btn)
2483 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2484 if (compose->toolbar->attach_btn)
2485 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2486 if (compose->toolbar->sig_btn)
2487 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2488 if (compose->toolbar->exteditor_btn)
2489 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2490 if (compose->toolbar->linewrap_current_btn)
2491 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2492 if (compose->toolbar->linewrap_all_btn)
2493 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2495 compose->modified = FALSE;
2496 compose_set_title(compose);
2497 compose->updating = FALSE;
2498 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2499 SCROLL_TO_CURSOR(compose);
2501 if (compose->deferred_destroy) {
2502 compose_destroy(compose);
2506 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2511 GList *compose_get_compose_list(void)
2513 return compose_list;
2516 void compose_entry_append(Compose *compose, const gchar *address,
2517 ComposeEntryType type, ComposePrefType pref_type)
2519 const gchar *header;
2521 gboolean in_quote = FALSE;
2522 if (!address || *address == '\0') return;
2529 header = N_("Bcc:");
2531 case COMPOSE_REPLYTO:
2532 header = N_("Reply-To:");
2534 case COMPOSE_NEWSGROUPS:
2535 header = N_("Newsgroups:");
2537 case COMPOSE_FOLLOWUPTO:
2538 header = N_( "Followup-To:");
2540 case COMPOSE_INREPLYTO:
2541 header = N_( "In-Reply-To:");
2548 header = prefs_common_translated_header_name(header);
2550 cur = begin = (gchar *)address;
2552 /* we separate the line by commas, but not if we're inside a quoted
2554 while (*cur != '\0') {
2556 in_quote = !in_quote;
2557 if (*cur == ',' && !in_quote) {
2558 gchar *tmp = g_strdup(begin);
2560 tmp[cur-begin]='\0';
2563 while (*tmp == ' ' || *tmp == '\t')
2565 compose_add_header_entry(compose, header, tmp, pref_type);
2572 gchar *tmp = g_strdup(begin);
2574 tmp[cur-begin]='\0';
2577 while (*tmp == ' ' || *tmp == '\t')
2579 compose_add_header_entry(compose, header, tmp, pref_type);
2584 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2586 #if !GTK_CHECK_VERSION(3, 0, 0)
2587 static GdkColor yellow;
2588 static GdkColor black;
2589 static gboolean yellow_initialised = FALSE;
2591 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2592 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2597 #if !GTK_CHECK_VERSION(3, 0, 0)
2598 if (!yellow_initialised) {
2599 gdk_color_parse("#f5f6be", &yellow);
2600 gdk_color_parse("#000000", &black);
2601 yellow_initialised = gdk_colormap_alloc_color(
2602 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2603 yellow_initialised &= gdk_colormap_alloc_color(
2604 gdk_colormap_get_system(), &black, FALSE, TRUE);
2608 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2609 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2610 if (gtk_entry_get_text(entry) &&
2611 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2612 #if !GTK_CHECK_VERSION(3, 0, 0)
2613 if (yellow_initialised) {
2615 gtk_widget_modify_base(
2616 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2617 GTK_STATE_NORMAL, &yellow);
2618 gtk_widget_modify_text(
2619 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2620 GTK_STATE_NORMAL, &black);
2621 #if !GTK_CHECK_VERSION(3, 0, 0)
2628 void compose_toolbar_cb(gint action, gpointer data)
2630 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2631 Compose *compose = (Compose*)toolbar_item->parent;
2633 cm_return_if_fail(compose != NULL);
2637 compose_send_cb(NULL, compose);
2640 compose_send_later_cb(NULL, compose);
2643 compose_draft(compose, COMPOSE_QUIT_EDITING);
2646 compose_insert_file_cb(NULL, compose);
2649 compose_attach_cb(NULL, compose);
2652 compose_insert_sig(compose, FALSE);
2655 compose_ext_editor_cb(NULL, compose);
2657 case A_LINEWRAP_CURRENT:
2658 compose_beautify_paragraph(compose, NULL, TRUE);
2660 case A_LINEWRAP_ALL:
2661 compose_wrap_all_full(compose, TRUE);
2664 compose_address_cb(NULL, compose);
2667 case A_CHECK_SPELLING:
2668 compose_check_all(NULL, compose);
2676 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2681 gchar *subject = NULL;
2685 gchar **attach = NULL;
2686 gchar *inreplyto = NULL;
2687 MailField mfield = NO_FIELD_PRESENT;
2689 /* get mailto parts but skip from */
2690 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2693 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2694 mfield = TO_FIELD_PRESENT;
2697 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2699 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2701 if (!g_utf8_validate (subject, -1, NULL)) {
2702 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2703 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2706 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2708 mfield = SUBJECT_FIELD_PRESENT;
2711 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2712 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2715 gboolean prev_autowrap = compose->autowrap;
2717 compose->autowrap = FALSE;
2719 mark = gtk_text_buffer_get_insert(buffer);
2720 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2722 if (!g_utf8_validate (body, -1, NULL)) {
2723 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2724 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2727 gtk_text_buffer_insert(buffer, &iter, body, -1);
2729 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2731 compose->autowrap = prev_autowrap;
2732 if (compose->autowrap)
2733 compose_wrap_all(compose);
2734 mfield = BODY_FIELD_PRESENT;
2738 gint i = 0, att = 0;
2739 gchar *warn_files = NULL;
2740 while (attach[i] != NULL) {
2741 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2742 if (utf8_filename) {
2743 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2744 gchar *tmp = g_strdup_printf("%s%s\n",
2745 warn_files?warn_files:"",
2751 g_free(utf8_filename);
2753 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2758 alertpanel_notice(ngettext(
2759 "The following file has been attached: \n%s",
2760 "The following files have been attached: \n%s", att), warn_files);
2765 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2778 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2780 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2781 {"Cc:", NULL, TRUE},
2782 {"References:", NULL, FALSE},
2783 {"Bcc:", NULL, TRUE},
2784 {"Newsgroups:", NULL, TRUE},
2785 {"Followup-To:", NULL, TRUE},
2786 {"List-Post:", NULL, FALSE},
2787 {"X-Priority:", NULL, FALSE},
2788 {NULL, NULL, FALSE}};
2804 cm_return_val_if_fail(msginfo != NULL, -1);
2806 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2807 procheader_get_header_fields(fp, hentry);
2810 if (hentry[H_REPLY_TO].body != NULL) {
2811 if (hentry[H_REPLY_TO].body[0] != '\0') {
2813 conv_unmime_header(hentry[H_REPLY_TO].body,
2816 g_free(hentry[H_REPLY_TO].body);
2817 hentry[H_REPLY_TO].body = NULL;
2819 if (hentry[H_CC].body != NULL) {
2820 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2821 g_free(hentry[H_CC].body);
2822 hentry[H_CC].body = NULL;
2824 if (hentry[H_REFERENCES].body != NULL) {
2825 if (compose->mode == COMPOSE_REEDIT)
2826 compose->references = hentry[H_REFERENCES].body;
2828 compose->references = compose_parse_references
2829 (hentry[H_REFERENCES].body, msginfo->msgid);
2830 g_free(hentry[H_REFERENCES].body);
2832 hentry[H_REFERENCES].body = NULL;
2834 if (hentry[H_BCC].body != NULL) {
2835 if (compose->mode == COMPOSE_REEDIT)
2837 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2838 g_free(hentry[H_BCC].body);
2839 hentry[H_BCC].body = NULL;
2841 if (hentry[H_NEWSGROUPS].body != NULL) {
2842 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2843 hentry[H_NEWSGROUPS].body = NULL;
2845 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2846 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2847 compose->followup_to =
2848 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2851 g_free(hentry[H_FOLLOWUP_TO].body);
2852 hentry[H_FOLLOWUP_TO].body = NULL;
2854 if (hentry[H_LIST_POST].body != NULL) {
2855 gchar *to = NULL, *start = NULL;
2857 extract_address(hentry[H_LIST_POST].body);
2858 if (hentry[H_LIST_POST].body[0] != '\0') {
2859 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2861 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2862 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2865 g_free(compose->ml_post);
2866 compose->ml_post = to;
2869 g_free(hentry[H_LIST_POST].body);
2870 hentry[H_LIST_POST].body = NULL;
2873 /* CLAWS - X-Priority */
2874 if (compose->mode == COMPOSE_REEDIT)
2875 if (hentry[H_X_PRIORITY].body != NULL) {
2878 priority = atoi(hentry[H_X_PRIORITY].body);
2879 g_free(hentry[H_X_PRIORITY].body);
2881 hentry[H_X_PRIORITY].body = NULL;
2883 if (priority < PRIORITY_HIGHEST ||
2884 priority > PRIORITY_LOWEST)
2885 priority = PRIORITY_NORMAL;
2887 compose->priority = priority;
2890 if (compose->mode == COMPOSE_REEDIT) {
2891 if (msginfo->inreplyto && *msginfo->inreplyto)
2892 compose->inreplyto = g_strdup(msginfo->inreplyto);
2896 if (msginfo->msgid && *msginfo->msgid)
2897 compose->inreplyto = g_strdup(msginfo->msgid);
2899 if (!compose->references) {
2900 if (msginfo->msgid && *msginfo->msgid) {
2901 if (msginfo->inreplyto && *msginfo->inreplyto)
2902 compose->references =
2903 g_strdup_printf("<%s>\n\t<%s>",
2907 compose->references =
2908 g_strconcat("<", msginfo->msgid, ">",
2910 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2911 compose->references =
2912 g_strconcat("<", msginfo->inreplyto, ">",
2920 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2925 cm_return_val_if_fail(msginfo != NULL, -1);
2927 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2928 procheader_get_header_fields(fp, entries);
2932 while (he != NULL && he->name != NULL) {
2934 GtkListStore *model = NULL;
2936 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
2937 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
2938 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
2939 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
2940 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
2947 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2949 GSList *ref_id_list, *cur;
2953 ref_id_list = references_list_append(NULL, ref);
2954 if (!ref_id_list) return NULL;
2955 if (msgid && *msgid)
2956 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2961 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2962 /* "<" + Message-ID + ">" + CR+LF+TAB */
2963 len += strlen((gchar *)cur->data) + 5;
2965 if (len > MAX_REFERENCES_LEN) {
2966 /* remove second message-ID */
2967 if (ref_id_list && ref_id_list->next &&
2968 ref_id_list->next->next) {
2969 g_free(ref_id_list->next->data);
2970 ref_id_list = g_slist_remove
2971 (ref_id_list, ref_id_list->next->data);
2973 slist_free_strings(ref_id_list);
2974 g_slist_free(ref_id_list);
2981 new_ref = g_string_new("");
2982 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2983 if (new_ref->len > 0)
2984 g_string_append(new_ref, "\n\t");
2985 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2988 slist_free_strings(ref_id_list);
2989 g_slist_free(ref_id_list);
2991 new_ref_str = new_ref->str;
2992 g_string_free(new_ref, FALSE);
2997 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2998 const gchar *fmt, const gchar *qmark,
2999 const gchar *body, gboolean rewrap,
3000 gboolean need_unescape,
3001 const gchar *err_msg)
3003 MsgInfo* dummyinfo = NULL;
3004 gchar *quote_str = NULL;
3006 gboolean prev_autowrap;
3007 const gchar *trimmed_body = body;
3008 gint cursor_pos = -1;
3009 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3010 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3015 SIGNAL_BLOCK(buffer);
3018 dummyinfo = compose_msginfo_new_from_compose(compose);
3019 msginfo = dummyinfo;
3022 if (qmark != NULL) {
3024 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3025 compose->gtkaspell);
3027 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3029 quote_fmt_scan_string(qmark);
3032 buf = quote_fmt_get_buffer();
3034 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3036 Xstrdup_a(quote_str, buf, goto error)
3039 if (fmt && *fmt != '\0') {
3042 while (*trimmed_body == '\n')
3046 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3047 compose->gtkaspell);
3049 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3051 if (need_unescape) {
3054 /* decode \-escape sequences in the internal representation of the quote format */
3055 tmp = g_malloc(strlen(fmt)+1);
3056 pref_get_unescaped_pref(tmp, fmt);
3057 quote_fmt_scan_string(tmp);
3061 quote_fmt_scan_string(fmt);
3065 buf = quote_fmt_get_buffer();
3067 gint line = quote_fmt_get_line();
3068 alertpanel_error(err_msg, line);
3074 prev_autowrap = compose->autowrap;
3075 compose->autowrap = FALSE;
3077 mark = gtk_text_buffer_get_insert(buffer);
3078 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3079 if (g_utf8_validate(buf, -1, NULL)) {
3080 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3082 gchar *tmpout = NULL;
3083 tmpout = conv_codeset_strdup
3084 (buf, conv_get_locale_charset_str_no_utf8(),
3086 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3088 tmpout = g_malloc(strlen(buf)*2+1);
3089 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3091 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3095 cursor_pos = quote_fmt_get_cursor_pos();
3096 if (cursor_pos == -1)
3097 cursor_pos = gtk_text_iter_get_offset(&iter);
3098 compose->set_cursor_pos = cursor_pos;
3100 gtk_text_buffer_get_start_iter(buffer, &iter);
3101 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3102 gtk_text_buffer_place_cursor(buffer, &iter);
3104 compose->autowrap = prev_autowrap;
3105 if (compose->autowrap && rewrap)
3106 compose_wrap_all(compose);
3113 SIGNAL_UNBLOCK(buffer);
3115 procmsg_msginfo_free( dummyinfo );
3120 /* if ml_post is of type addr@host and from is of type
3121 * addr-anything@host, return TRUE
3123 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3125 gchar *left_ml = NULL;
3126 gchar *right_ml = NULL;
3127 gchar *left_from = NULL;
3128 gchar *right_from = NULL;
3129 gboolean result = FALSE;
3131 if (!ml_post || !from)
3134 left_ml = g_strdup(ml_post);
3135 if (strstr(left_ml, "@")) {
3136 right_ml = strstr(left_ml, "@")+1;
3137 *(strstr(left_ml, "@")) = '\0';
3140 left_from = g_strdup(from);
3141 if (strstr(left_from, "@")) {
3142 right_from = strstr(left_from, "@")+1;
3143 *(strstr(left_from, "@")) = '\0';
3146 if (left_ml && left_from && right_ml && right_from
3147 && !strncmp(left_from, left_ml, strlen(left_ml))
3148 && !strcmp(right_from, right_ml)) {
3157 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3158 gboolean respect_default_to)
3162 if (!folder || !folder->prefs)
3165 if (respect_default_to && folder->prefs->enable_default_to) {
3166 compose_entry_append(compose, folder->prefs->default_to,
3167 COMPOSE_TO, PREF_FOLDER);
3168 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3170 if (folder->prefs->enable_default_cc)
3171 compose_entry_append(compose, folder->prefs->default_cc,
3172 COMPOSE_CC, PREF_FOLDER);
3173 if (folder->prefs->enable_default_bcc)
3174 compose_entry_append(compose, folder->prefs->default_bcc,
3175 COMPOSE_BCC, PREF_FOLDER);
3176 if (folder->prefs->enable_default_replyto)
3177 compose_entry_append(compose, folder->prefs->default_replyto,
3178 COMPOSE_REPLYTO, PREF_FOLDER);
3181 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3186 if (!compose || !msginfo)
3189 if (msginfo->subject && *msginfo->subject) {
3190 buf = p = g_strdup(msginfo->subject);
3191 p += subject_get_prefix_length(p);
3192 memmove(buf, p, strlen(p) + 1);
3194 buf2 = g_strdup_printf("Re: %s", buf);
3195 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3200 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3203 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3204 gboolean to_all, gboolean to_ml,
3206 gboolean followup_and_reply_to)
3208 GSList *cc_list = NULL;
3211 gchar *replyto = NULL;
3212 gchar *ac_email = NULL;
3214 gboolean reply_to_ml = FALSE;
3215 gboolean default_reply_to = FALSE;
3217 cm_return_if_fail(compose->account != NULL);
3218 cm_return_if_fail(msginfo != NULL);
3220 reply_to_ml = to_ml && compose->ml_post;
3222 default_reply_to = msginfo->folder &&
3223 msginfo->folder->prefs->enable_default_reply_to;
3225 if (compose->account->protocol != A_NNTP) {
3226 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3228 if (reply_to_ml && !default_reply_to) {
3230 gboolean is_subscr = is_subscription(compose->ml_post,
3233 /* normal answer to ml post with a reply-to */
3234 compose_entry_append(compose,
3236 COMPOSE_TO, PREF_ML);
3237 if (compose->replyto)
3238 compose_entry_append(compose,
3240 COMPOSE_CC, PREF_ML);
3242 /* answer to subscription confirmation */
3243 if (compose->replyto)
3244 compose_entry_append(compose,
3246 COMPOSE_TO, PREF_ML);
3247 else if (msginfo->from)
3248 compose_entry_append(compose,
3250 COMPOSE_TO, PREF_ML);
3253 else if (!(to_all || to_sender) && default_reply_to) {
3254 compose_entry_append(compose,
3255 msginfo->folder->prefs->default_reply_to,
3256 COMPOSE_TO, PREF_FOLDER);
3257 compose_entry_mark_default_to(compose,
3258 msginfo->folder->prefs->default_reply_to);
3263 Xstrdup_a(tmp1, msginfo->from, return);
3264 extract_address(tmp1);
3265 if (to_all || to_sender ||
3266 !account_find_from_address(tmp1, FALSE))
3267 compose_entry_append(compose,
3268 (compose->replyto && !to_sender)
3269 ? compose->replyto :
3270 msginfo->from ? msginfo->from : "",
3271 COMPOSE_TO, PREF_NONE);
3272 else if (!to_all && !to_sender) {
3273 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3274 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3275 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3276 if (compose->replyto) {
3277 compose_entry_append(compose,
3279 COMPOSE_TO, PREF_NONE);
3281 compose_entry_append(compose,
3282 msginfo->from ? msginfo->from : "",
3283 COMPOSE_TO, PREF_NONE);
3286 /* replying to own mail, use original recp */
3287 compose_entry_append(compose,
3288 msginfo->to ? msginfo->to : "",
3289 COMPOSE_TO, PREF_NONE);
3290 compose_entry_append(compose,
3291 msginfo->cc ? msginfo->cc : "",
3292 COMPOSE_CC, PREF_NONE);
3297 if (to_sender || (compose->followup_to &&
3298 !strncmp(compose->followup_to, "poster", 6)))
3299 compose_entry_append
3301 (compose->replyto ? compose->replyto :
3302 msginfo->from ? msginfo->from : ""),
3303 COMPOSE_TO, PREF_NONE);
3305 else if (followup_and_reply_to || to_all) {
3306 compose_entry_append
3308 (compose->replyto ? compose->replyto :
3309 msginfo->from ? msginfo->from : ""),
3310 COMPOSE_TO, PREF_NONE);
3312 compose_entry_append
3314 compose->followup_to ? compose->followup_to :
3315 compose->newsgroups ? compose->newsgroups : "",
3316 COMPOSE_NEWSGROUPS, PREF_NONE);
3319 compose_entry_append
3321 compose->followup_to ? compose->followup_to :
3322 compose->newsgroups ? compose->newsgroups : "",
3323 COMPOSE_NEWSGROUPS, PREF_NONE);
3325 compose_reply_set_subject(compose, msginfo);
3327 if (to_ml && compose->ml_post) return;
3328 if (!to_all || compose->account->protocol == A_NNTP) return;
3330 if (compose->replyto) {
3331 Xstrdup_a(replyto, compose->replyto, return);
3332 extract_address(replyto);
3334 if (msginfo->from) {
3335 Xstrdup_a(from, msginfo->from, return);
3336 extract_address(from);
3339 if (replyto && from)
3340 cc_list = address_list_append_with_comments(cc_list, from);
3341 if (to_all && msginfo->folder &&
3342 msginfo->folder->prefs->enable_default_reply_to)
3343 cc_list = address_list_append_with_comments(cc_list,
3344 msginfo->folder->prefs->default_reply_to);
3345 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3346 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3348 ac_email = g_utf8_strdown(compose->account->address, -1);
3351 for (cur = cc_list; cur != NULL; cur = cur->next) {
3352 gchar *addr = g_utf8_strdown(cur->data, -1);
3353 extract_address(addr);
3355 if (strcmp(ac_email, addr))
3356 compose_entry_append(compose, (gchar *)cur->data,
3357 COMPOSE_CC, PREF_NONE);
3359 debug_print("Cc address same as compose account's, ignoring\n");
3364 slist_free_strings(cc_list);
3365 g_slist_free(cc_list);
3371 #define SET_ENTRY(entry, str) \
3374 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3377 #define SET_ADDRESS(type, str) \
3380 compose_entry_append(compose, str, type, PREF_NONE); \
3383 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3385 cm_return_if_fail(msginfo != NULL);
3387 SET_ENTRY(subject_entry, msginfo->subject);
3388 SET_ENTRY(from_name, msginfo->from);
3389 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3390 SET_ADDRESS(COMPOSE_CC, compose->cc);
3391 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3392 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3393 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3394 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3396 compose_update_priority_menu_item(compose);
3397 compose_update_privacy_system_menu_item(compose, FALSE);
3398 compose_show_first_last_header(compose, TRUE);
3404 static void compose_insert_sig(Compose *compose, gboolean replace)
3406 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3407 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3409 GtkTextIter iter, iter_end;
3410 gint cur_pos, ins_pos;
3411 gboolean prev_autowrap;
3412 gboolean found = FALSE;
3413 gboolean exists = FALSE;
3415 cm_return_if_fail(compose->account != NULL);
3419 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3420 G_CALLBACK(compose_changed_cb),
3423 mark = gtk_text_buffer_get_insert(buffer);
3424 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3425 cur_pos = gtk_text_iter_get_offset (&iter);
3428 gtk_text_buffer_get_end_iter(buffer, &iter);
3430 exists = (compose->sig_str != NULL);
3433 GtkTextIter first_iter, start_iter, end_iter;
3435 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3437 if (!exists || compose->sig_str[0] == '\0')
3440 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3441 compose->signature_tag);
3444 /* include previous \n\n */
3445 gtk_text_iter_backward_chars(&first_iter, 1);
3446 start_iter = first_iter;
3447 end_iter = first_iter;
3449 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3450 compose->signature_tag);
3451 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3452 compose->signature_tag);
3454 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3460 g_free(compose->sig_str);
3461 compose->sig_str = account_get_signature_str(compose->account);
3463 cur_pos = gtk_text_iter_get_offset(&iter);
3465 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3466 g_free(compose->sig_str);
3467 compose->sig_str = NULL;
3469 if (compose->sig_inserted == FALSE)
3470 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3471 compose->sig_inserted = TRUE;
3473 cur_pos = gtk_text_iter_get_offset(&iter);
3474 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3476 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3477 gtk_text_iter_forward_chars(&iter, 1);
3478 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3479 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3481 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3482 cur_pos = gtk_text_buffer_get_char_count (buffer);
3485 /* put the cursor where it should be
3486 * either where the quote_fmt says, either where it was */
3487 if (compose->set_cursor_pos < 0)
3488 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3490 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3491 compose->set_cursor_pos);
3493 compose->set_cursor_pos = -1;
3494 gtk_text_buffer_place_cursor(buffer, &iter);
3495 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3496 G_CALLBACK(compose_changed_cb),
3502 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3505 GtkTextBuffer *buffer;
3508 const gchar *cur_encoding;
3509 gchar buf[BUFFSIZE];
3512 gboolean prev_autowrap;
3513 gboolean badtxt = FALSE;
3514 struct stat file_stat;
3517 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3519 /* get the size of the file we are about to insert */
3520 ret = g_stat(file, &file_stat);
3522 gchar *shortfile = g_path_get_basename(file);
3523 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3525 return COMPOSE_INSERT_NO_FILE;
3526 } else if (prefs_common.warn_large_insert == TRUE) {
3528 /* ask user for confirmation if the file is large */
3529 if (prefs_common.warn_large_insert_size < 0 ||
3530 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3534 msg = g_strdup_printf(_("You are about to insert a file of %s "
3535 "in the message body. Are you sure you want to do that?"),
3536 to_human_readable(file_stat.st_size));
3537 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3538 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3541 /* do we ask for confirmation next time? */
3542 if (aval & G_ALERTDISABLE) {
3543 /* no confirmation next time, disable feature in preferences */
3544 aval &= ~G_ALERTDISABLE;
3545 prefs_common.warn_large_insert = FALSE;
3548 /* abort file insertion if user canceled action */
3549 if (aval != G_ALERTALTERNATE) {
3550 return COMPOSE_INSERT_NO_FILE;
3556 if ((fp = g_fopen(file, "rb")) == NULL) {
3557 FILE_OP_ERROR(file, "fopen");
3558 return COMPOSE_INSERT_READ_ERROR;
3561 prev_autowrap = compose->autowrap;
3562 compose->autowrap = FALSE;
3564 text = GTK_TEXT_VIEW(compose->text);
3565 buffer = gtk_text_view_get_buffer(text);
3566 mark = gtk_text_buffer_get_insert(buffer);
3567 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3569 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3570 G_CALLBACK(text_inserted),
3573 cur_encoding = conv_get_locale_charset_str_no_utf8();
3575 while (fgets(buf, sizeof(buf), fp) != NULL) {
3578 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3579 str = g_strdup(buf);
3581 str = conv_codeset_strdup
3582 (buf, cur_encoding, CS_INTERNAL);
3585 /* strip <CR> if DOS/Windows file,
3586 replace <CR> with <LF> if Macintosh file. */
3589 if (len > 0 && str[len - 1] != '\n') {
3591 if (str[len] == '\r') str[len] = '\n';
3594 gtk_text_buffer_insert(buffer, &iter, str, -1);
3598 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3599 G_CALLBACK(text_inserted),
3601 compose->autowrap = prev_autowrap;
3602 if (compose->autowrap)
3603 compose_wrap_all(compose);
3608 return COMPOSE_INSERT_INVALID_CHARACTER;
3610 return COMPOSE_INSERT_SUCCESS;
3613 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3614 const gchar *filename,
3615 const gchar *content_type,
3616 const gchar *charset)
3624 GtkListStore *store;
3626 gboolean has_binary = FALSE;
3628 if (!is_file_exist(file)) {
3629 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3630 gboolean result = FALSE;
3631 if (file_from_uri && is_file_exist(file_from_uri)) {
3632 result = compose_attach_append(
3633 compose, file_from_uri,
3634 filename, content_type,
3637 g_free(file_from_uri);
3640 alertpanel_error("File %s doesn't exist\n", filename);
3643 if ((size = get_file_size(file)) < 0) {
3644 alertpanel_error("Can't get file size of %s\n", filename);
3648 alertpanel_error(_("File %s is empty."), filename);
3651 if ((fp = g_fopen(file, "rb")) == NULL) {
3652 alertpanel_error(_("Can't read %s."), filename);
3657 ainfo = g_new0(AttachInfo, 1);
3658 auto_ainfo = g_auto_pointer_new_with_free
3659 (ainfo, (GFreeFunc) compose_attach_info_free);
3660 ainfo->file = g_strdup(file);
3663 ainfo->content_type = g_strdup(content_type);
3664 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3666 MsgFlags flags = {0, 0};
3668 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3669 ainfo->encoding = ENC_7BIT;