2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2013 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 "folder_item_prefs.h"
98 #include "addr_compl.h"
99 #include "quote_fmt.h"
101 #include "foldersel.h"
104 #include "message_search.h"
105 #include "combobox.h"
109 #include "autofaces.h"
110 #include "spell_entry.h"
123 #define N_ATTACH_COLS (N_COL_COLUMNS)
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
128 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
130 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
132 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
134 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
135 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
136 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
137 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
138 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
139 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
140 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
141 } ComposeCallAdvancedAction;
145 PRIORITY_HIGHEST = 1,
154 COMPOSE_INSERT_SUCCESS,
155 COMPOSE_INSERT_READ_ERROR,
156 COMPOSE_INSERT_INVALID_CHARACTER,
157 COMPOSE_INSERT_NO_FILE
158 } ComposeInsertResult;
162 COMPOSE_WRITE_FOR_SEND,
163 COMPOSE_WRITE_FOR_STORE
168 COMPOSE_QUOTE_FORCED,
175 SUBJECT_FIELD_PRESENT,
180 #define B64_LINE_SIZE 57
181 #define B64_BUFFSIZE 77
183 #define MAX_REFERENCES_LEN 999
185 static GList *compose_list = NULL;
186 static GSList *extra_headers = NULL;
188 static Compose *compose_generic_new (PrefsAccount *account,
192 GList *listAddress );
194 static Compose *compose_create (PrefsAccount *account,
199 static void compose_entry_mark_default_to (Compose *compose,
200 const gchar *address);
201 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
202 ComposeQuoteMode quote_mode,
206 static Compose *compose_forward_multiple (PrefsAccount *account,
207 GSList *msginfo_list);
208 static Compose *compose_reply (MsgInfo *msginfo,
209 ComposeQuoteMode quote_mode,
214 static Compose *compose_reply_mode (ComposeMode mode,
215 GSList *msginfo_list,
217 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
218 static void compose_update_privacy_systems_menu(Compose *compose);
220 static GtkWidget *compose_account_option_menu_create
222 static void compose_set_out_encoding (Compose *compose);
223 static void compose_set_template_menu (Compose *compose);
224 static void compose_destroy (Compose *compose);
226 static MailField compose_entries_set (Compose *compose,
228 ComposeEntryType to_type);
229 static gint compose_parse_header (Compose *compose,
231 static gint compose_parse_manual_headers (Compose *compose,
233 HeaderEntry *entries);
234 static gchar *compose_parse_references (const gchar *ref,
237 static gchar *compose_quote_fmt (Compose *compose,
243 gboolean need_unescape,
244 const gchar *err_msg);
246 static void compose_reply_set_entry (Compose *compose,
252 followup_and_reply_to);
253 static void compose_reedit_set_entry (Compose *compose,
256 static void compose_insert_sig (Compose *compose,
258 static ComposeInsertResult compose_insert_file (Compose *compose,
261 static gboolean compose_attach_append (Compose *compose,
264 const gchar *content_type,
265 const gchar *charset);
266 static void compose_attach_parts (Compose *compose,
269 static gboolean compose_beautify_paragraph (Compose *compose,
270 GtkTextIter *par_iter,
272 static void compose_wrap_all (Compose *compose);
273 static void compose_wrap_all_full (Compose *compose,
276 static void compose_set_title (Compose *compose);
277 static void compose_select_account (Compose *compose,
278 PrefsAccount *account,
281 static PrefsAccount *compose_current_mail_account(void);
282 /* static gint compose_send (Compose *compose); */
283 static gboolean compose_check_for_valid_recipient
285 static gboolean compose_check_entries (Compose *compose,
286 gboolean check_everything);
287 static gint compose_write_to_file (Compose *compose,
290 gboolean attach_parts);
291 static gint compose_write_body_to_file (Compose *compose,
293 static gint compose_remove_reedit_target (Compose *compose,
295 static void compose_remove_draft (Compose *compose);
296 static gint compose_queue_sub (Compose *compose,
300 gboolean check_subject,
301 gboolean remove_reedit_target);
302 static int compose_add_attachments (Compose *compose,
304 static gchar *compose_get_header (Compose *compose);
305 static gchar *compose_get_manual_headers_info (Compose *compose);
307 static void compose_convert_header (Compose *compose,
312 gboolean addr_field);
314 static void compose_attach_info_free (AttachInfo *ainfo);
315 static void compose_attach_remove_selected (GtkAction *action,
318 static void compose_template_apply (Compose *compose,
321 static void compose_attach_property (GtkAction *action,
323 static void compose_attach_property_create (gboolean *cancelled);
324 static void attach_property_ok (GtkWidget *widget,
325 gboolean *cancelled);
326 static void attach_property_cancel (GtkWidget *widget,
327 gboolean *cancelled);
328 static gint attach_property_delete_event (GtkWidget *widget,
330 gboolean *cancelled);
331 static gboolean attach_property_key_pressed (GtkWidget *widget,
333 gboolean *cancelled);
335 static void compose_exec_ext_editor (Compose *compose);
337 static gint compose_exec_ext_editor_real (const gchar *file);
338 static gboolean compose_ext_editor_kill (Compose *compose);
339 static gboolean compose_input_cb (GIOChannel *source,
340 GIOCondition condition,
342 static void compose_set_ext_editor_sensitive (Compose *compose,
344 #endif /* G_OS_UNIX */
346 static void compose_undo_state_changed (UndoMain *undostruct,
351 static void compose_create_header_entry (Compose *compose);
352 static void compose_add_header_entry (Compose *compose, const gchar *header,
353 gchar *text, ComposePrefType pref_type);
354 static void compose_remove_header_entries(Compose *compose);
356 static void compose_update_priority_menu_item(Compose * compose);
358 static void compose_spell_menu_changed (void *data);
359 static void compose_dict_changed (void *data);
361 static void compose_add_field_list ( Compose *compose,
362 GList *listAddress );
364 /* callback functions */
366 static void compose_notebook_size_alloc (GtkNotebook *notebook,
367 GtkAllocation *allocation,
369 static gboolean compose_edit_size_alloc (GtkEditable *widget,
370 GtkAllocation *allocation,
371 GtkSHRuler *shruler);
372 static void account_activated (GtkComboBox *optmenu,
374 static void attach_selected (GtkTreeView *tree_view,
375 GtkTreePath *tree_path,
376 GtkTreeViewColumn *column,
378 static gboolean attach_button_pressed (GtkWidget *widget,
379 GdkEventButton *event,
381 static gboolean attach_key_pressed (GtkWidget *widget,
384 static void compose_send_cb (GtkAction *action, gpointer data);
385 static void compose_send_later_cb (GtkAction *action, gpointer data);
387 static void compose_save_cb (GtkAction *action,
390 static void compose_attach_cb (GtkAction *action,
392 static void compose_insert_file_cb (GtkAction *action,
394 static void compose_insert_sig_cb (GtkAction *action,
396 static void compose_replace_sig_cb (GtkAction *action,
399 static void compose_close_cb (GtkAction *action,
401 static void compose_print_cb (GtkAction *action,
404 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
406 static void compose_address_cb (GtkAction *action,
408 static void about_show_cb (GtkAction *action,
410 static void compose_template_activate_cb(GtkWidget *widget,
413 static void compose_ext_editor_cb (GtkAction *action,
416 static gint compose_delete_cb (GtkWidget *widget,
420 static void compose_undo_cb (GtkAction *action,
422 static void compose_redo_cb (GtkAction *action,
424 static void compose_cut_cb (GtkAction *action,
426 static void compose_copy_cb (GtkAction *action,
428 static void compose_paste_cb (GtkAction *action,
430 static void compose_paste_as_quote_cb (GtkAction *action,
432 static void compose_paste_no_wrap_cb (GtkAction *action,
434 static void compose_paste_wrap_cb (GtkAction *action,
436 static void compose_allsel_cb (GtkAction *action,
439 static void compose_advanced_action_cb (GtkAction *action,
442 static void compose_grab_focus_cb (GtkWidget *widget,
445 static void compose_changed_cb (GtkTextBuffer *textbuf,
448 static void compose_wrap_cb (GtkAction *action,
450 static void compose_wrap_all_cb (GtkAction *action,
452 static void compose_find_cb (GtkAction *action,
454 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
456 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
459 static void compose_toggle_ruler_cb (GtkToggleAction *action,
461 static void compose_toggle_sign_cb (GtkToggleAction *action,
463 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
465 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
466 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
467 static void activate_privacy_system (Compose *compose,
468 PrefsAccount *account,
470 static void compose_use_signing(Compose *compose, gboolean use_signing);
471 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
472 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
474 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
476 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
477 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
478 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
480 static void compose_attach_drag_received_cb (GtkWidget *widget,
481 GdkDragContext *drag_context,
484 GtkSelectionData *data,
488 static void compose_insert_drag_received_cb (GtkWidget *widget,
489 GdkDragContext *drag_context,
492 GtkSelectionData *data,
496 static void compose_header_drag_received_cb (GtkWidget *widget,
497 GdkDragContext *drag_context,
500 GtkSelectionData *data,
505 static gboolean compose_drag_drop (GtkWidget *widget,
506 GdkDragContext *drag_context,
508 guint time, gpointer user_data);
509 static gboolean completion_set_focus_to_subject
514 static void text_inserted (GtkTextBuffer *buffer,
519 static Compose *compose_generic_reply(MsgInfo *msginfo,
520 ComposeQuoteMode quote_mode,
524 gboolean followup_and_reply_to,
527 static void compose_headerentry_changed_cb (GtkWidget *entry,
528 ComposeHeaderEntry *headerentry);
529 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
531 ComposeHeaderEntry *headerentry);
532 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
533 ComposeHeaderEntry *headerentry);
535 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
537 static void compose_allow_user_actions (Compose *compose, gboolean allow);
539 static void compose_nothing_cb (GtkAction *action, gpointer data)
545 static void compose_check_all (GtkAction *action, gpointer data);
546 static void compose_highlight_all (GtkAction *action, gpointer data);
547 static void compose_check_backwards (GtkAction *action, gpointer data);
548 static void compose_check_forwards_go (GtkAction *action, gpointer data);
551 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
553 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
556 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
557 FolderItem *folder_item);
559 static void compose_attach_update_label(Compose *compose);
560 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
561 gboolean respect_default_to);
562 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
564 static GtkActionEntry compose_popup_entries[] =
566 {"Compose", NULL, "Compose" },
567 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
568 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
569 {"Compose/---", NULL, "---", NULL, NULL, NULL },
570 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
573 static GtkActionEntry compose_entries[] =
575 {"Menu", NULL, "Menu" },
577 {"Message", NULL, N_("_Message") },
578 {"Edit", NULL, N_("_Edit") },
580 {"Spelling", NULL, N_("_Spelling") },
582 {"Options", NULL, N_("_Options") },
583 {"Tools", NULL, N_("_Tools") },
584 {"Help", NULL, N_("_Help") },
586 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
587 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
588 {"Message/---", NULL, "---" },
590 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
591 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
592 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
593 {"Message/ReplaceSig", NULL, N_("_Replace signature"), NULL, NULL, G_CALLBACK(compose_replace_sig_cb) },
594 /* {"Message/---", NULL, "---" }, */
595 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
596 /* {"Message/---", NULL, "---" }, */
597 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
598 /* {"Message/---", NULL, "---" }, */
599 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
602 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
603 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
604 {"Edit/---", NULL, "---" },
606 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
607 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
608 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
610 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
611 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
612 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
613 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
615 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
617 {"Edit/Advanced", NULL, N_("A_dvanced") },
618 {"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*/
619 {"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*/
620 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
621 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
622 {"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*/
623 {"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*/
624 {"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*/
625 {"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*/
626 {"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*/
627 {"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*/
628 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
629 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
630 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
631 {"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*/
633 /* {"Edit/---", NULL, "---" }, */
634 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
636 /* {"Edit/---", NULL, "---" }, */
637 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
638 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
639 /* {"Edit/---", NULL, "---" }, */
640 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
643 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
644 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
645 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
646 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
648 {"Spelling/---", NULL, "---" },
649 {"Spelling/Options", NULL, N_("_Options") },
654 {"Options/ReplyMode", NULL, N_("Reply _mode") },
655 {"Options/---", NULL, "---" },
656 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
657 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
659 /* {"Options/---", NULL, "---" }, */
661 {"Options/Priority", NULL, N_("_Priority") },
663 {"Options/Encoding", NULL, N_("Character _encoding") },
664 {"Options/Encoding/---", NULL, "---" },
665 #define ENC_ACTION(cs_char,c_char,string) \
666 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
668 {"Options/Encoding/Western", NULL, N_("Western European") },
669 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
670 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
671 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
672 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
673 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
674 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
675 {"Options/Encoding/Korean", NULL, N_("Korean") },
676 {"Options/Encoding/Thai", NULL, N_("Thai") },
679 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
681 {"Tools/Template", NULL, N_("_Template") },
682 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
683 {"Tools/Actions", NULL, N_("Actio_ns") },
684 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
687 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
690 static GtkToggleActionEntry compose_toggle_entries[] =
692 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
693 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
694 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
695 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
696 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
697 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
698 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
701 static GtkRadioActionEntry compose_radio_rm_entries[] =
703 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
704 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
705 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
706 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
709 static GtkRadioActionEntry compose_radio_prio_entries[] =
711 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
712 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
713 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
714 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
715 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
718 static GtkRadioActionEntry compose_radio_enc_entries[] =
720 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
729 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
730 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
731 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
732 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
733 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
734 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
735 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
736 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
737 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
738 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
739 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
740 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
741 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
742 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
743 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
744 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
745 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
746 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
747 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
748 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
749 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
750 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
751 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
754 static GtkTargetEntry compose_mime_types[] =
756 {"text/uri-list", 0, 0},
757 {"UTF8_STRING", 0, 0},
761 static gboolean compose_put_existing_to_front(MsgInfo *info)
763 GList *compose_list = compose_get_compose_list();
767 for (elem = compose_list; elem != NULL && elem->data != NULL;
769 Compose *c = (Compose*)elem->data;
771 if (!c->targetinfo || !c->targetinfo->msgid ||
775 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
776 gtkut_window_popup(c->window);
784 static GdkColor quote_color1 =
785 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
786 static GdkColor quote_color2 =
787 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
788 static GdkColor quote_color3 =
789 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
791 static GdkColor quote_bgcolor1 =
792 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
793 static GdkColor quote_bgcolor2 =
794 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
795 static GdkColor quote_bgcolor3 =
796 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
798 static GdkColor signature_color = {
805 static GdkColor uri_color = {
812 static void compose_create_tags(GtkTextView *text, Compose *compose)
814 GtkTextBuffer *buffer;
815 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
816 #if !GTK_CHECK_VERSION(2, 24, 0)
823 buffer = gtk_text_view_get_buffer(text);
825 if (prefs_common.enable_color) {
826 /* grab the quote colors, converting from an int to a GdkColor */
827 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
829 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
831 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
833 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
835 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
837 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
839 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
841 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
844 signature_color = quote_color1 = quote_color2 = quote_color3 =
845 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
848 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
849 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
850 "foreground-gdk", "e_color1,
851 "paragraph-background-gdk", "e_bgcolor1,
853 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
854 "foreground-gdk", "e_color2,
855 "paragraph-background-gdk", "e_bgcolor2,
857 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
858 "foreground-gdk", "e_color3,
859 "paragraph-background-gdk", "e_bgcolor3,
862 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
863 "foreground-gdk", "e_color1,
865 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
866 "foreground-gdk", "e_color2,
868 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
869 "foreground-gdk", "e_color3,
873 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
874 "foreground-gdk", &signature_color,
877 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
878 "foreground-gdk", &uri_color,
880 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
881 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
883 #if !GTK_CHECK_VERSION(2, 24, 0)
884 color[0] = quote_color1;
885 color[1] = quote_color2;
886 color[2] = quote_color3;
887 color[3] = quote_bgcolor1;
888 color[4] = quote_bgcolor2;
889 color[5] = quote_bgcolor3;
890 color[6] = signature_color;
891 color[7] = uri_color;
893 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
894 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
896 for (i = 0; i < 8; i++) {
897 if (success[i] == FALSE) {
898 g_warning("Compose: color allocation failed.\n");
899 quote_color1 = quote_color2 = quote_color3 =
900 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
901 signature_color = uri_color = black;
907 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
910 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
913 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
915 return compose_generic_new(account, mailto, item, NULL, NULL);
918 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
920 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
923 #define SCROLL_TO_CURSOR(compose) { \
924 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
925 gtk_text_view_get_buffer( \
926 GTK_TEXT_VIEW(compose->text))); \
927 gtk_text_view_scroll_mark_onscreen( \
928 GTK_TEXT_VIEW(compose->text), \
932 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
935 if (folderidentifier) {
936 #if !GTK_CHECK_VERSION(2, 24, 0)
937 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
939 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
941 prefs_common.compose_save_to_history = add_history(
942 prefs_common.compose_save_to_history, folderidentifier);
943 #if !GTK_CHECK_VERSION(2, 24, 0)
944 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
945 prefs_common.compose_save_to_history);
947 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
948 prefs_common.compose_save_to_history);
952 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
953 if (folderidentifier)
954 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
956 gtk_entry_set_text(GTK_ENTRY(entry), "");
959 static gchar *compose_get_save_to(Compose *compose)
962 gchar *result = NULL;
963 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
964 result = gtk_editable_get_chars(entry, 0, -1);
967 #if !GTK_CHECK_VERSION(2, 24, 0)
968 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
970 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
972 prefs_common.compose_save_to_history = add_history(
973 prefs_common.compose_save_to_history, result);
974 #if !GTK_CHECK_VERSION(2, 24, 0)
975 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
976 prefs_common.compose_save_to_history);
978 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
979 prefs_common.compose_save_to_history);
985 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
986 GList *attach_files, GList *listAddress )
989 GtkTextView *textview;
990 GtkTextBuffer *textbuf;
992 const gchar *subject_format = NULL;
993 const gchar *body_format = NULL;
994 gchar *mailto_from = NULL;
995 PrefsAccount *mailto_account = NULL;
996 MsgInfo* dummyinfo = NULL;
997 gint cursor_pos = -1;
998 MailField mfield = NO_FIELD_PRESENT;
1002 /* check if mailto defines a from */
1003 if (mailto && *mailto != '\0') {
1004 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1005 /* mailto defines a from, check if we can get account prefs from it,
1006 if not, the account prefs will be guessed using other ways, but we'll keep
1009 mailto_account = account_find_from_address(mailto_from, TRUE);
1010 if (mailto_account == NULL) {
1012 Xstrdup_a(tmp_from, mailto_from, return NULL);
1013 extract_address(tmp_from);
1014 mailto_account = account_find_from_address(tmp_from, TRUE);
1018 account = mailto_account;
1021 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1022 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
1023 account = account_find_from_id(item->prefs->default_account);
1025 /* if no account prefs set, fallback to the current one */
1026 if (!account) account = cur_account;
1027 cm_return_val_if_fail(account != NULL, NULL);
1029 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1031 /* override from name if mailto asked for it */
1033 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1034 g_free(mailto_from);
1036 /* override from name according to folder properties */
1037 if (item && item->prefs &&
1038 item->prefs->compose_with_format &&
1039 item->prefs->compose_override_from_format &&
1040 *item->prefs->compose_override_from_format != '\0') {
1045 dummyinfo = compose_msginfo_new_from_compose(compose);
1047 /* decode \-escape sequences in the internal representation of the quote format */
1048 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1049 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1052 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1053 compose->gtkaspell);
1055 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1057 quote_fmt_scan_string(tmp);
1060 buf = quote_fmt_get_buffer();
1062 alertpanel_error(_("New message From format error."));
1064 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1065 quote_fmt_reset_vartable();
1070 compose->replyinfo = NULL;
1071 compose->fwdinfo = NULL;
1073 textview = GTK_TEXT_VIEW(compose->text);
1074 textbuf = gtk_text_view_get_buffer(textview);
1075 compose_create_tags(textview, compose);
1077 undo_block(compose->undostruct);
1079 compose_set_dictionaries_from_folder_prefs(compose, item);
1082 if (account->auto_sig)
1083 compose_insert_sig(compose, FALSE);
1084 gtk_text_buffer_get_start_iter(textbuf, &iter);
1085 gtk_text_buffer_place_cursor(textbuf, &iter);
1087 if (account->protocol != A_NNTP) {
1088 if (mailto && *mailto != '\0') {
1089 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1092 compose_set_folder_prefs(compose, item, TRUE);
1094 if (item && item->ret_rcpt) {
1095 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1098 if (mailto && *mailto != '\0') {
1099 if (!strchr(mailto, '@'))
1100 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1102 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1103 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1104 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1105 mfield = TO_FIELD_PRESENT;
1108 * CLAWS: just don't allow return receipt request, even if the user
1109 * may want to send an email. simple but foolproof.
1111 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1113 compose_add_field_list( compose, listAddress );
1115 if (item && item->prefs && item->prefs->compose_with_format) {
1116 subject_format = item->prefs->compose_subject_format;
1117 body_format = item->prefs->compose_body_format;
1118 } else if (account->compose_with_format) {
1119 subject_format = account->compose_subject_format;
1120 body_format = account->compose_body_format;
1121 } else if (prefs_common.compose_with_format) {
1122 subject_format = prefs_common.compose_subject_format;
1123 body_format = prefs_common.compose_body_format;
1126 if (subject_format || body_format) {
1129 && *subject_format != '\0' )
1131 gchar *subject = NULL;
1136 dummyinfo = compose_msginfo_new_from_compose(compose);
1138 /* decode \-escape sequences in the internal representation of the quote format */
1139 tmp = g_malloc(strlen(subject_format)+1);
1140 pref_get_unescaped_pref(tmp, subject_format);
1142 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1144 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1145 compose->gtkaspell);
1147 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1149 quote_fmt_scan_string(tmp);
1152 buf = quote_fmt_get_buffer();
1154 alertpanel_error(_("New message subject format error."));
1156 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1157 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1158 quote_fmt_reset_vartable();
1162 mfield = SUBJECT_FIELD_PRESENT;
1166 && *body_format != '\0' )
1169 GtkTextBuffer *buffer;
1170 GtkTextIter start, end;
1174 dummyinfo = compose_msginfo_new_from_compose(compose);
1176 text = GTK_TEXT_VIEW(compose->text);
1177 buffer = gtk_text_view_get_buffer(text);
1178 gtk_text_buffer_get_start_iter(buffer, &start);
1179 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1180 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1182 compose_quote_fmt(compose, dummyinfo,
1184 NULL, tmp, FALSE, TRUE,
1185 _("The body of the \"New message\" template has an error at line %d."));
1186 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1187 quote_fmt_reset_vartable();
1191 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1192 gtkaspell_highlight_all(compose->gtkaspell);
1194 mfield = BODY_FIELD_PRESENT;
1198 procmsg_msginfo_free( dummyinfo );
1204 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1205 ainfo = (AttachInfo *) curr->data;
1206 compose_attach_append(compose, ainfo->file, ainfo->name,
1207 ainfo->content_type, ainfo->charset);
1211 compose_show_first_last_header(compose, TRUE);
1213 /* Set save folder */
1214 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1215 gchar *folderidentifier;
1217 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1218 folderidentifier = folder_item_get_identifier(item);
1219 compose_set_save_to(compose, folderidentifier);
1220 g_free(folderidentifier);
1223 /* Place cursor according to provided input (mfield) */
1225 case NO_FIELD_PRESENT:
1226 if (compose->header_last)
1227 gtk_widget_grab_focus(compose->header_last->entry);
1229 case TO_FIELD_PRESENT:
1230 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1232 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1235 gtk_widget_grab_focus(compose->subject_entry);
1237 case SUBJECT_FIELD_PRESENT:
1238 textview = GTK_TEXT_VIEW(compose->text);
1241 textbuf = gtk_text_view_get_buffer(textview);
1244 mark = gtk_text_buffer_get_insert(textbuf);
1245 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1246 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1248 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1249 * only defers where it comes to the variable body
1250 * is not null. If no body is present compose->text
1251 * will be null in which case you cannot place the
1252 * cursor inside the component so. An empty component
1253 * is therefore created before placing the cursor
1255 case BODY_FIELD_PRESENT:
1256 cursor_pos = quote_fmt_get_cursor_pos();
1257 if (cursor_pos == -1)
1258 gtk_widget_grab_focus(compose->header_last->entry);
1260 gtk_widget_grab_focus(compose->text);
1264 undo_unblock(compose->undostruct);
1266 if (prefs_common.auto_exteditor)
1267 compose_exec_ext_editor(compose);
1269 compose->draft_timeout_tag = -1;
1270 SCROLL_TO_CURSOR(compose);
1272 compose->modified = FALSE;
1273 compose_set_title(compose);
1275 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1280 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1281 gboolean override_pref, const gchar *system)
1283 const gchar *privacy = NULL;
1285 cm_return_if_fail(compose != NULL);
1286 cm_return_if_fail(account != NULL);
1288 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1291 if (account->default_privacy_system && strlen(account->default_privacy_system))
1292 privacy = account->default_privacy_system;
1296 GSList *privacy_avail = privacy_get_system_ids();
1297 if (privacy_avail && g_slist_length(privacy_avail)) {
1298 privacy = (gchar *)(privacy_avail->data);
1301 if (privacy != NULL) {
1303 g_free(compose->privacy_system);
1304 compose->privacy_system = NULL;
1306 if (compose->privacy_system == NULL)
1307 compose->privacy_system = g_strdup(privacy);
1308 else if (*(compose->privacy_system) == '\0') {
1309 g_free(compose->privacy_system);
1310 compose->privacy_system = g_strdup(privacy);
1312 compose_update_privacy_system_menu_item(compose, FALSE);
1313 compose_use_encryption(compose, TRUE);
1317 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1319 const gchar *privacy = NULL;
1321 if (account->default_privacy_system && strlen(account->default_privacy_system))
1322 privacy = account->default_privacy_system;
1326 GSList *privacy_avail = privacy_get_system_ids();
1327 if (privacy_avail && g_slist_length(privacy_avail)) {
1328 privacy = (gchar *)(privacy_avail->data);
1332 if (privacy != NULL) {
1334 g_free(compose->privacy_system);
1335 compose->privacy_system = NULL;
1337 if (compose->privacy_system == NULL)
1338 compose->privacy_system = g_strdup(privacy);
1339 compose_update_privacy_system_menu_item(compose, FALSE);
1340 compose_use_signing(compose, TRUE);
1344 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1348 Compose *compose = NULL;
1350 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1352 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1353 cm_return_val_if_fail(msginfo != NULL, NULL);
1355 list_len = g_slist_length(msginfo_list);
1359 case COMPOSE_REPLY_TO_ADDRESS:
1360 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1361 FALSE, prefs_common.default_reply_list, FALSE, body);
1363 case COMPOSE_REPLY_WITH_QUOTE:
1364 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1365 FALSE, prefs_common.default_reply_list, FALSE, body);
1367 case COMPOSE_REPLY_WITHOUT_QUOTE:
1368 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1369 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1371 case COMPOSE_REPLY_TO_SENDER:
1372 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1373 FALSE, FALSE, TRUE, body);
1375 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1376 compose = compose_followup_and_reply_to(msginfo,
1377 COMPOSE_QUOTE_CHECK,
1378 FALSE, FALSE, body);
1380 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1381 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1382 FALSE, FALSE, TRUE, body);
1384 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1385 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1386 FALSE, FALSE, TRUE, NULL);
1388 case COMPOSE_REPLY_TO_ALL:
1389 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1390 TRUE, FALSE, FALSE, body);
1392 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1393 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1394 TRUE, FALSE, FALSE, body);
1396 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1397 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1398 TRUE, FALSE, FALSE, NULL);
1400 case COMPOSE_REPLY_TO_LIST:
1401 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1402 FALSE, TRUE, FALSE, body);
1404 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1405 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1406 FALSE, TRUE, FALSE, body);
1408 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1409 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1410 FALSE, TRUE, FALSE, NULL);
1412 case COMPOSE_FORWARD:
1413 if (prefs_common.forward_as_attachment) {
1414 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1417 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1421 case COMPOSE_FORWARD_INLINE:
1422 /* check if we reply to more than one Message */
1423 if (list_len == 1) {
1424 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1427 /* more messages FALL THROUGH */
1428 case COMPOSE_FORWARD_AS_ATTACH:
1429 compose = compose_forward_multiple(NULL, msginfo_list);
1431 case COMPOSE_REDIRECT:
1432 compose = compose_redirect(NULL, msginfo, FALSE);
1435 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1438 if (compose == NULL) {
1439 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1443 compose->rmode = mode;
1444 switch (compose->rmode) {
1446 case COMPOSE_REPLY_WITH_QUOTE:
1447 case COMPOSE_REPLY_WITHOUT_QUOTE:
1448 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1449 debug_print("reply mode Normal\n");
1450 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1451 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1453 case COMPOSE_REPLY_TO_SENDER:
1454 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1455 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1456 debug_print("reply mode Sender\n");
1457 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1459 case COMPOSE_REPLY_TO_ALL:
1460 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1461 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1462 debug_print("reply mode All\n");
1463 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1465 case COMPOSE_REPLY_TO_LIST:
1466 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1467 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1468 debug_print("reply mode List\n");
1469 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1471 case COMPOSE_REPLY_TO_ADDRESS:
1472 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1480 static Compose *compose_reply(MsgInfo *msginfo,
1481 ComposeQuoteMode quote_mode,
1487 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1488 to_sender, FALSE, body);
1491 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1492 ComposeQuoteMode quote_mode,
1497 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1498 to_sender, TRUE, body);
1501 static void compose_extract_original_charset(Compose *compose)
1503 MsgInfo *info = NULL;
1504 if (compose->replyinfo) {
1505 info = compose->replyinfo;
1506 } else if (compose->fwdinfo) {
1507 info = compose->fwdinfo;
1508 } else if (compose->targetinfo) {
1509 info = compose->targetinfo;
1512 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1513 MimeInfo *partinfo = mimeinfo;
1514 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1515 partinfo = procmime_mimeinfo_next(partinfo);
1517 compose->orig_charset =
1518 g_strdup(procmime_mimeinfo_get_parameter(
1519 partinfo, "charset"));
1521 procmime_mimeinfo_free_all(mimeinfo);
1525 #define SIGNAL_BLOCK(buffer) { \
1526 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1527 G_CALLBACK(compose_changed_cb), \
1529 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1530 G_CALLBACK(text_inserted), \
1534 #define SIGNAL_UNBLOCK(buffer) { \
1535 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1536 G_CALLBACK(compose_changed_cb), \
1538 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1539 G_CALLBACK(text_inserted), \
1543 static Compose *compose_generic_reply(MsgInfo *msginfo,
1544 ComposeQuoteMode quote_mode,
1545 gboolean to_all, gboolean to_ml,
1547 gboolean followup_and_reply_to,
1551 PrefsAccount *account = NULL;
1552 GtkTextView *textview;
1553 GtkTextBuffer *textbuf;
1554 gboolean quote = FALSE;
1555 const gchar *qmark = NULL;
1556 const gchar *body_fmt = NULL;
1557 gchar *s_system = NULL;
1559 cm_return_val_if_fail(msginfo != NULL, NULL);
1560 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1562 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1564 cm_return_val_if_fail(account != NULL, NULL);
1566 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1568 compose->updating = TRUE;
1570 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1571 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1573 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1574 if (!compose->replyinfo)
1575 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1577 compose_extract_original_charset(compose);
1579 if (msginfo->folder && msginfo->folder->ret_rcpt)
1580 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1582 /* Set save folder */
1583 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1584 gchar *folderidentifier;
1586 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1587 folderidentifier = folder_item_get_identifier(msginfo->folder);
1588 compose_set_save_to(compose, folderidentifier);
1589 g_free(folderidentifier);
1592 if (compose_parse_header(compose, msginfo) < 0) {
1593 compose->updating = FALSE;
1594 compose_destroy(compose);
1598 /* override from name according to folder properties */
1599 if (msginfo->folder && msginfo->folder->prefs &&
1600 msginfo->folder->prefs->reply_with_format &&
1601 msginfo->folder->prefs->reply_override_from_format &&
1602 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1607 /* decode \-escape sequences in the internal representation of the quote format */
1608 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1609 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1612 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1613 compose->gtkaspell);
1615 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1617 quote_fmt_scan_string(tmp);
1620 buf = quote_fmt_get_buffer();
1622 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1624 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1625 quote_fmt_reset_vartable();
1630 textview = (GTK_TEXT_VIEW(compose->text));
1631 textbuf = gtk_text_view_get_buffer(textview);
1632 compose_create_tags(textview, compose);
1634 undo_block(compose->undostruct);
1636 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1637 gtkaspell_block_check(compose->gtkaspell);
1640 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1641 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1642 /* use the reply format of folder (if enabled), or the account's one
1643 (if enabled) or fallback to the global reply format, which is always
1644 enabled (even if empty), and use the relevant quotemark */
1646 if (msginfo->folder && msginfo->folder->prefs &&
1647 msginfo->folder->prefs->reply_with_format) {
1648 qmark = msginfo->folder->prefs->reply_quotemark;
1649 body_fmt = msginfo->folder->prefs->reply_body_format;
1651 } else if (account->reply_with_format) {
1652 qmark = account->reply_quotemark;
1653 body_fmt = account->reply_body_format;
1656 qmark = prefs_common.quotemark;
1657 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1658 body_fmt = gettext(prefs_common.quotefmt);
1665 /* empty quotemark is not allowed */
1666 if (qmark == NULL || *qmark == '\0')
1668 compose_quote_fmt(compose, compose->replyinfo,
1669 body_fmt, qmark, body, FALSE, TRUE,
1670 _("The body of the \"Reply\" template has an error at line %d."));
1671 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1672 quote_fmt_reset_vartable();
1675 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1676 compose_force_encryption(compose, account, FALSE, s_system);
1679 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1680 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1681 compose_force_signing(compose, account, s_system);
1685 SIGNAL_BLOCK(textbuf);
1687 if (account->auto_sig)
1688 compose_insert_sig(compose, FALSE);
1690 compose_wrap_all(compose);
1693 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1694 gtkaspell_highlight_all(compose->gtkaspell);
1695 gtkaspell_unblock_check(compose->gtkaspell);
1697 SIGNAL_UNBLOCK(textbuf);
1699 gtk_widget_grab_focus(compose->text);
1701 undo_unblock(compose->undostruct);
1703 if (prefs_common.auto_exteditor)
1704 compose_exec_ext_editor(compose);
1706 compose->modified = FALSE;
1707 compose_set_title(compose);
1709 compose->updating = FALSE;
1710 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1711 SCROLL_TO_CURSOR(compose);
1713 if (compose->deferred_destroy) {
1714 compose_destroy(compose);
1722 #define INSERT_FW_HEADER(var, hdr) \
1723 if (msginfo->var && *msginfo->var) { \
1724 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1725 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1726 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1729 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1730 gboolean as_attach, const gchar *body,
1731 gboolean no_extedit,
1735 GtkTextView *textview;
1736 GtkTextBuffer *textbuf;
1737 gint cursor_pos = -1;
1740 cm_return_val_if_fail(msginfo != NULL, NULL);
1741 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1744 !(account = compose_guess_forward_account_from_msginfo
1746 account = cur_account;
1748 if (!prefs_common.forward_as_attachment)
1749 mode = COMPOSE_FORWARD_INLINE;
1751 mode = COMPOSE_FORWARD;
1752 compose = compose_create(account, msginfo->folder, mode, batch);
1754 compose->updating = TRUE;
1755 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1756 if (!compose->fwdinfo)
1757 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1759 compose_extract_original_charset(compose);
1761 if (msginfo->subject && *msginfo->subject) {
1762 gchar *buf, *buf2, *p;
1764 buf = p = g_strdup(msginfo->subject);
1765 p += subject_get_prefix_length(p);
1766 memmove(buf, p, strlen(p) + 1);
1768 buf2 = g_strdup_printf("Fw: %s", buf);
1769 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1775 /* override from name according to folder properties */
1776 if (msginfo->folder && msginfo->folder->prefs &&
1777 msginfo->folder->prefs->forward_with_format &&
1778 msginfo->folder->prefs->forward_override_from_format &&
1779 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1783 MsgInfo *full_msginfo = NULL;
1786 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1788 full_msginfo = procmsg_msginfo_copy(msginfo);
1790 /* decode \-escape sequences in the internal representation of the quote format */
1791 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1792 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1795 gtkaspell_block_check(compose->gtkaspell);
1796 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1797 compose->gtkaspell);
1799 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1801 quote_fmt_scan_string(tmp);
1804 buf = quote_fmt_get_buffer();
1806 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1808 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1809 quote_fmt_reset_vartable();
1812 procmsg_msginfo_free(full_msginfo);
1815 textview = GTK_TEXT_VIEW(compose->text);
1816 textbuf = gtk_text_view_get_buffer(textview);
1817 compose_create_tags(textview, compose);
1819 undo_block(compose->undostruct);
1823 msgfile = procmsg_get_message_file(msginfo);
1824 if (!is_file_exist(msgfile))
1825 g_warning("%s: file not exist\n", msgfile);
1827 compose_attach_append(compose, msgfile, msgfile,
1828 "message/rfc822", NULL);
1832 const gchar *qmark = NULL;
1833 const gchar *body_fmt = NULL;
1834 MsgInfo *full_msginfo;
1836 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1838 full_msginfo = procmsg_msginfo_copy(msginfo);
1840 /* use the forward format of folder (if enabled), or the account's one
1841 (if enabled) or fallback to the global forward format, which is always
1842 enabled (even if empty), and use the relevant quotemark */
1843 if (msginfo->folder && msginfo->folder->prefs &&
1844 msginfo->folder->prefs->forward_with_format) {
1845 qmark = msginfo->folder->prefs->forward_quotemark;
1846 body_fmt = msginfo->folder->prefs->forward_body_format;
1848 } else if (account->forward_with_format) {
1849 qmark = account->forward_quotemark;
1850 body_fmt = account->forward_body_format;
1853 qmark = prefs_common.fw_quotemark;
1854 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1855 body_fmt = gettext(prefs_common.fw_quotefmt);
1860 /* empty quotemark is not allowed */
1861 if (qmark == NULL || *qmark == '\0')
1864 compose_quote_fmt(compose, full_msginfo,
1865 body_fmt, qmark, body, FALSE, TRUE,
1866 _("The body of the \"Forward\" template has an error at line %d."));
1867 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1868 quote_fmt_reset_vartable();
1869 compose_attach_parts(compose, msginfo);
1871 procmsg_msginfo_free(full_msginfo);
1874 SIGNAL_BLOCK(textbuf);
1876 if (account->auto_sig)
1877 compose_insert_sig(compose, FALSE);
1879 compose_wrap_all(compose);
1882 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1883 gtkaspell_highlight_all(compose->gtkaspell);
1884 gtkaspell_unblock_check(compose->gtkaspell);
1886 SIGNAL_UNBLOCK(textbuf);
1888 cursor_pos = quote_fmt_get_cursor_pos();
1889 if (cursor_pos == -1)
1890 gtk_widget_grab_focus(compose->header_last->entry);
1892 gtk_widget_grab_focus(compose->text);
1894 if (!no_extedit && prefs_common.auto_exteditor)
1895 compose_exec_ext_editor(compose);
1898 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1899 gchar *folderidentifier;
1901 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1902 folderidentifier = folder_item_get_identifier(msginfo->folder);
1903 compose_set_save_to(compose, folderidentifier);
1904 g_free(folderidentifier);
1907 undo_unblock(compose->undostruct);
1909 compose->modified = FALSE;
1910 compose_set_title(compose);
1912 compose->updating = FALSE;
1913 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1914 SCROLL_TO_CURSOR(compose);
1916 if (compose->deferred_destroy) {
1917 compose_destroy(compose);
1921 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1926 #undef INSERT_FW_HEADER
1928 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1931 GtkTextView *textview;
1932 GtkTextBuffer *textbuf;
1936 gboolean single_mail = TRUE;
1938 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1940 if (g_slist_length(msginfo_list) > 1)
1941 single_mail = FALSE;
1943 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1944 if (((MsgInfo *)msginfo->data)->folder == NULL)
1947 /* guess account from first selected message */
1949 !(account = compose_guess_forward_account_from_msginfo
1950 (msginfo_list->data)))
1951 account = cur_account;
1953 cm_return_val_if_fail(account != NULL, NULL);
1955 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1956 if (msginfo->data) {
1957 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1958 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1962 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1963 g_warning("no msginfo_list");
1967 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1969 compose->updating = TRUE;
1971 /* override from name according to folder properties */
1972 if (msginfo_list->data) {
1973 MsgInfo *msginfo = msginfo_list->data;
1975 if (msginfo->folder && msginfo->folder->prefs &&
1976 msginfo->folder->prefs->forward_with_format &&
1977 msginfo->folder->prefs->forward_override_from_format &&
1978 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1983 /* decode \-escape sequences in the internal representation of the quote format */
1984 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1985 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1988 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1989 compose->gtkaspell);
1991 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1993 quote_fmt_scan_string(tmp);
1996 buf = quote_fmt_get_buffer();
1998 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2000 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
2001 quote_fmt_reset_vartable();
2007 textview = GTK_TEXT_VIEW(compose->text);
2008 textbuf = gtk_text_view_get_buffer(textview);
2009 compose_create_tags(textview, compose);
2011 undo_block(compose->undostruct);
2012 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
2013 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
2015 if (!is_file_exist(msgfile))
2016 g_warning("%s: file not exist\n", msgfile);
2018 compose_attach_append(compose, msgfile, msgfile,
2019 "message/rfc822", NULL);
2024 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2025 if (info->subject && *info->subject) {
2026 gchar *buf, *buf2, *p;
2028 buf = p = g_strdup(info->subject);
2029 p += subject_get_prefix_length(p);
2030 memmove(buf, p, strlen(p) + 1);
2032 buf2 = g_strdup_printf("Fw: %s", buf);
2033 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2039 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2040 _("Fw: multiple emails"));
2043 SIGNAL_BLOCK(textbuf);
2045 if (account->auto_sig)
2046 compose_insert_sig(compose, FALSE);
2048 compose_wrap_all(compose);
2050 SIGNAL_UNBLOCK(textbuf);
2052 gtk_text_buffer_get_start_iter(textbuf, &iter);
2053 gtk_text_buffer_place_cursor(textbuf, &iter);
2055 gtk_widget_grab_focus(compose->header_last->entry);
2056 undo_unblock(compose->undostruct);
2057 compose->modified = FALSE;
2058 compose_set_title(compose);
2060 compose->updating = FALSE;
2061 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2062 SCROLL_TO_CURSOR(compose);
2064 if (compose->deferred_destroy) {
2065 compose_destroy(compose);
2069 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2074 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2076 GtkTextIter start = *iter;
2077 GtkTextIter end_iter;
2078 int start_pos = gtk_text_iter_get_offset(&start);
2080 if (!compose->account->sig_sep)
2083 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2084 start_pos+strlen(compose->account->sig_sep));
2086 /* check sig separator */
2087 str = gtk_text_iter_get_text(&start, &end_iter);
2088 if (!strcmp(str, compose->account->sig_sep)) {
2090 /* check end of line (\n) */
2091 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2092 start_pos+strlen(compose->account->sig_sep));
2093 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2094 start_pos+strlen(compose->account->sig_sep)+1);
2095 tmp = gtk_text_iter_get_text(&start, &end_iter);
2096 if (!strcmp(tmp,"\n")) {
2108 static void compose_colorize_signature(Compose *compose)
2110 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2112 GtkTextIter end_iter;
2113 gtk_text_buffer_get_start_iter(buffer, &iter);
2114 while (gtk_text_iter_forward_line(&iter))
2115 if (compose_is_sig_separator(compose, buffer, &iter)) {
2116 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2117 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2121 #define BLOCK_WRAP() { \
2122 prev_autowrap = compose->autowrap; \
2123 buffer = gtk_text_view_get_buffer( \
2124 GTK_TEXT_VIEW(compose->text)); \
2125 compose->autowrap = FALSE; \
2127 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2128 G_CALLBACK(compose_changed_cb), \
2130 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2131 G_CALLBACK(text_inserted), \
2134 #define UNBLOCK_WRAP() { \
2135 compose->autowrap = prev_autowrap; \
2136 if (compose->autowrap) { \
2137 gint old = compose->draft_timeout_tag; \
2138 compose->draft_timeout_tag = -2; \
2139 compose_wrap_all(compose); \
2140 compose->draft_timeout_tag = old; \
2143 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2144 G_CALLBACK(compose_changed_cb), \
2146 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2147 G_CALLBACK(text_inserted), \
2151 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2153 Compose *compose = NULL;
2154 PrefsAccount *account = NULL;
2155 GtkTextView *textview;
2156 GtkTextBuffer *textbuf;
2160 gchar buf[BUFFSIZE];
2161 gboolean use_signing = FALSE;
2162 gboolean use_encryption = FALSE;
2163 gchar *privacy_system = NULL;
2164 int priority = PRIORITY_NORMAL;
2165 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2166 gboolean autowrap = prefs_common.autowrap;
2167 gboolean autoindent = prefs_common.auto_indent;
2168 HeaderEntry *manual_headers = NULL;
2170 cm_return_val_if_fail(msginfo != NULL, NULL);
2171 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2173 if (compose_put_existing_to_front(msginfo)) {
2177 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2178 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2179 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2180 gchar queueheader_buf[BUFFSIZE];
2183 /* Select Account from queue headers */
2184 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2185 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2186 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2187 account = account_find_from_id(id);
2189 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2190 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2191 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2192 account = account_find_from_id(id);
2194 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2195 sizeof(queueheader_buf), "NAID:")) {
2196 id = atoi(&queueheader_buf[strlen("NAID:")]);
2197 account = account_find_from_id(id);
2199 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2200 sizeof(queueheader_buf), "MAID:")) {
2201 id = atoi(&queueheader_buf[strlen("MAID:")]);
2202 account = account_find_from_id(id);
2204 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2205 sizeof(queueheader_buf), "S:")) {
2206 account = account_find_from_address(queueheader_buf, FALSE);
2208 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2209 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2210 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2211 use_signing = param;
2214 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2215 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2216 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2217 use_signing = param;
2220 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2221 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2222 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2223 use_encryption = param;
2225 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2226 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2227 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2228 use_encryption = param;
2230 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2231 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2232 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2235 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2236 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2237 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2240 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2241 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2242 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2244 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2245 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2246 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2248 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2249 sizeof(queueheader_buf), "X-Priority: ")) {
2250 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2253 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2254 sizeof(queueheader_buf), "RMID:")) {
2255 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2256 if (tokens[0] && tokens[1] && tokens[2]) {
2257 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2258 if (orig_item != NULL) {
2259 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2264 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2265 sizeof(queueheader_buf), "FMID:")) {
2266 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2267 if (tokens[0] && tokens[1] && tokens[2]) {
2268 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2269 if (orig_item != NULL) {
2270 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2275 /* Get manual headers */
2276 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2277 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2278 if (*listmh != '\0') {
2279 debug_print("Got manual headers: %s\n", listmh);
2280 manual_headers = procheader_entries_from_str(listmh);
2285 account = msginfo->folder->folder->account;
2288 if (!account && prefs_common.reedit_account_autosel) {
2289 gchar from[BUFFSIZE];
2290 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2291 extract_address(from);
2292 account = account_find_from_address(from, FALSE);
2296 account = cur_account;
2298 cm_return_val_if_fail(account != NULL, NULL);
2300 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2302 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2303 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2304 compose->autowrap = autowrap;
2305 compose->replyinfo = replyinfo;
2306 compose->fwdinfo = fwdinfo;
2308 compose->updating = TRUE;
2309 compose->priority = priority;
2311 if (privacy_system != NULL) {
2312 compose->privacy_system = privacy_system;
2313 compose_use_signing(compose, use_signing);
2314 compose_use_encryption(compose, use_encryption);
2315 compose_update_privacy_system_menu_item(compose, FALSE);
2317 activate_privacy_system(compose, account, FALSE);
2320 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2322 compose_extract_original_charset(compose);
2324 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2325 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2326 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2327 gchar queueheader_buf[BUFFSIZE];
2329 /* Set message save folder */
2330 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2331 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2332 compose_set_save_to(compose, &queueheader_buf[4]);
2334 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2335 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2337 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2342 if (compose_parse_header(compose, msginfo) < 0) {
2343 compose->updating = FALSE;
2344 compose_destroy(compose);
2347 compose_reedit_set_entry(compose, msginfo);
2349 textview = GTK_TEXT_VIEW(compose->text);
2350 textbuf = gtk_text_view_get_buffer(textview);
2351 compose_create_tags(textview, compose);
2353 mark = gtk_text_buffer_get_insert(textbuf);
2354 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2356 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2357 G_CALLBACK(compose_changed_cb),
2360 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2361 fp = procmime_get_first_encrypted_text_content(msginfo);
2363 compose_force_encryption(compose, account, TRUE, NULL);
2366 fp = procmime_get_first_text_content(msginfo);
2369 g_warning("Can't get text part\n");
2373 gboolean prev_autowrap;
2374 GtkTextBuffer *buffer;
2376 while (fgets(buf, sizeof(buf), fp) != NULL) {
2378 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2384 compose_attach_parts(compose, msginfo);
2386 compose_colorize_signature(compose);
2388 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2389 G_CALLBACK(compose_changed_cb),
2392 if (manual_headers != NULL) {
2393 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2394 procheader_entries_free(manual_headers);
2395 compose->updating = FALSE;
2396 compose_destroy(compose);
2399 procheader_entries_free(manual_headers);
2402 gtk_widget_grab_focus(compose->text);
2404 if (prefs_common.auto_exteditor) {
2405 compose_exec_ext_editor(compose);
2407 compose->modified = FALSE;
2408 compose_set_title(compose);
2410 compose->updating = FALSE;
2411 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2412 SCROLL_TO_CURSOR(compose);
2414 if (compose->deferred_destroy) {
2415 compose_destroy(compose);
2419 compose->sig_str = account_get_signature_str(compose->account);
2421 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2426 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2433 cm_return_val_if_fail(msginfo != NULL, NULL);
2436 account = account_get_reply_account(msginfo,
2437 prefs_common.reply_account_autosel);
2438 cm_return_val_if_fail(account != NULL, NULL);
2440 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2442 compose->updating = TRUE;
2444 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2445 compose->replyinfo = NULL;
2446 compose->fwdinfo = NULL;
2448 compose_show_first_last_header(compose, TRUE);
2450 gtk_widget_grab_focus(compose->header_last->entry);
2452 filename = procmsg_get_message_file(msginfo);
2454 if (filename == NULL) {
2455 compose->updating = FALSE;
2456 compose_destroy(compose);
2461 compose->redirect_filename = filename;
2463 /* Set save folder */
2464 item = msginfo->folder;
2465 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2466 gchar *folderidentifier;
2468 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2469 folderidentifier = folder_item_get_identifier(item);
2470 compose_set_save_to(compose, folderidentifier);
2471 g_free(folderidentifier);
2474 compose_attach_parts(compose, msginfo);
2476 if (msginfo->subject)
2477 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2479 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2481 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2482 _("The body of the \"Redirect\" template has an error at line %d."));
2483 quote_fmt_reset_vartable();
2484 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2486 compose_colorize_signature(compose);
2489 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2490 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2491 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2493 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2494 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2495 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2496 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2497 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", FALSE);
2498 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2499 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2500 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2501 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2503 if (compose->toolbar->draft_btn)
2504 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2505 if (compose->toolbar->insert_btn)
2506 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2507 if (compose->toolbar->attach_btn)
2508 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2509 if (compose->toolbar->sig_btn)
2510 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2511 if (compose->toolbar->exteditor_btn)
2512 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2513 if (compose->toolbar->linewrap_current_btn)
2514 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2515 if (compose->toolbar->linewrap_all_btn)
2516 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2518 compose->modified = FALSE;
2519 compose_set_title(compose);
2520 compose->updating = FALSE;
2521 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2522 SCROLL_TO_CURSOR(compose);
2524 if (compose->deferred_destroy) {
2525 compose_destroy(compose);
2529 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2534 GList *compose_get_compose_list(void)
2536 return compose_list;
2539 void compose_entry_append(Compose *compose, const gchar *address,
2540 ComposeEntryType type, ComposePrefType pref_type)
2542 const gchar *header;
2544 gboolean in_quote = FALSE;
2545 if (!address || *address == '\0') return;
2552 header = N_("Bcc:");
2554 case COMPOSE_REPLYTO:
2555 header = N_("Reply-To:");
2557 case COMPOSE_NEWSGROUPS:
2558 header = N_("Newsgroups:");
2560 case COMPOSE_FOLLOWUPTO:
2561 header = N_( "Followup-To:");
2563 case COMPOSE_INREPLYTO:
2564 header = N_( "In-Reply-To:");
2571 header = prefs_common_translated_header_name(header);
2573 cur = begin = (gchar *)address;
2575 /* we separate the line by commas, but not if we're inside a quoted
2577 while (*cur != '\0') {
2579 in_quote = !in_quote;
2580 if (*cur == ',' && !in_quote) {
2581 gchar *tmp = g_strdup(begin);
2583 tmp[cur-begin]='\0';
2586 while (*tmp == ' ' || *tmp == '\t')
2588 compose_add_header_entry(compose, header, tmp, pref_type);
2595 gchar *tmp = g_strdup(begin);
2597 tmp[cur-begin]='\0';
2598 while (*tmp == ' ' || *tmp == '\t')
2600 compose_add_header_entry(compose, header, tmp, pref_type);
2605 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2607 #if !GTK_CHECK_VERSION(3, 0, 0)
2608 static GdkColor yellow;
2609 static GdkColor black;
2610 static gboolean yellow_initialised = FALSE;
2612 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2613 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2618 #if !GTK_CHECK_VERSION(3, 0, 0)
2619 if (!yellow_initialised) {
2620 gdk_color_parse("#f5f6be", &yellow);
2621 gdk_color_parse("#000000", &black);
2622 yellow_initialised = gdk_colormap_alloc_color(
2623 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2624 yellow_initialised &= gdk_colormap_alloc_color(
2625 gdk_colormap_get_system(), &black, FALSE, TRUE);
2629 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2630 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2631 if (gtk_entry_get_text(entry) &&
2632 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2633 #if !GTK_CHECK_VERSION(3, 0, 0)
2634 if (yellow_initialised) {
2636 gtk_widget_modify_base(
2637 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2638 GTK_STATE_NORMAL, &yellow);
2639 gtk_widget_modify_text(
2640 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2641 GTK_STATE_NORMAL, &black);
2642 #if !GTK_CHECK_VERSION(3, 0, 0)
2649 void compose_toolbar_cb(gint action, gpointer data)
2651 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2652 Compose *compose = (Compose*)toolbar_item->parent;
2654 cm_return_if_fail(compose != NULL);
2658 compose_send_cb(NULL, compose);
2661 compose_send_later_cb(NULL, compose);
2664 compose_draft(compose, COMPOSE_QUIT_EDITING);
2667 compose_insert_file_cb(NULL, compose);
2670 compose_attach_cb(NULL, compose);
2673 compose_insert_sig(compose, FALSE);
2676 compose_ext_editor_cb(NULL, compose);
2678 case A_LINEWRAP_CURRENT:
2679 compose_beautify_paragraph(compose, NULL, TRUE);
2681 case A_LINEWRAP_ALL:
2682 compose_wrap_all_full(compose, TRUE);
2685 compose_address_cb(NULL, compose);
2688 case A_CHECK_SPELLING:
2689 compose_check_all(NULL, compose);
2697 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2702 gchar *subject = NULL;
2706 gchar **attach = NULL;
2707 gchar *inreplyto = NULL;
2708 MailField mfield = NO_FIELD_PRESENT;
2710 /* get mailto parts but skip from */
2711 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2714 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2715 mfield = TO_FIELD_PRESENT;
2718 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2720 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2722 if (!g_utf8_validate (subject, -1, NULL)) {
2723 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2724 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2727 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2729 mfield = SUBJECT_FIELD_PRESENT;
2732 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2733 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2736 gboolean prev_autowrap = compose->autowrap;
2738 compose->autowrap = FALSE;
2740 mark = gtk_text_buffer_get_insert(buffer);
2741 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2743 if (!g_utf8_validate (body, -1, NULL)) {
2744 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2745 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2748 gtk_text_buffer_insert(buffer, &iter, body, -1);
2750 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2752 compose->autowrap = prev_autowrap;
2753 if (compose->autowrap)
2754 compose_wrap_all(compose);
2755 mfield = BODY_FIELD_PRESENT;
2759 gint i = 0, att = 0;
2760 gchar *warn_files = NULL;
2761 while (attach[i] != NULL) {
2762 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2763 if (utf8_filename) {
2764 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2765 gchar *tmp = g_strdup_printf("%s%s\n",
2766 warn_files?warn_files:"",
2772 g_free(utf8_filename);
2774 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2779 alertpanel_notice(ngettext(
2780 "The following file has been attached: \n%s",
2781 "The following files have been attached: \n%s", att), warn_files);
2786 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2799 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2801 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2802 {"Cc:", NULL, TRUE},
2803 {"References:", NULL, FALSE},
2804 {"Bcc:", NULL, TRUE},
2805 {"Newsgroups:", NULL, TRUE},
2806 {"Followup-To:", NULL, TRUE},
2807 {"List-Post:", NULL, FALSE},
2808 {"X-Priority:", NULL, FALSE},
2809 {NULL, NULL, FALSE}};
2825 cm_return_val_if_fail(msginfo != NULL, -1);
2827 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2828 procheader_get_header_fields(fp, hentry);
2831 if (hentry[H_REPLY_TO].body != NULL) {
2832 if (hentry[H_REPLY_TO].body[0] != '\0') {
2834 conv_unmime_header(hentry[H_REPLY_TO].body,
2837 g_free(hentry[H_REPLY_TO].body);
2838 hentry[H_REPLY_TO].body = NULL;
2840 if (hentry[H_CC].body != NULL) {
2841 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2842 g_free(hentry[H_CC].body);
2843 hentry[H_CC].body = NULL;
2845 if (hentry[H_REFERENCES].body != NULL) {
2846 if (compose->mode == COMPOSE_REEDIT)
2847 compose->references = hentry[H_REFERENCES].body;
2849 compose->references = compose_parse_references
2850 (hentry[H_REFERENCES].body, msginfo->msgid);
2851 g_free(hentry[H_REFERENCES].body);
2853 hentry[H_REFERENCES].body = NULL;
2855 if (hentry[H_BCC].body != NULL) {
2856 if (compose->mode == COMPOSE_REEDIT)
2858 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2859 g_free(hentry[H_BCC].body);
2860 hentry[H_BCC].body = NULL;
2862 if (hentry[H_NEWSGROUPS].body != NULL) {
2863 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2864 hentry[H_NEWSGROUPS].body = NULL;
2866 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2867 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2868 compose->followup_to =
2869 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2872 g_free(hentry[H_FOLLOWUP_TO].body);
2873 hentry[H_FOLLOWUP_TO].body = NULL;
2875 if (hentry[H_LIST_POST].body != NULL) {
2876 gchar *to = NULL, *start = NULL;
2878 extract_address(hentry[H_LIST_POST].body);
2879 if (hentry[H_LIST_POST].body[0] != '\0') {
2880 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2882 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2883 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2886 g_free(compose->ml_post);
2887 compose->ml_post = to;
2890 g_free(hentry[H_LIST_POST].body);
2891 hentry[H_LIST_POST].body = NULL;
2894 /* CLAWS - X-Priority */
2895 if (compose->mode == COMPOSE_REEDIT)
2896 if (hentry[H_X_PRIORITY].body != NULL) {
2899 priority = atoi(hentry[H_X_PRIORITY].body);
2900 g_free(hentry[H_X_PRIORITY].body);
2902 hentry[H_X_PRIORITY].body = NULL;
2904 if (priority < PRIORITY_HIGHEST ||
2905 priority > PRIORITY_LOWEST)
2906 priority = PRIORITY_NORMAL;
2908 compose->priority = priority;
2911 if (compose->mode == COMPOSE_REEDIT) {
2912 if (msginfo->inreplyto && *msginfo->inreplyto)
2913 compose->inreplyto = g_strdup(msginfo->inreplyto);
2917 if (msginfo->msgid && *msginfo->msgid)
2918 compose->inreplyto = g_strdup(msginfo->msgid);
2920 if (!compose->references) {
2921 if (msginfo->msgid && *msginfo->msgid) {
2922 if (msginfo->inreplyto && *msginfo->inreplyto)
2923 compose->references =
2924 g_strdup_printf("<%s>\n\t<%s>",
2928 compose->references =
2929 g_strconcat("<", msginfo->msgid, ">",
2931 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2932 compose->references =
2933 g_strconcat("<", msginfo->inreplyto, ">",
2941 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2946 cm_return_val_if_fail(msginfo != NULL, -1);
2948 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2949 procheader_get_header_fields(fp, entries);
2953 while (he != NULL && he->name != NULL) {
2955 GtkListStore *model = NULL;
2957 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
2958 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
2959 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
2960 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
2961 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
2968 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2970 GSList *ref_id_list, *cur;
2974 ref_id_list = references_list_append(NULL, ref);
2975 if (!ref_id_list) return NULL;
2976 if (msgid && *msgid)
2977 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2982 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2983 /* "<" + Message-ID + ">" + CR+LF+TAB */
2984 len += strlen((gchar *)cur->data) + 5;
2986 if (len > MAX_REFERENCES_LEN) {
2987 /* remove second message-ID */
2988 if (ref_id_list && ref_id_list->next &&
2989 ref_id_list->next->next) {
2990 g_free(ref_id_list->next->data);
2991 ref_id_list = g_slist_remove
2992 (ref_id_list, ref_id_list->next->data);
2994 slist_free_strings_full(ref_id_list);
3001 new_ref = g_string_new("");
3002 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3003 if (new_ref->len > 0)
3004 g_string_append(new_ref, "\n\t");
3005 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3008 slist_free_strings_full(ref_id_list);
3010 new_ref_str = new_ref->str;
3011 g_string_free(new_ref, FALSE);
3016 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3017 const gchar *fmt, const gchar *qmark,
3018 const gchar *body, gboolean rewrap,
3019 gboolean need_unescape,
3020 const gchar *err_msg)
3022 MsgInfo* dummyinfo = NULL;
3023 gchar *quote_str = NULL;
3025 gboolean prev_autowrap;
3026 const gchar *trimmed_body = body;
3027 gint cursor_pos = -1;
3028 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3029 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3034 SIGNAL_BLOCK(buffer);
3037 dummyinfo = compose_msginfo_new_from_compose(compose);
3038 msginfo = dummyinfo;
3041 if (qmark != NULL) {
3043 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3044 compose->gtkaspell);
3046 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3048 quote_fmt_scan_string(qmark);
3051 buf = quote_fmt_get_buffer();
3053 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3055 Xstrdup_a(quote_str, buf, goto error)
3058 if (fmt && *fmt != '\0') {
3061 while (*trimmed_body == '\n')
3065 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3066 compose->gtkaspell);
3068 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3070 if (need_unescape) {
3073 /* decode \-escape sequences in the internal representation of the quote format */
3074 tmp = g_malloc(strlen(fmt)+1);
3075 pref_get_unescaped_pref(tmp, fmt);
3076 quote_fmt_scan_string(tmp);
3080 quote_fmt_scan_string(fmt);
3084 buf = quote_fmt_get_buffer();
3086 gint line = quote_fmt_get_line();
3087 alertpanel_error(err_msg, line);
3093 prev_autowrap = compose->autowrap;
3094 compose->autowrap = FALSE;
3096 mark = gtk_text_buffer_get_insert(buffer);
3097 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3098 if (g_utf8_validate(buf, -1, NULL)) {
3099 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3101 gchar *tmpout = NULL;
3102 tmpout = conv_codeset_strdup
3103 (buf, conv_get_locale_charset_str_no_utf8(),
3105 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3107 tmpout = g_malloc(strlen(buf)*2+1);
3108 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3110 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3114 cursor_pos = quote_fmt_get_cursor_pos();
3115 if (cursor_pos == -1)
3116 cursor_pos = gtk_text_iter_get_offset(&iter);
3117 compose->set_cursor_pos = cursor_pos;
3119 gtk_text_buffer_get_start_iter(buffer, &iter);
3120 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3121 gtk_text_buffer_place_cursor(buffer, &iter);
3123 compose->autowrap = prev_autowrap;
3124 if (compose->autowrap && rewrap)
3125 compose_wrap_all(compose);
3132 SIGNAL_UNBLOCK(buffer);
3134 procmsg_msginfo_free( dummyinfo );
3139 /* if ml_post is of type addr@host and from is of type
3140 * addr-anything@host, return TRUE
3142 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3144 gchar *left_ml = NULL;
3145 gchar *right_ml = NULL;
3146 gchar *left_from = NULL;
3147 gchar *right_from = NULL;
3148 gboolean result = FALSE;
3150 if (!ml_post || !from)
3153 left_ml = g_strdup(ml_post);
3154 if (strstr(left_ml, "@")) {
3155 right_ml = strstr(left_ml, "@")+1;
3156 *(strstr(left_ml, "@")) = '\0';
3159 left_from = g_strdup(from);
3160 if (strstr(left_from, "@")) {
3161 right_from = strstr(left_from, "@")+1;
3162 *(strstr(left_from, "@")) = '\0';
3165 if (left_ml && left_from && right_ml && right_from
3166 && !strncmp(left_from, left_ml, strlen(left_ml))
3167 && !strcmp(right_from, right_ml)) {
3176 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3177 gboolean respect_default_to)
3181 if (!folder || !folder->prefs)
3184 if (respect_default_to && folder->prefs->enable_default_to) {
3185 compose_entry_append(compose, folder->prefs->default_to,
3186 COMPOSE_TO, PREF_FOLDER);
3187 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3189 if (folder->prefs->enable_default_cc)
3190 compose_entry_append(compose, folder->prefs->default_cc,
3191 COMPOSE_CC, PREF_FOLDER);
3192 if (folder->prefs->enable_default_bcc)
3193 compose_entry_append(compose, folder->prefs->default_bcc,
3194 COMPOSE_BCC, PREF_FOLDER);
3195 if (folder->prefs->enable_default_replyto)
3196 compose_entry_append(compose, folder->prefs->default_replyto,
3197 COMPOSE_REPLYTO, PREF_FOLDER);
3200 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3205 if (!compose || !msginfo)
3208 if (msginfo->subject && *msginfo->subject) {
3209 buf = p = g_strdup(msginfo->subject);
3210 p += subject_get_prefix_length(p);
3211 memmove(buf, p, strlen(p) + 1);
3213 buf2 = g_strdup_printf("Re: %s", buf);
3214 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3219 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3222 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3223 gboolean to_all, gboolean to_ml,
3225 gboolean followup_and_reply_to)
3227 GSList *cc_list = NULL;
3230 gchar *replyto = NULL;
3231 gchar *ac_email = NULL;
3233 gboolean reply_to_ml = FALSE;
3234 gboolean default_reply_to = FALSE;
3236 cm_return_if_fail(compose->account != NULL);
3237 cm_return_if_fail(msginfo != NULL);
3239 reply_to_ml = to_ml && compose->ml_post;
3241 default_reply_to = msginfo->folder &&
3242 msginfo->folder->prefs->enable_default_reply_to;
3244 if (compose->account->protocol != A_NNTP) {
3245 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3247 if (reply_to_ml && !default_reply_to) {
3249 gboolean is_subscr = is_subscription(compose->ml_post,
3252 /* normal answer to ml post with a reply-to */
3253 compose_entry_append(compose,
3255 COMPOSE_TO, PREF_ML);
3256 if (compose->replyto)
3257 compose_entry_append(compose,
3259 COMPOSE_CC, PREF_ML);
3261 /* answer to subscription confirmation */
3262 if (compose->replyto)
3263 compose_entry_append(compose,
3265 COMPOSE_TO, PREF_ML);
3266 else if (msginfo->from)
3267 compose_entry_append(compose,
3269 COMPOSE_TO, PREF_ML);
3272 else if (!(to_all || to_sender) && default_reply_to) {
3273 compose_entry_append(compose,
3274 msginfo->folder->prefs->default_reply_to,
3275 COMPOSE_TO, PREF_FOLDER);
3276 compose_entry_mark_default_to(compose,
3277 msginfo->folder->prefs->default_reply_to);
3282 Xstrdup_a(tmp1, msginfo->from, return);
3283 extract_address(tmp1);
3284 if (to_all || to_sender ||
3285 !account_find_from_address(tmp1, FALSE))
3286 compose_entry_append(compose,
3287 (compose->replyto && !to_sender)
3288 ? compose->replyto :
3289 msginfo->from ? msginfo->from : "",
3290 COMPOSE_TO, PREF_NONE);
3291 else if (!to_all && !to_sender) {
3292 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3293 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3294 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3295 if (compose->replyto) {
3296 compose_entry_append(compose,
3298 COMPOSE_TO, PREF_NONE);
3300 compose_entry_append(compose,
3301 msginfo->from ? msginfo->from : "",
3302 COMPOSE_TO, PREF_NONE);
3305 /* replying to own mail, use original recp */
3306 compose_entry_append(compose,
3307 msginfo->to ? msginfo->to : "",
3308 COMPOSE_TO, PREF_NONE);
3309 compose_entry_append(compose,
3310 msginfo->cc ? msginfo->cc : "",
3311 COMPOSE_CC, PREF_NONE);
3316 if (to_sender || (compose->followup_to &&
3317 !strncmp(compose->followup_to, "poster", 6)))
3318 compose_entry_append
3320 (compose->replyto ? compose->replyto :
3321 msginfo->from ? msginfo->from : ""),
3322 COMPOSE_TO, PREF_NONE);
3324 else if (followup_and_reply_to || to_all) {
3325 compose_entry_append
3327 (compose->replyto ? compose->replyto :
3328 msginfo->from ? msginfo->from : ""),
3329 COMPOSE_TO, PREF_NONE);
3331 compose_entry_append
3333 compose->followup_to ? compose->followup_to :
3334 compose->newsgroups ? compose->newsgroups : "",
3335 COMPOSE_NEWSGROUPS, PREF_NONE);
3338 compose_entry_append
3340 compose->followup_to ? compose->followup_to :
3341 compose->newsgroups ? compose->newsgroups : "",
3342 COMPOSE_NEWSGROUPS, PREF_NONE);
3344 compose_reply_set_subject(compose, msginfo);
3346 if (to_ml && compose->ml_post) return;
3347 if (!to_all || compose->account->protocol == A_NNTP) return;
3349 if (compose->replyto) {
3350 Xstrdup_a(replyto, compose->replyto, return);
3351 extract_address(replyto);
3353 if (msginfo->from) {
3354 Xstrdup_a(from, msginfo->from, return);
3355 extract_address(from);
3358 if (replyto && from)
3359 cc_list = address_list_append_with_comments(cc_list, from);
3360 if (to_all && msginfo->folder &&
3361 msginfo->folder->prefs->enable_default_reply_to)
3362 cc_list = address_list_append_with_comments(cc_list,
3363 msginfo->folder->prefs->default_reply_to);
3364 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3365 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3367 ac_email = g_utf8_strdown(compose->account->address, -1);
3370 for (cur = cc_list; cur != NULL; cur = cur->next) {
3371 gchar *addr = g_utf8_strdown(cur->data, -1);
3372 extract_address(addr);
3374 if (strcmp(ac_email, addr))
3375 compose_entry_append(compose, (gchar *)cur->data,
3376 COMPOSE_CC, PREF_NONE);
3378 debug_print("Cc address same as compose account's, ignoring\n");
3383 slist_free_strings_full(cc_list);
3389 #define SET_ENTRY(entry, str) \
3392 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3395 #define SET_ADDRESS(type, str) \
3398 compose_entry_append(compose, str, type, PREF_NONE); \
3401 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3403 cm_return_if_fail(msginfo != NULL);
3405 SET_ENTRY(subject_entry, msginfo->subject);
3406 SET_ENTRY(from_name, msginfo->from);
3407 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3408 SET_ADDRESS(COMPOSE_CC, compose->cc);
3409 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3410 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3411 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3412 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3414 compose_update_priority_menu_item(compose);
3415 compose_update_privacy_system_menu_item(compose, FALSE);
3416 compose_show_first_last_header(compose, TRUE);
3422 static void compose_insert_sig(Compose *compose, gboolean replace)
3424 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3425 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3427 GtkTextIter iter, iter_end;
3428 gint cur_pos, ins_pos;
3429 gboolean prev_autowrap;
3430 gboolean found = FALSE;
3431 gboolean exists = FALSE;
3433 cm_return_if_fail(compose->account != NULL);
3437 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3438 G_CALLBACK(compose_changed_cb),
3441 mark = gtk_text_buffer_get_insert(buffer);
3442 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3443 cur_pos = gtk_text_iter_get_offset (&iter);
3446 gtk_text_buffer_get_end_iter(buffer, &iter);
3448 exists = (compose->sig_str != NULL);
3451 GtkTextIter first_iter, start_iter, end_iter;
3453 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3455 if (!exists || compose->sig_str[0] == '\0')
3458 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3459 compose->signature_tag);
3462 /* include previous \n\n */
3463 gtk_text_iter_backward_chars(&first_iter, 1);
3464 start_iter = first_iter;
3465 end_iter = first_iter;
3467 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3468 compose->signature_tag);
3469 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3470 compose->signature_tag);
3472 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3478 g_free(compose->sig_str);
3479 compose->sig_str = account_get_signature_str(compose->account);
3481 cur_pos = gtk_text_iter_get_offset(&iter);
3483 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3484 g_free(compose->sig_str);
3485 compose->sig_str = NULL;
3487 if (compose->sig_inserted == FALSE)
3488 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3489 compose->sig_inserted = TRUE;
3491 cur_pos = gtk_text_iter_get_offset(&iter);
3492 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3494 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3495 gtk_text_iter_forward_chars(&iter, 1);
3496 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3497 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3499 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3500 cur_pos = gtk_text_buffer_get_char_count (buffer);
3503 /* put the cursor where it should be
3504 * either where the quote_fmt says, either where it was */
3505 if (compose->set_cursor_pos < 0)
3506 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3508 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3509 compose->set_cursor_pos);
3511 compose->set_cursor_pos = -1;
3512 gtk_text_buffer_place_cursor(buffer, &iter);
3513 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3514 G_CALLBACK(compose_changed_cb),
3520 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3523 GtkTextBuffer *buffer;
3526 const gchar *cur_encoding;
3527 gchar buf[BUFFSIZE];
3530 gboolean prev_autowrap;
3531 gboolean badtxt = FALSE;
3532 struct stat file_stat;
3534 GString *file_contents = NULL;
3536 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3538 /* get the size of the file we are about to insert */
3539 ret = g_stat(file, &file_stat);
3541 gchar *shortfile = g_path_get_basename(file);
3542 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3544 return COMPOSE_INSERT_NO_FILE;
3545 } else if (prefs_common.warn_large_insert == TRUE) {
3547 /* ask user for confirmation if the file is large */
3548 if (prefs_common.warn_large_insert_size < 0 ||
3549 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3553 msg = g_strdup_printf(_("You are about to insert a file of %s "
3554 "in the message body. Are you sure you want to do that?"),
3555 to_human_readable(file_stat.st_size));
3556 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3557 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3560 /* do we ask for confirmation next time? */
3561 if (aval & G_ALERTDISABLE) {
3562 /* no confirmation next time, disable feature in preferences */
3563 aval &= ~G_ALERTDISABLE;
3564 prefs_common.warn_large_insert = FALSE;
3567 /* abort file insertion if user canceled action */
3568 if (aval != G_ALERTALTERNATE) {
3569 return COMPOSE_INSERT_NO_FILE;
3575 if ((fp = g_fopen(file, "rb")) == NULL) {
3576 FILE_OP_ERROR(file, "fopen");
3577 return COMPOSE_INSERT_READ_ERROR;
3580 prev_autowrap = compose->autowrap;
3581 compose->autowrap = FALSE;
3583 text = GTK_TEXT_VIEW(compose->text);
3584 buffer = gtk_text_view_get_buffer(text);
3585 mark = gtk_text_buffer_get_insert(buffer);
3586 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3588 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3589 G_CALLBACK(text_inserted),
3592 cur_encoding = conv_get_locale_charset_str_no_utf8();
3594 file_contents = g_string_new("");
3595 while (fgets(buf, sizeof(buf), fp) != NULL) {
3598 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3599 str = g_strdup(buf);
3601 str = conv_codeset_strdup
3602 (buf, cur_encoding, CS_INTERNAL);
3605 /* strip <CR> if DOS/Windows file,
3606 replace <CR> with <LF> if Macintosh file. */
3609 if (len > 0 && str[len - 1] != '\n') {
3611 if (str[len] == '\r') str[len] = '\n';
3614 file_contents = g_string_append(file_contents, str);
3618 gtk_text_buffer_insert(buffer, &iter, file_contents->str, -1);
3619 g_string_free(file_contents, TRUE);
3621 compose_changed_cb(NULL, compose);
3622 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3623 G_CALLBACK(text_inserted),
3625 compose->autowrap = prev_autowrap;
3626 if (compose->autowrap)
3627 compose_wrap_all(compose);
3632 return COMPOSE_INSERT_INVALID_CHARACTER;
3634 return COMPOSE_INSERT_SUCCESS;
3637 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3638 const gchar *filename,
3639 const gchar *content_type,
3640 const gchar *charset)
3648 GtkListStore *store;
3650 gboolean has_binary = FALSE;
3652 if (!is_file_exist(file)) {
3653 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3654 gboolean result = FALSE;
3655 if (file_from_uri && is_file_exist(file_from_uri)) {
3656 result = compose_attach_append(
3657 compose, file_from_uri,
3658 filename, content_type,
3661 g_free(file_from_uri);
3664 alertpanel_error("File %s doesn't exist\n", filename);
3667 if ((size = get_file_size(file)) < 0) {
3668 alertpanel_error("Can't get file size of %s\n", filename);