2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2011 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/>.
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #include "addressbook.h"
64 #include "folderview.h"
67 #include "stock_pixmap.h"
68 #include "send_message.h"
71 #include "customheader.h"
72 #include "prefs_common.h"
73 #include "prefs_account.h"
77 #include "procheader.h"
79 #include "statusbar.h"
82 #include "quoted-printable.h"
86 #include "gtkshruler.h"
88 #include "alertpanel.h"
89 #include "manage_window.h"
91 #include "addr_compl.h"
92 #include "quote_fmt.h"
94 #include "foldersel.h"
97 #include "message_search.h"
102 #include "autofaces.h"
103 #include "spell_entry.h"
116 #define N_ATTACH_COLS (N_COL_COLUMNS)
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
132 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
134 } ComposeCallAdvancedAction;
138 PRIORITY_HIGHEST = 1,
147 COMPOSE_INSERT_SUCCESS,
148 COMPOSE_INSERT_READ_ERROR,
149 COMPOSE_INSERT_INVALID_CHARACTER,
150 COMPOSE_INSERT_NO_FILE
151 } ComposeInsertResult;
155 COMPOSE_WRITE_FOR_SEND,
156 COMPOSE_WRITE_FOR_STORE
161 COMPOSE_QUOTE_FORCED,
168 SUBJECT_FIELD_PRESENT,
173 #define B64_LINE_SIZE 57
174 #define B64_BUFFSIZE 77
176 #define MAX_REFERENCES_LEN 999
178 static GList *compose_list = NULL;
180 static Compose *compose_generic_new (PrefsAccount *account,
184 GList *listAddress );
186 static Compose *compose_create (PrefsAccount *account,
191 static void compose_entry_mark_default_to (Compose *compose,
192 const gchar *address);
193 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
194 ComposeQuoteMode quote_mode,
198 static Compose *compose_forward_multiple (PrefsAccount *account,
199 GSList *msginfo_list);
200 static Compose *compose_reply (MsgInfo *msginfo,
201 ComposeQuoteMode quote_mode,
206 static Compose *compose_reply_mode (ComposeMode mode,
207 GSList *msginfo_list,
209 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
210 static void compose_update_privacy_systems_menu(Compose *compose);
212 static GtkWidget *compose_account_option_menu_create
214 static void compose_set_out_encoding (Compose *compose);
215 static void compose_set_template_menu (Compose *compose);
216 static void compose_destroy (Compose *compose);
218 static MailField compose_entries_set (Compose *compose,
220 ComposeEntryType to_type);
221 static gint compose_parse_header (Compose *compose,
223 static gint compose_parse_manual_headers (Compose *compose,
225 HeaderEntry *entries);
226 static gchar *compose_parse_references (const gchar *ref,
229 static gchar *compose_quote_fmt (Compose *compose,
235 gboolean need_unescape,
236 const gchar *err_msg);
238 static void compose_reply_set_entry (Compose *compose,
244 followup_and_reply_to);
245 static void compose_reedit_set_entry (Compose *compose,
248 static void compose_insert_sig (Compose *compose,
250 static ComposeInsertResult compose_insert_file (Compose *compose,
253 static gboolean compose_attach_append (Compose *compose,
256 const gchar *content_type,
257 const gchar *charset);
258 static void compose_attach_parts (Compose *compose,
261 static gboolean compose_beautify_paragraph (Compose *compose,
262 GtkTextIter *par_iter,
264 static void compose_wrap_all (Compose *compose);
265 static void compose_wrap_all_full (Compose *compose,
268 static void compose_set_title (Compose *compose);
269 static void compose_select_account (Compose *compose,
270 PrefsAccount *account,
273 static PrefsAccount *compose_current_mail_account(void);
274 /* static gint compose_send (Compose *compose); */
275 static gboolean compose_check_for_valid_recipient
277 static gboolean compose_check_entries (Compose *compose,
278 gboolean check_everything);
279 static gint compose_write_to_file (Compose *compose,
282 gboolean attach_parts);
283 static gint compose_write_body_to_file (Compose *compose,
285 static gint compose_remove_reedit_target (Compose *compose,
287 static void compose_remove_draft (Compose *compose);
288 static gint compose_queue_sub (Compose *compose,
292 gboolean check_subject,
293 gboolean remove_reedit_target);
294 static int compose_add_attachments (Compose *compose,
296 static gchar *compose_get_header (Compose *compose);
297 static gchar *compose_get_manual_headers_info (Compose *compose);
299 static void compose_convert_header (Compose *compose,
304 gboolean addr_field);
306 static void compose_attach_info_free (AttachInfo *ainfo);
307 static void compose_attach_remove_selected (GtkAction *action,
310 static void compose_template_apply (Compose *compose,
313 static void compose_attach_property (GtkAction *action,
315 static void compose_attach_property_create (gboolean *cancelled);
316 static void attach_property_ok (GtkWidget *widget,
317 gboolean *cancelled);
318 static void attach_property_cancel (GtkWidget *widget,
319 gboolean *cancelled);
320 static gint attach_property_delete_event (GtkWidget *widget,
322 gboolean *cancelled);
323 static gboolean attach_property_key_pressed (GtkWidget *widget,
325 gboolean *cancelled);
327 static void compose_exec_ext_editor (Compose *compose);
329 static gint compose_exec_ext_editor_real (const gchar *file);
330 static gboolean compose_ext_editor_kill (Compose *compose);
331 static gboolean compose_input_cb (GIOChannel *source,
332 GIOCondition condition,
334 static void compose_set_ext_editor_sensitive (Compose *compose,
336 #endif /* G_OS_UNIX */
338 static void compose_undo_state_changed (UndoMain *undostruct,
343 static void compose_create_header_entry (Compose *compose);
344 static void compose_add_header_entry (Compose *compose, const gchar *header,
345 gchar *text, ComposePrefType pref_type);
346 static void compose_remove_header_entries(Compose *compose);
348 static void compose_update_priority_menu_item(Compose * compose);
350 static void compose_spell_menu_changed (void *data);
351 static void compose_dict_changed (void *data);
353 static void compose_add_field_list ( Compose *compose,
354 GList *listAddress );
356 /* callback functions */
358 static void compose_notebook_size_alloc (GtkNotebook *notebook,
359 GtkAllocation *allocation,
361 static gboolean compose_edit_size_alloc (GtkEditable *widget,
362 GtkAllocation *allocation,
363 GtkSHRuler *shruler);
364 static void account_activated (GtkComboBox *optmenu,
366 static void attach_selected (GtkTreeView *tree_view,
367 GtkTreePath *tree_path,
368 GtkTreeViewColumn *column,
370 static gboolean attach_button_pressed (GtkWidget *widget,
371 GdkEventButton *event,
373 static gboolean attach_key_pressed (GtkWidget *widget,
376 static void compose_send_cb (GtkAction *action, gpointer data);
377 static void compose_send_later_cb (GtkAction *action, gpointer data);
379 static void compose_save_cb (GtkAction *action,
382 static void compose_attach_cb (GtkAction *action,
384 static void compose_insert_file_cb (GtkAction *action,
386 static void compose_insert_sig_cb (GtkAction *action,
389 static void compose_close_cb (GtkAction *action,
391 static void compose_print_cb (GtkAction *action,
394 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
396 static void compose_address_cb (GtkAction *action,
398 static void about_show_cb (GtkAction *action,
400 static void compose_template_activate_cb(GtkWidget *widget,
403 static void compose_ext_editor_cb (GtkAction *action,
406 static gint compose_delete_cb (GtkWidget *widget,
410 static void compose_undo_cb (GtkAction *action,
412 static void compose_redo_cb (GtkAction *action,
414 static void compose_cut_cb (GtkAction *action,
416 static void compose_copy_cb (GtkAction *action,
418 static void compose_paste_cb (GtkAction *action,
420 static void compose_paste_as_quote_cb (GtkAction *action,
422 static void compose_paste_no_wrap_cb (GtkAction *action,
424 static void compose_paste_wrap_cb (GtkAction *action,
426 static void compose_allsel_cb (GtkAction *action,
429 static void compose_advanced_action_cb (GtkAction *action,
432 static void compose_grab_focus_cb (GtkWidget *widget,
435 static void compose_changed_cb (GtkTextBuffer *textbuf,
438 static void compose_wrap_cb (GtkAction *action,
440 static void compose_wrap_all_cb (GtkAction *action,
442 static void compose_find_cb (GtkAction *action,
444 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
446 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
449 static void compose_toggle_ruler_cb (GtkToggleAction *action,
451 static void compose_toggle_sign_cb (GtkToggleAction *action,
453 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
455 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
456 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
457 static void activate_privacy_system (Compose *compose,
458 PrefsAccount *account,
460 static void compose_use_signing(Compose *compose, gboolean use_signing);
461 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
462 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
464 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
466 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
467 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
468 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
470 static void compose_attach_drag_received_cb (GtkWidget *widget,
471 GdkDragContext *drag_context,
474 GtkSelectionData *data,
478 static void compose_insert_drag_received_cb (GtkWidget *widget,
479 GdkDragContext *drag_context,
482 GtkSelectionData *data,
486 static void compose_header_drag_received_cb (GtkWidget *widget,
487 GdkDragContext *drag_context,
490 GtkSelectionData *data,
495 static gboolean compose_drag_drop (GtkWidget *widget,
496 GdkDragContext *drag_context,
498 guint time, gpointer user_data);
500 static void text_inserted (GtkTextBuffer *buffer,
505 static Compose *compose_generic_reply(MsgInfo *msginfo,
506 ComposeQuoteMode quote_mode,
510 gboolean followup_and_reply_to,
513 static void compose_headerentry_changed_cb (GtkWidget *entry,
514 ComposeHeaderEntry *headerentry);
515 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
517 ComposeHeaderEntry *headerentry);
518 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
519 ComposeHeaderEntry *headerentry);
521 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
523 static void compose_allow_user_actions (Compose *compose, gboolean allow);
525 static void compose_nothing_cb (GtkAction *action, gpointer data)
531 static void compose_check_all (GtkAction *action, gpointer data);
532 static void compose_highlight_all (GtkAction *action, gpointer data);
533 static void compose_check_backwards (GtkAction *action, gpointer data);
534 static void compose_check_forwards_go (GtkAction *action, gpointer data);
537 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
539 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
542 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
543 FolderItem *folder_item);
545 static void compose_attach_update_label(Compose *compose);
546 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
547 gboolean respect_default_to);
549 static GtkActionEntry compose_popup_entries[] =
551 {"Compose", NULL, "Compose" },
552 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
553 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
554 {"Compose/---", NULL, "---", NULL, NULL, NULL },
555 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
558 static GtkActionEntry compose_entries[] =
560 {"Menu", NULL, "Menu" },
562 {"Message", NULL, N_("_Message") },
563 {"Edit", NULL, N_("_Edit") },
565 {"Spelling", NULL, N_("_Spelling") },
567 {"Options", NULL, N_("_Options") },
568 {"Tools", NULL, N_("_Tools") },
569 {"Help", NULL, N_("_Help") },
571 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
572 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
573 {"Message/---", NULL, "---" },
575 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
576 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
577 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
578 /* {"Message/---", NULL, "---" }, */
579 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
580 /* {"Message/---", NULL, "---" }, */
581 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
582 /* {"Message/---", NULL, "---" }, */
583 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
586 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
587 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
588 {"Edit/---", NULL, "---" },
590 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
591 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
592 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
594 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
595 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
596 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
597 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
599 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
601 {"Edit/Advanced", NULL, N_("A_dvanced") },
602 {"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*/
603 {"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*/
604 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
605 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
606 {"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*/
607 {"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*/
608 {"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*/
609 {"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*/
610 {"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*/
611 {"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*/
612 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
613 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
614 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
615 {"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*/
617 /* {"Edit/---", NULL, "---" }, */
618 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
620 /* {"Edit/---", NULL, "---" }, */
621 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
622 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
623 /* {"Edit/---", NULL, "---" }, */
624 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
627 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
628 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
629 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
630 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
632 {"Spelling/---", NULL, "---" },
633 {"Spelling/Options", NULL, N_("_Options") },
638 {"Options/ReplyMode", NULL, N_("Reply _mode") },
639 {"Options/---", NULL, "---" },
640 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
641 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
643 /* {"Options/---", NULL, "---" }, */
645 {"Options/Priority", NULL, N_("_Priority") },
647 {"Options/Encoding", NULL, N_("Character _encoding") },
648 {"Options/Encoding/---", NULL, "---" },
649 #define ENC_ACTION(cs_char,c_char,string) \
650 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
652 {"Options/Encoding/Western", NULL, N_("Western European") },
653 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
654 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
655 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
656 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
657 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
658 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
659 {"Options/Encoding/Korean", NULL, N_("Korean") },
660 {"Options/Encoding/Thai", NULL, N_("Thai") },
663 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
665 {"Tools/Template", NULL, N_("_Template") },
666 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
667 {"Tools/Actions", NULL, N_("Actio_ns") },
668 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
671 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
674 static GtkToggleActionEntry compose_toggle_entries[] =
676 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
677 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
678 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
679 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
680 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
681 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
682 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
685 static GtkRadioActionEntry compose_radio_rm_entries[] =
687 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
688 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
689 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
690 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
693 static GtkRadioActionEntry compose_radio_prio_entries[] =
695 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
696 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
697 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
698 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
699 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
702 static GtkRadioActionEntry compose_radio_enc_entries[] =
704 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
729 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
730 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
731 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
732 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
733 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
734 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
735 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
738 static GtkTargetEntry compose_mime_types[] =
740 {"text/uri-list", 0, 0},
741 {"UTF8_STRING", 0, 0},
745 static gboolean compose_put_existing_to_front(MsgInfo *info)
747 GList *compose_list = compose_get_compose_list();
751 for (elem = compose_list; elem != NULL && elem->data != NULL;
753 Compose *c = (Compose*)elem->data;
755 if (!c->targetinfo || !c->targetinfo->msgid ||
759 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
760 gtkut_window_popup(c->window);
768 static GdkColor quote_color1 =
769 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
770 static GdkColor quote_color2 =
771 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
772 static GdkColor quote_color3 =
773 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
775 static GdkColor quote_bgcolor1 =
776 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
777 static GdkColor quote_bgcolor2 =
778 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
779 static GdkColor quote_bgcolor3 =
780 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
782 static GdkColor signature_color = {
789 static GdkColor uri_color = {
796 static void compose_create_tags(GtkTextView *text, Compose *compose)
798 GtkTextBuffer *buffer;
799 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
800 #if !GTK_CHECK_VERSION(2, 24, 0)
807 buffer = gtk_text_view_get_buffer(text);
809 if (prefs_common.enable_color) {
810 /* grab the quote colors, converting from an int to a GdkColor */
811 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
813 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
815 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
817 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
819 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
821 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
823 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
825 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
828 signature_color = quote_color1 = quote_color2 = quote_color3 =
829 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
832 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
833 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
834 "foreground-gdk", "e_color1,
835 "paragraph-background-gdk", "e_bgcolor1,
837 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
838 "foreground-gdk", "e_color2,
839 "paragraph-background-gdk", "e_bgcolor2,
841 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
842 "foreground-gdk", "e_color3,
843 "paragraph-background-gdk", "e_bgcolor3,
846 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
847 "foreground-gdk", "e_color1,
849 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
850 "foreground-gdk", "e_color2,
852 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
853 "foreground-gdk", "e_color3,
857 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
858 "foreground-gdk", &signature_color,
861 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
862 "foreground-gdk", &uri_color,
864 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
865 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
867 color[0] = quote_color1;
868 color[1] = quote_color2;
869 color[2] = quote_color3;
870 color[3] = quote_bgcolor1;
871 color[4] = quote_bgcolor2;
872 color[5] = quote_bgcolor3;
873 color[6] = signature_color;
874 color[7] = uri_color;
875 #if !GTK_CHECK_VERSION(2, 24, 0)
876 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
877 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
879 for (i = 0; i < 8; i++) {
880 if (success[i] == FALSE) {
883 g_warning("Compose: color allocation failed.\n");
884 style = gtk_widget_get_style(GTK_WIDGET(text));
885 quote_color1 = quote_color2 = quote_color3 =
886 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
887 signature_color = uri_color = black;
893 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
896 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
899 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
901 return compose_generic_new(account, mailto, item, NULL, NULL);
904 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
906 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
909 #define SCROLL_TO_CURSOR(compose) { \
910 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
911 gtk_text_view_get_buffer( \
912 GTK_TEXT_VIEW(compose->text))); \
913 gtk_text_view_scroll_mark_onscreen( \
914 GTK_TEXT_VIEW(compose->text), \
918 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
921 if (folderidentifier) {
922 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
923 prefs_common.compose_save_to_history = add_history(
924 prefs_common.compose_save_to_history, folderidentifier);
925 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
926 prefs_common.compose_save_to_history);
929 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
930 if (folderidentifier)
931 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
933 gtk_entry_set_text(GTK_ENTRY(entry), "");
936 static gchar *compose_get_save_to(Compose *compose)
939 gchar *result = NULL;
940 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
941 result = gtk_editable_get_chars(entry, 0, -1);
944 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
945 prefs_common.compose_save_to_history = add_history(
946 prefs_common.compose_save_to_history, result);
947 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
948 prefs_common.compose_save_to_history);
953 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
954 GList *attach_files, GList *listAddress )
957 GtkTextView *textview;
958 GtkTextBuffer *textbuf;
960 const gchar *subject_format = NULL;
961 const gchar *body_format = NULL;
962 gchar *mailto_from = NULL;
963 PrefsAccount *mailto_account = NULL;
964 MsgInfo* dummyinfo = NULL;
965 gint cursor_pos = -1;
966 MailField mfield = NO_FIELD_PRESENT;
970 /* check if mailto defines a from */
971 if (mailto && *mailto != '\0') {
972 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
973 /* mailto defines a from, check if we can get account prefs from it,
974 if not, the account prefs will be guessed using other ways, but we'll keep
977 mailto_account = account_find_from_address(mailto_from, TRUE);
979 account = mailto_account;
982 /* if no account prefs set from mailto, set if from folder prefs (if any) */
983 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
984 account = account_find_from_id(item->prefs->default_account);
986 /* if no account prefs set, fallback to the current one */
987 if (!account) account = cur_account;
988 cm_return_val_if_fail(account != NULL, NULL);
990 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
992 /* override from name if mailto asked for it */
994 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
997 /* override from name according to folder properties */
998 if (item && item->prefs &&
999 item->prefs->compose_with_format &&
1000 item->prefs->compose_override_from_format &&
1001 *item->prefs->compose_override_from_format != '\0') {
1006 dummyinfo = compose_msginfo_new_from_compose(compose);
1008 /* decode \-escape sequences in the internal representation of the quote format */
1009 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1010 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1013 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1014 compose->gtkaspell);
1016 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1018 quote_fmt_scan_string(tmp);
1021 buf = quote_fmt_get_buffer();
1023 alertpanel_error(_("New message From format error."));
1025 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1026 quote_fmt_reset_vartable();
1031 compose->replyinfo = NULL;
1032 compose->fwdinfo = NULL;
1034 textview = GTK_TEXT_VIEW(compose->text);
1035 textbuf = gtk_text_view_get_buffer(textview);
1036 compose_create_tags(textview, compose);
1038 undo_block(compose->undostruct);
1040 compose_set_dictionaries_from_folder_prefs(compose, item);
1043 if (account->auto_sig)
1044 compose_insert_sig(compose, FALSE);
1045 gtk_text_buffer_get_start_iter(textbuf, &iter);
1046 gtk_text_buffer_place_cursor(textbuf, &iter);
1048 if (account->protocol != A_NNTP) {
1049 if (mailto && *mailto != '\0') {
1050 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1053 compose_set_folder_prefs(compose, item, TRUE);
1055 if (item && item->ret_rcpt) {
1056 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1059 if (mailto && *mailto != '\0') {
1060 if (!strchr(mailto, '@'))
1061 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1063 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1064 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1065 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1066 mfield = TO_FIELD_PRESENT;
1069 * CLAWS: just don't allow return receipt request, even if the user
1070 * may want to send an email. simple but foolproof.
1072 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1074 compose_add_field_list( compose, listAddress );
1076 if (item && item->prefs && item->prefs->compose_with_format) {
1077 subject_format = item->prefs->compose_subject_format;
1078 body_format = item->prefs->compose_body_format;
1079 } else if (account->compose_with_format) {
1080 subject_format = account->compose_subject_format;
1081 body_format = account->compose_body_format;
1082 } else if (prefs_common.compose_with_format) {
1083 subject_format = prefs_common.compose_subject_format;
1084 body_format = prefs_common.compose_body_format;
1087 if (subject_format || body_format) {
1090 && *subject_format != '\0' )
1092 gchar *subject = NULL;
1097 dummyinfo = compose_msginfo_new_from_compose(compose);
1099 /* decode \-escape sequences in the internal representation of the quote format */
1100 tmp = g_malloc(strlen(subject_format)+1);
1101 pref_get_unescaped_pref(tmp, subject_format);
1103 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1105 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1106 compose->gtkaspell);
1108 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1110 quote_fmt_scan_string(tmp);
1113 buf = quote_fmt_get_buffer();
1115 alertpanel_error(_("New message subject format error."));
1117 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1118 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1119 quote_fmt_reset_vartable();
1123 mfield = SUBJECT_FIELD_PRESENT;
1127 && *body_format != '\0' )
1130 GtkTextBuffer *buffer;
1131 GtkTextIter start, end;
1135 dummyinfo = compose_msginfo_new_from_compose(compose);
1137 text = GTK_TEXT_VIEW(compose->text);
1138 buffer = gtk_text_view_get_buffer(text);
1139 gtk_text_buffer_get_start_iter(buffer, &start);
1140 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1141 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1143 compose_quote_fmt(compose, dummyinfo,
1145 NULL, tmp, FALSE, TRUE,
1146 _("The body of the \"New message\" template has an error at line %d."));
1147 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1148 quote_fmt_reset_vartable();
1152 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1153 gtkaspell_highlight_all(compose->gtkaspell);
1155 mfield = BODY_FIELD_PRESENT;
1159 procmsg_msginfo_free( dummyinfo );
1165 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1166 ainfo = (AttachInfo *) curr->data;
1167 compose_attach_append(compose, ainfo->file, ainfo->name,
1168 ainfo->content_type, ainfo->charset);
1172 compose_show_first_last_header(compose, TRUE);
1174 /* Set save folder */
1175 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1176 gchar *folderidentifier;
1178 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1179 folderidentifier = folder_item_get_identifier(item);
1180 compose_set_save_to(compose, folderidentifier);
1181 g_free(folderidentifier);
1184 /* Place cursor according to provided input (mfield) */
1186 case NO_FIELD_PRESENT:
1187 if (compose->header_last)
1188 gtk_widget_grab_focus(compose->header_last->entry);
1190 case TO_FIELD_PRESENT:
1191 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1193 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1196 gtk_widget_grab_focus(compose->subject_entry);
1198 case SUBJECT_FIELD_PRESENT:
1199 textview = GTK_TEXT_VIEW(compose->text);
1202 textbuf = gtk_text_view_get_buffer(textview);
1205 mark = gtk_text_buffer_get_insert(textbuf);
1206 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1207 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1209 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1210 * only defers where it comes to the variable body
1211 * is not null. If no body is present compose->text
1212 * will be null in which case you cannot place the
1213 * cursor inside the component so. An empty component
1214 * is therefore created before placing the cursor
1216 case BODY_FIELD_PRESENT:
1217 cursor_pos = quote_fmt_get_cursor_pos();
1218 if (cursor_pos == -1)
1219 gtk_widget_grab_focus(compose->header_last->entry);
1221 gtk_widget_grab_focus(compose->text);
1225 undo_unblock(compose->undostruct);
1227 if (prefs_common.auto_exteditor)
1228 compose_exec_ext_editor(compose);
1230 compose->draft_timeout_tag = -1;
1231 SCROLL_TO_CURSOR(compose);
1233 compose->modified = FALSE;
1234 compose_set_title(compose);
1236 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1241 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1242 gboolean override_pref, const gchar *system)
1244 const gchar *privacy = NULL;
1246 cm_return_if_fail(compose != NULL);
1247 cm_return_if_fail(account != NULL);
1249 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1254 else if (account->default_privacy_system
1255 && strlen(account->default_privacy_system)) {
1256 privacy = account->default_privacy_system;
1258 GSList *privacy_avail = privacy_get_system_ids();
1259 if (privacy_avail && g_slist_length(privacy_avail)) {
1260 privacy = (gchar *)(privacy_avail->data);
1263 if (privacy != NULL) {
1265 g_free(compose->privacy_system);
1266 compose->privacy_system = NULL;
1268 if (compose->privacy_system == NULL)
1269 compose->privacy_system = g_strdup(privacy);
1270 else if (*(compose->privacy_system) == '\0') {
1271 g_free(compose->privacy_system);
1272 compose->privacy_system = g_strdup(privacy);
1274 compose_update_privacy_system_menu_item(compose, FALSE);
1275 compose_use_encryption(compose, TRUE);
1279 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1281 const gchar *privacy = NULL;
1285 else if (account->default_privacy_system
1286 && strlen(account->default_privacy_system)) {
1287 privacy = account->default_privacy_system;
1289 GSList *privacy_avail = privacy_get_system_ids();
1290 if (privacy_avail && g_slist_length(privacy_avail)) {
1291 privacy = (gchar *)(privacy_avail->data);
1295 if (privacy != NULL) {
1297 g_free(compose->privacy_system);
1298 compose->privacy_system = NULL;
1300 if (compose->privacy_system == NULL)
1301 compose->privacy_system = g_strdup(privacy);
1302 compose_update_privacy_system_menu_item(compose, FALSE);
1303 compose_use_signing(compose, TRUE);
1307 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1311 Compose *compose = NULL;
1313 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1315 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1316 cm_return_val_if_fail(msginfo != NULL, NULL);
1318 list_len = g_slist_length(msginfo_list);
1322 case COMPOSE_REPLY_TO_ADDRESS:
1323 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1324 FALSE, prefs_common.default_reply_list, FALSE, body);
1326 case COMPOSE_REPLY_WITH_QUOTE:
1327 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1328 FALSE, prefs_common.default_reply_list, FALSE, body);
1330 case COMPOSE_REPLY_WITHOUT_QUOTE:
1331 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1332 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1334 case COMPOSE_REPLY_TO_SENDER:
1335 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1336 FALSE, FALSE, TRUE, body);
1338 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1339 compose = compose_followup_and_reply_to(msginfo,
1340 COMPOSE_QUOTE_CHECK,
1341 FALSE, FALSE, body);
1343 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1344 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1345 FALSE, FALSE, TRUE, body);
1347 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1348 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1349 FALSE, FALSE, TRUE, NULL);
1351 case COMPOSE_REPLY_TO_ALL:
1352 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1353 TRUE, FALSE, FALSE, body);
1355 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1356 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1357 TRUE, FALSE, FALSE, body);
1359 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1360 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1361 TRUE, FALSE, FALSE, NULL);
1363 case COMPOSE_REPLY_TO_LIST:
1364 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1365 FALSE, TRUE, FALSE, body);
1367 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1368 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1369 FALSE, TRUE, FALSE, body);
1371 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1372 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1373 FALSE, TRUE, FALSE, NULL);
1375 case COMPOSE_FORWARD:
1376 if (prefs_common.forward_as_attachment) {
1377 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1380 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1384 case COMPOSE_FORWARD_INLINE:
1385 /* check if we reply to more than one Message */
1386 if (list_len == 1) {
1387 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1390 /* more messages FALL THROUGH */
1391 case COMPOSE_FORWARD_AS_ATTACH:
1392 compose = compose_forward_multiple(NULL, msginfo_list);
1394 case COMPOSE_REDIRECT:
1395 compose = compose_redirect(NULL, msginfo, FALSE);
1398 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1401 if (compose == NULL) {
1402 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1406 compose->rmode = mode;
1407 switch (compose->rmode) {
1409 case COMPOSE_REPLY_WITH_QUOTE:
1410 case COMPOSE_REPLY_WITHOUT_QUOTE:
1411 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1412 debug_print("reply mode Normal\n");
1413 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1414 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1416 case COMPOSE_REPLY_TO_SENDER:
1417 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1418 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1419 debug_print("reply mode Sender\n");
1420 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1422 case COMPOSE_REPLY_TO_ALL:
1423 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1424 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1425 debug_print("reply mode All\n");
1426 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1428 case COMPOSE_REPLY_TO_LIST:
1429 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1430 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1431 debug_print("reply mode List\n");
1432 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1434 case COMPOSE_REPLY_TO_ADDRESS:
1435 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1443 static Compose *compose_reply(MsgInfo *msginfo,
1444 ComposeQuoteMode quote_mode,
1450 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1451 to_sender, FALSE, body);
1454 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1455 ComposeQuoteMode quote_mode,
1460 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1461 to_sender, TRUE, body);
1464 static void compose_extract_original_charset(Compose *compose)
1466 MsgInfo *info = NULL;
1467 if (compose->replyinfo) {
1468 info = compose->replyinfo;
1469 } else if (compose->fwdinfo) {
1470 info = compose->fwdinfo;
1471 } else if (compose->targetinfo) {
1472 info = compose->targetinfo;
1475 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1476 MimeInfo *partinfo = mimeinfo;
1477 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1478 partinfo = procmime_mimeinfo_next(partinfo);
1480 compose->orig_charset =
1481 g_strdup(procmime_mimeinfo_get_parameter(
1482 partinfo, "charset"));
1484 procmime_mimeinfo_free_all(mimeinfo);
1488 #define SIGNAL_BLOCK(buffer) { \
1489 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1490 G_CALLBACK(compose_changed_cb), \
1492 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1493 G_CALLBACK(text_inserted), \
1497 #define SIGNAL_UNBLOCK(buffer) { \
1498 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1499 G_CALLBACK(compose_changed_cb), \
1501 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1502 G_CALLBACK(text_inserted), \
1506 static Compose *compose_generic_reply(MsgInfo *msginfo,
1507 ComposeQuoteMode quote_mode,
1508 gboolean to_all, gboolean to_ml,
1510 gboolean followup_and_reply_to,
1514 PrefsAccount *account = NULL;
1515 GtkTextView *textview;
1516 GtkTextBuffer *textbuf;
1517 gboolean quote = FALSE;
1518 const gchar *qmark = NULL;
1519 const gchar *body_fmt = NULL;
1520 gchar *s_system = NULL;
1522 cm_return_val_if_fail(msginfo != NULL, NULL);
1523 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1525 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1527 cm_return_val_if_fail(account != NULL, NULL);
1529 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1531 compose->updating = TRUE;
1533 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1534 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1536 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1537 if (!compose->replyinfo)
1538 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1540 compose_extract_original_charset(compose);
1542 if (msginfo->folder && msginfo->folder->ret_rcpt)
1543 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1545 /* Set save folder */
1546 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1547 gchar *folderidentifier;
1549 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1550 folderidentifier = folder_item_get_identifier(msginfo->folder);
1551 compose_set_save_to(compose, folderidentifier);
1552 g_free(folderidentifier);
1555 if (compose_parse_header(compose, msginfo) < 0) {
1556 compose->updating = FALSE;
1557 compose_destroy(compose);
1561 /* override from name according to folder properties */
1562 if (msginfo->folder && msginfo->folder->prefs &&
1563 msginfo->folder->prefs->reply_with_format &&
1564 msginfo->folder->prefs->reply_override_from_format &&
1565 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1570 /* decode \-escape sequences in the internal representation of the quote format */
1571 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1572 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1575 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1576 compose->gtkaspell);
1578 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1580 quote_fmt_scan_string(tmp);
1583 buf = quote_fmt_get_buffer();
1585 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1587 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1588 quote_fmt_reset_vartable();
1593 textview = (GTK_TEXT_VIEW(compose->text));
1594 textbuf = gtk_text_view_get_buffer(textview);
1595 compose_create_tags(textview, compose);
1597 undo_block(compose->undostruct);
1599 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1600 gtkaspell_block_check(compose->gtkaspell);
1603 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1604 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1605 /* use the reply format of folder (if enabled), or the account's one
1606 (if enabled) or fallback to the global reply format, which is always
1607 enabled (even if empty), and use the relevant quotemark */
1609 if (msginfo->folder && msginfo->folder->prefs &&
1610 msginfo->folder->prefs->reply_with_format) {
1611 qmark = msginfo->folder->prefs->reply_quotemark;
1612 body_fmt = msginfo->folder->prefs->reply_body_format;
1614 } else if (account->reply_with_format) {
1615 qmark = account->reply_quotemark;
1616 body_fmt = account->reply_body_format;
1619 qmark = prefs_common.quotemark;
1620 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1621 body_fmt = gettext(prefs_common.quotefmt);
1628 /* empty quotemark is not allowed */
1629 if (qmark == NULL || *qmark == '\0')
1631 compose_quote_fmt(compose, compose->replyinfo,
1632 body_fmt, qmark, body, FALSE, TRUE,
1633 _("The body of the \"Reply\" template has an error at line %d."));
1634 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1635 quote_fmt_reset_vartable();
1638 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1639 compose_force_encryption(compose, account, FALSE, s_system);
1642 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1643 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1644 compose_force_signing(compose, account, s_system);
1648 SIGNAL_BLOCK(textbuf);
1650 if (account->auto_sig)
1651 compose_insert_sig(compose, FALSE);
1653 compose_wrap_all(compose);
1656 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1657 gtkaspell_highlight_all(compose->gtkaspell);
1658 gtkaspell_unblock_check(compose->gtkaspell);
1660 SIGNAL_UNBLOCK(textbuf);
1662 gtk_widget_grab_focus(compose->text);
1664 undo_unblock(compose->undostruct);
1666 if (prefs_common.auto_exteditor)
1667 compose_exec_ext_editor(compose);
1669 compose->modified = FALSE;
1670 compose_set_title(compose);
1672 compose->updating = FALSE;
1673 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1674 SCROLL_TO_CURSOR(compose);
1676 if (compose->deferred_destroy) {
1677 compose_destroy(compose);
1685 #define INSERT_FW_HEADER(var, hdr) \
1686 if (msginfo->var && *msginfo->var) { \
1687 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1688 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1689 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1692 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1693 gboolean as_attach, const gchar *body,
1694 gboolean no_extedit,
1698 GtkTextView *textview;
1699 GtkTextBuffer *textbuf;
1700 gint cursor_pos = -1;
1703 cm_return_val_if_fail(msginfo != NULL, NULL);
1704 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1707 !(account = compose_guess_forward_account_from_msginfo
1709 account = cur_account;
1711 if (!prefs_common.forward_as_attachment)
1712 mode = COMPOSE_FORWARD_INLINE;
1714 mode = COMPOSE_FORWARD;
1715 compose = compose_create(account, msginfo->folder, mode, batch);
1717 compose->updating = TRUE;
1718 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1719 if (!compose->fwdinfo)
1720 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1722 compose_extract_original_charset(compose);
1724 if (msginfo->subject && *msginfo->subject) {
1725 gchar *buf, *buf2, *p;
1727 buf = p = g_strdup(msginfo->subject);
1728 p += subject_get_prefix_length(p);
1729 memmove(buf, p, strlen(p) + 1);
1731 buf2 = g_strdup_printf("Fw: %s", buf);
1732 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1738 /* override from name according to folder properties */
1739 if (msginfo->folder && msginfo->folder->prefs &&
1740 msginfo->folder->prefs->forward_with_format &&
1741 msginfo->folder->prefs->forward_override_from_format &&
1742 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1746 MsgInfo *full_msginfo = NULL;
1749 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1751 full_msginfo = procmsg_msginfo_copy(msginfo);
1753 /* decode \-escape sequences in the internal representation of the quote format */
1754 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1755 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1758 gtkaspell_block_check(compose->gtkaspell);
1759 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1760 compose->gtkaspell);
1762 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1764 quote_fmt_scan_string(tmp);
1767 buf = quote_fmt_get_buffer();
1769 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1771 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1772 quote_fmt_reset_vartable();
1775 procmsg_msginfo_free(full_msginfo);
1778 textview = GTK_TEXT_VIEW(compose->text);
1779 textbuf = gtk_text_view_get_buffer(textview);
1780 compose_create_tags(textview, compose);
1782 undo_block(compose->undostruct);
1786 msgfile = procmsg_get_message_file(msginfo);
1787 if (!is_file_exist(msgfile))
1788 g_warning("%s: file not exist\n", msgfile);
1790 compose_attach_append(compose, msgfile, msgfile,
1791 "message/rfc822", NULL);
1795 const gchar *qmark = NULL;
1796 const gchar *body_fmt = NULL;
1797 MsgInfo *full_msginfo;
1799 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1800 body_fmt = gettext(prefs_common.fw_quotefmt);
1804 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1806 full_msginfo = procmsg_msginfo_copy(msginfo);
1808 /* use the forward format of folder (if enabled), or the account's one
1809 (if enabled) or fallback to the global forward format, which is always
1810 enabled (even if empty), and use the relevant quotemark */
1811 if (msginfo->folder && msginfo->folder->prefs &&
1812 msginfo->folder->prefs->forward_with_format) {
1813 qmark = msginfo->folder->prefs->forward_quotemark;
1814 body_fmt = msginfo->folder->prefs->forward_body_format;
1816 } else if (account->forward_with_format) {
1817 qmark = account->forward_quotemark;
1818 body_fmt = account->forward_body_format;
1821 qmark = prefs_common.fw_quotemark;
1822 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1823 body_fmt = gettext(prefs_common.fw_quotefmt);
1828 /* empty quotemark is not allowed */
1829 if (qmark == NULL || *qmark == '\0')
1832 compose_quote_fmt(compose, full_msginfo,
1833 body_fmt, qmark, body, FALSE, TRUE,
1834 _("The body of the \"Forward\" template has an error at line %d."));
1835 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1836 quote_fmt_reset_vartable();
1837 compose_attach_parts(compose, msginfo);
1839 procmsg_msginfo_free(full_msginfo);
1842 SIGNAL_BLOCK(textbuf);
1844 if (account->auto_sig)
1845 compose_insert_sig(compose, FALSE);
1847 compose_wrap_all(compose);
1850 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1851 gtkaspell_highlight_all(compose->gtkaspell);
1852 gtkaspell_unblock_check(compose->gtkaspell);
1854 SIGNAL_UNBLOCK(textbuf);
1856 cursor_pos = quote_fmt_get_cursor_pos();
1857 if (cursor_pos == -1)
1858 gtk_widget_grab_focus(compose->header_last->entry);
1860 gtk_widget_grab_focus(compose->text);
1862 if (!no_extedit && prefs_common.auto_exteditor)
1863 compose_exec_ext_editor(compose);
1866 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1867 gchar *folderidentifier;
1869 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1870 folderidentifier = folder_item_get_identifier(msginfo->folder);
1871 compose_set_save_to(compose, folderidentifier);
1872 g_free(folderidentifier);
1875 undo_unblock(compose->undostruct);
1877 compose->modified = FALSE;
1878 compose_set_title(compose);
1880 compose->updating = FALSE;
1881 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1882 SCROLL_TO_CURSOR(compose);
1884 if (compose->deferred_destroy) {
1885 compose_destroy(compose);
1889 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1894 #undef INSERT_FW_HEADER
1896 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1899 GtkTextView *textview;
1900 GtkTextBuffer *textbuf;
1904 gboolean single_mail = TRUE;
1906 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1908 if (g_slist_length(msginfo_list) > 1)
1909 single_mail = FALSE;
1911 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1912 if (((MsgInfo *)msginfo->data)->folder == NULL)
1915 /* guess account from first selected message */
1917 !(account = compose_guess_forward_account_from_msginfo
1918 (msginfo_list->data)))
1919 account = cur_account;
1921 cm_return_val_if_fail(account != NULL, NULL);
1923 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1924 if (msginfo->data) {
1925 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1926 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1930 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1931 g_warning("no msginfo_list");
1935 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1937 compose->updating = TRUE;
1939 /* override from name according to folder properties */
1940 if (msginfo_list->data) {
1941 MsgInfo *msginfo = msginfo_list->data;
1943 if (msginfo->folder && msginfo->folder->prefs &&
1944 msginfo->folder->prefs->forward_with_format &&
1945 msginfo->folder->prefs->forward_override_from_format &&
1946 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1951 /* decode \-escape sequences in the internal representation of the quote format */
1952 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1953 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1956 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1957 compose->gtkaspell);
1959 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1961 quote_fmt_scan_string(tmp);
1964 buf = quote_fmt_get_buffer();
1966 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1968 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1969 quote_fmt_reset_vartable();
1975 textview = GTK_TEXT_VIEW(compose->text);
1976 textbuf = gtk_text_view_get_buffer(textview);
1977 compose_create_tags(textview, compose);
1979 undo_block(compose->undostruct);
1980 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1981 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1983 if (!is_file_exist(msgfile))
1984 g_warning("%s: file not exist\n", msgfile);
1986 compose_attach_append(compose, msgfile, msgfile,
1987 "message/rfc822", NULL);
1992 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1993 if (info->subject && *info->subject) {
1994 gchar *buf, *buf2, *p;
1996 buf = p = g_strdup(info->subject);
1997 p += subject_get_prefix_length(p);
1998 memmove(buf, p, strlen(p) + 1);
2000 buf2 = g_strdup_printf("Fw: %s", buf);
2001 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2007 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2008 _("Fw: multiple emails"));
2011 SIGNAL_BLOCK(textbuf);
2013 if (account->auto_sig)
2014 compose_insert_sig(compose, FALSE);
2016 compose_wrap_all(compose);
2018 SIGNAL_UNBLOCK(textbuf);
2020 gtk_text_buffer_get_start_iter(textbuf, &iter);
2021 gtk_text_buffer_place_cursor(textbuf, &iter);
2023 gtk_widget_grab_focus(compose->header_last->entry);
2024 undo_unblock(compose->undostruct);
2025 compose->modified = FALSE;
2026 compose_set_title(compose);
2028 compose->updating = FALSE;
2029 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2030 SCROLL_TO_CURSOR(compose);
2032 if (compose->deferred_destroy) {
2033 compose_destroy(compose);
2037 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2042 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2044 GtkTextIter start = *iter;
2045 GtkTextIter end_iter;
2046 int start_pos = gtk_text_iter_get_offset(&start);
2048 if (!compose->account->sig_sep)
2051 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2052 start_pos+strlen(compose->account->sig_sep));
2054 /* check sig separator */
2055 str = gtk_text_iter_get_text(&start, &end_iter);
2056 if (!strcmp(str, compose->account->sig_sep)) {
2058 /* check end of line (\n) */
2059 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2060 start_pos+strlen(compose->account->sig_sep));
2061 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2062 start_pos+strlen(compose->account->sig_sep)+1);
2063 tmp = gtk_text_iter_get_text(&start, &end_iter);
2064 if (!strcmp(tmp,"\n")) {
2076 static void compose_colorize_signature(Compose *compose)
2078 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2080 GtkTextIter end_iter;
2081 gtk_text_buffer_get_start_iter(buffer, &iter);
2082 while (gtk_text_iter_forward_line(&iter))
2083 if (compose_is_sig_separator(compose, buffer, &iter)) {
2084 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2085 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2089 #define BLOCK_WRAP() { \
2090 prev_autowrap = compose->autowrap; \
2091 buffer = gtk_text_view_get_buffer( \
2092 GTK_TEXT_VIEW(compose->text)); \
2093 compose->autowrap = FALSE; \
2095 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2096 G_CALLBACK(compose_changed_cb), \
2098 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2099 G_CALLBACK(text_inserted), \
2102 #define UNBLOCK_WRAP() { \
2103 compose->autowrap = prev_autowrap; \
2104 if (compose->autowrap) { \
2105 gint old = compose->draft_timeout_tag; \
2106 compose->draft_timeout_tag = -2; \
2107 compose_wrap_all(compose); \
2108 compose->draft_timeout_tag = old; \
2111 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2112 G_CALLBACK(compose_changed_cb), \
2114 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2115 G_CALLBACK(text_inserted), \
2119 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2121 Compose *compose = NULL;
2122 PrefsAccount *account = NULL;
2123 GtkTextView *textview;
2124 GtkTextBuffer *textbuf;
2128 gchar buf[BUFFSIZE];
2129 gboolean use_signing = FALSE;
2130 gboolean use_encryption = FALSE;
2131 gchar *privacy_system = NULL;
2132 int priority = PRIORITY_NORMAL;
2133 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2134 gboolean autowrap = prefs_common.autowrap;
2135 gboolean autoindent = prefs_common.auto_indent;
2136 HeaderEntry *manual_headers = NULL;
2138 cm_return_val_if_fail(msginfo != NULL, NULL);
2139 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2141 if (compose_put_existing_to_front(msginfo)) {
2145 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2146 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2147 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2148 gchar queueheader_buf[BUFFSIZE];
2151 /* Select Account from queue headers */
2152 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2153 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2154 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2155 account = account_find_from_id(id);
2157 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2158 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2159 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2160 account = account_find_from_id(id);
2162 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2163 sizeof(queueheader_buf), "NAID:")) {
2164 id = atoi(&queueheader_buf[strlen("NAID:")]);
2165 account = account_find_from_id(id);
2167 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2168 sizeof(queueheader_buf), "MAID:")) {
2169 id = atoi(&queueheader_buf[strlen("MAID:")]);
2170 account = account_find_from_id(id);
2172 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2173 sizeof(queueheader_buf), "S:")) {
2174 account = account_find_from_address(queueheader_buf, FALSE);
2176 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2177 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2178 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2179 use_signing = param;
2182 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2183 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2184 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2185 use_signing = param;
2188 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2189 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2190 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2191 use_encryption = param;
2193 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2194 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2195 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2196 use_encryption = param;
2198 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2199 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2200 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2203 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2204 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2205 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2208 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2209 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2210 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2212 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2213 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2214 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2216 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2217 sizeof(queueheader_buf), "X-Priority: ")) {
2218 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2221 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2222 sizeof(queueheader_buf), "RMID:")) {
2223 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2224 if (tokens[0] && tokens[1] && tokens[2]) {
2225 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2226 if (orig_item != NULL) {
2227 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2232 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2233 sizeof(queueheader_buf), "FMID:")) {
2234 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2235 if (tokens[0] && tokens[1] && tokens[2]) {
2236 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2237 if (orig_item != NULL) {
2238 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2243 /* Get manual headers */
2244 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2245 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2246 if (*listmh != '\0') {
2247 debug_print("Got manual headers: %s\n", listmh);
2248 manual_headers = procheader_entries_from_str(listmh);
2253 account = msginfo->folder->folder->account;
2256 if (!account && prefs_common.reedit_account_autosel) {
2257 gchar from[BUFFSIZE];
2258 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2259 extract_address(from);
2260 account = account_find_from_address(from, FALSE);
2264 account = cur_account;
2266 cm_return_val_if_fail(account != NULL, NULL);
2268 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2270 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2271 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2272 compose->autowrap = autowrap;
2273 compose->replyinfo = replyinfo;
2274 compose->fwdinfo = fwdinfo;
2276 compose->updating = TRUE;
2277 compose->priority = priority;
2279 if (privacy_system != NULL) {
2280 compose->privacy_system = privacy_system;
2281 compose_use_signing(compose, use_signing);
2282 compose_use_encryption(compose, use_encryption);
2283 compose_update_privacy_system_menu_item(compose, FALSE);
2285 activate_privacy_system(compose, account, FALSE);
2288 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2290 compose_extract_original_charset(compose);
2292 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2293 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2294 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2295 gchar queueheader_buf[BUFFSIZE];
2297 /* Set message save folder */
2298 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2299 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2300 compose_set_save_to(compose, &queueheader_buf[4]);
2302 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2303 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2305 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2310 if (compose_parse_header(compose, msginfo) < 0) {
2311 compose->updating = FALSE;
2312 compose_destroy(compose);
2315 compose_reedit_set_entry(compose, msginfo);
2317 textview = GTK_TEXT_VIEW(compose->text);
2318 textbuf = gtk_text_view_get_buffer(textview);
2319 compose_create_tags(textview, compose);
2321 mark = gtk_text_buffer_get_insert(textbuf);
2322 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2324 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2325 G_CALLBACK(compose_changed_cb),
2328 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2329 fp = procmime_get_first_encrypted_text_content(msginfo);
2331 compose_force_encryption(compose, account, TRUE, NULL);
2334 fp = procmime_get_first_text_content(msginfo);
2337 g_warning("Can't get text part\n");
2341 gboolean prev_autowrap = compose->autowrap;
2342 GtkTextBuffer *buffer = textbuf;
2344 while (fgets(buf, sizeof(buf), fp) != NULL) {
2346 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2352 compose_attach_parts(compose, msginfo);
2354 compose_colorize_signature(compose);
2356 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2357 G_CALLBACK(compose_changed_cb),
2360 if (manual_headers != NULL) {
2361 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2362 procheader_entries_free(manual_headers);
2363 compose->updating = FALSE;
2364 compose_destroy(compose);
2367 procheader_entries_free(manual_headers);
2370 gtk_widget_grab_focus(compose->text);
2372 if (prefs_common.auto_exteditor) {
2373 compose_exec_ext_editor(compose);
2375 compose->modified = FALSE;
2376 compose_set_title(compose);
2378 compose->updating = FALSE;
2379 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2380 SCROLL_TO_CURSOR(compose);
2382 if (compose->deferred_destroy) {
2383 compose_destroy(compose);
2387 compose->sig_str = account_get_signature_str(compose->account);
2389 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2394 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2401 cm_return_val_if_fail(msginfo != NULL, NULL);
2404 account = account_get_reply_account(msginfo,
2405 prefs_common.reply_account_autosel);
2406 cm_return_val_if_fail(account != NULL, NULL);
2408 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2410 compose->updating = TRUE;
2412 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2413 compose->replyinfo = NULL;
2414 compose->fwdinfo = NULL;
2416 compose_show_first_last_header(compose, TRUE);
2418 gtk_widget_grab_focus(compose->header_last->entry);
2420 filename = procmsg_get_message_file(msginfo);
2422 if (filename == NULL) {
2423 compose->updating = FALSE;
2424 compose_destroy(compose);
2429 compose->redirect_filename = filename;
2431 /* Set save folder */
2432 item = msginfo->folder;
2433 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2434 gchar *folderidentifier;
2436 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2437 folderidentifier = folder_item_get_identifier(item);
2438 compose_set_save_to(compose, folderidentifier);
2439 g_free(folderidentifier);
2442 compose_attach_parts(compose, msginfo);
2444 if (msginfo->subject)
2445 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2447 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2449 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2450 _("The body of the \"Redirect\" template has an error at line %d."));
2451 quote_fmt_reset_vartable();
2452 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2454 compose_colorize_signature(compose);
2457 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2458 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2459 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2461 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2462 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2463 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2464 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2465 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2466 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2467 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2468 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2470 if (compose->toolbar->draft_btn)
2471 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2472 if (compose->toolbar->insert_btn)
2473 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2474 if (compose->toolbar->attach_btn)
2475 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2476 if (compose->toolbar->sig_btn)
2477 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2478 if (compose->toolbar->exteditor_btn)
2479 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2480 if (compose->toolbar->linewrap_current_btn)
2481 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2482 if (compose->toolbar->linewrap_all_btn)
2483 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2485 compose->modified = FALSE;
2486 compose_set_title(compose);
2487 compose->updating = FALSE;
2488 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2489 SCROLL_TO_CURSOR(compose);
2491 if (compose->deferred_destroy) {
2492 compose_destroy(compose);
2496 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2501 GList *compose_get_compose_list(void)
2503 return compose_list;
2506 void compose_entry_append(Compose *compose, const gchar *address,
2507 ComposeEntryType type, ComposePrefType pref_type)
2509 const gchar *header;
2511 gboolean in_quote = FALSE;
2512 if (!address || *address == '\0') return;
2519 header = N_("Bcc:");
2521 case COMPOSE_REPLYTO:
2522 header = N_("Reply-To:");
2524 case COMPOSE_NEWSGROUPS:
2525 header = N_("Newsgroups:");
2527 case COMPOSE_FOLLOWUPTO:
2528 header = N_( "Followup-To:");
2530 case COMPOSE_INREPLYTO:
2531 header = N_( "In-Reply-To:");
2538 header = prefs_common_translated_header_name(header);
2540 cur = begin = (gchar *)address;
2542 /* we separate the line by commas, but not if we're inside a quoted
2544 while (*cur != '\0') {
2546 in_quote = !in_quote;
2547 if (*cur == ',' && !in_quote) {
2548 gchar *tmp = g_strdup(begin);
2550 tmp[cur-begin]='\0';
2553 while (*tmp == ' ' || *tmp == '\t')
2555 compose_add_header_entry(compose, header, tmp, pref_type);
2562 gchar *tmp = g_strdup(begin);
2564 tmp[cur-begin]='\0';
2567 while (*tmp == ' ' || *tmp == '\t')
2569 compose_add_header_entry(compose, header, tmp, pref_type);
2574 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2576 #if !GTK_CHECK_VERSION(3, 0, 0)
2577 static GdkColor yellow;
2578 static GdkColor black;
2579 static gboolean yellow_initialised = FALSE;
2581 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2582 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2587 #if !GTK_CHECK_VERSION(3, 0, 0)
2588 if (!yellow_initialised) {
2589 gdk_color_parse("#f5f6be", &yellow);
2590 gdk_color_parse("#000000", &black);
2591 yellow_initialised = gdk_colormap_alloc_color(
2592 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2593 yellow_initialised &= gdk_colormap_alloc_color(
2594 gdk_colormap_get_system(), &black, FALSE, TRUE);
2598 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2599 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2600 if (gtk_entry_get_text(entry) &&
2601 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2602 #if !GTK_CHECK_VERSION(3, 0, 0)
2603 if (yellow_initialised) {
2605 gtk_widget_modify_base(
2606 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2607 GTK_STATE_NORMAL, &yellow);
2608 gtk_widget_modify_text(
2609 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2610 GTK_STATE_NORMAL, &black);
2611 #if !GTK_CHECK_VERSION(3, 0, 0)
2618 void compose_toolbar_cb(gint action, gpointer data)
2620 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2621 Compose *compose = (Compose*)toolbar_item->parent;
2623 cm_return_if_fail(compose != NULL);
2627 compose_send_cb(NULL, compose);
2630 compose_send_later_cb(NULL, compose);
2633 compose_draft(compose, COMPOSE_QUIT_EDITING);
2636 compose_insert_file_cb(NULL, compose);
2639 compose_attach_cb(NULL, compose);
2642 compose_insert_sig(compose, FALSE);
2645 compose_ext_editor_cb(NULL, compose);
2647 case A_LINEWRAP_CURRENT:
2648 compose_beautify_paragraph(compose, NULL, TRUE);
2650 case A_LINEWRAP_ALL:
2651 compose_wrap_all_full(compose, TRUE);
2654 compose_address_cb(NULL, compose);
2657 case A_CHECK_SPELLING:
2658 compose_check_all(NULL, compose);
2666 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2671 gchar *subject = NULL;
2675 gchar **attach = NULL;
2676 gchar *inreplyto = NULL;
2677 MailField mfield = NO_FIELD_PRESENT;
2679 /* get mailto parts but skip from */
2680 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2683 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2684 mfield = TO_FIELD_PRESENT;
2687 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2689 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2691 if (!g_utf8_validate (subject, -1, NULL)) {
2692 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2693 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2696 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2698 mfield = SUBJECT_FIELD_PRESENT;
2701 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2702 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2705 gboolean prev_autowrap = compose->autowrap;
2707 compose->autowrap = FALSE;
2709 mark = gtk_text_buffer_get_insert(buffer);
2710 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2712 if (!g_utf8_validate (body, -1, NULL)) {
2713 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2714 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2717 gtk_text_buffer_insert(buffer, &iter, body, -1);
2719 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2721 compose->autowrap = prev_autowrap;
2722 if (compose->autowrap)
2723 compose_wrap_all(compose);
2724 mfield = BODY_FIELD_PRESENT;
2728 gint i = 0, att = 0;
2729 gchar *warn_files = NULL;
2730 while (attach[i] != NULL) {
2731 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2732 if (utf8_filename) {
2733 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2734 gchar *tmp = g_strdup_printf("%s%s\n",
2735 warn_files?warn_files:"",
2741 g_free(utf8_filename);
2743 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2748 alertpanel_notice(ngettext(
2749 "The following file has been attached: \n%s",
2750 "The following files have been attached: \n%s", att), warn_files);
2755 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2768 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2770 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2771 {"Cc:", NULL, TRUE},
2772 {"References:", NULL, FALSE},
2773 {"Bcc:", NULL, TRUE},
2774 {"Newsgroups:", NULL, TRUE},
2775 {"Followup-To:", NULL, TRUE},
2776 {"List-Post:", NULL, FALSE},
2777 {"X-Priority:", NULL, FALSE},
2778 {NULL, NULL, FALSE}};
2794 cm_return_val_if_fail(msginfo != NULL, -1);
2796 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2797 procheader_get_header_fields(fp, hentry);
2800 if (hentry[H_REPLY_TO].body != NULL) {
2801 if (hentry[H_REPLY_TO].body[0] != '\0') {
2803 conv_unmime_header(hentry[H_REPLY_TO].body,
2806 g_free(hentry[H_REPLY_TO].body);
2807 hentry[H_REPLY_TO].body = NULL;
2809 if (hentry[H_CC].body != NULL) {
2810 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2811 g_free(hentry[H_CC].body);
2812 hentry[H_CC].body = NULL;
2814 if (hentry[H_REFERENCES].body != NULL) {
2815 if (compose->mode == COMPOSE_REEDIT)
2816 compose->references = hentry[H_REFERENCES].body;
2818 compose->references = compose_parse_references
2819 (hentry[H_REFERENCES].body, msginfo->msgid);
2820 g_free(hentry[H_REFERENCES].body);
2822 hentry[H_REFERENCES].body = NULL;
2824 if (hentry[H_BCC].body != NULL) {
2825 if (compose->mode == COMPOSE_REEDIT)
2827 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2828 g_free(hentry[H_BCC].body);
2829 hentry[H_BCC].body = NULL;
2831 if (hentry[H_NEWSGROUPS].body != NULL) {
2832 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2833 hentry[H_NEWSGROUPS].body = NULL;
2835 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2836 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2837 compose->followup_to =
2838 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2841 g_free(hentry[H_FOLLOWUP_TO].body);
2842 hentry[H_FOLLOWUP_TO].body = NULL;
2844 if (hentry[H_LIST_POST].body != NULL) {
2845 gchar *to = NULL, *start = NULL;
2847 extract_address(hentry[H_LIST_POST].body);
2848 if (hentry[H_LIST_POST].body[0] != '\0') {
2849 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2851 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2852 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2855 g_free(compose->ml_post);
2856 compose->ml_post = to;
2859 g_free(hentry[H_LIST_POST].body);
2860 hentry[H_LIST_POST].body = NULL;
2863 /* CLAWS - X-Priority */
2864 if (compose->mode == COMPOSE_REEDIT)
2865 if (hentry[H_X_PRIORITY].body != NULL) {
2868 priority = atoi(hentry[H_X_PRIORITY].body);
2869 g_free(hentry[H_X_PRIORITY].body);
2871 hentry[H_X_PRIORITY].body = NULL;
2873 if (priority < PRIORITY_HIGHEST ||
2874 priority > PRIORITY_LOWEST)
2875 priority = PRIORITY_NORMAL;
2877 compose->priority = priority;
2880 if (compose->mode == COMPOSE_REEDIT) {
2881 if (msginfo->inreplyto && *msginfo->inreplyto)
2882 compose->inreplyto = g_strdup(msginfo->inreplyto);
2886 if (msginfo->msgid && *msginfo->msgid)
2887 compose->inreplyto = g_strdup(msginfo->msgid);
2889 if (!compose->references) {
2890 if (msginfo->msgid && *msginfo->msgid) {
2891 if (msginfo->inreplyto && *msginfo->inreplyto)
2892 compose->references =
2893 g_strdup_printf("<%s>\n\t<%s>",
2897 compose->references =
2898 g_strconcat("<", msginfo->msgid, ">",
2900 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2901 compose->references =
2902 g_strconcat("<", msginfo->inreplyto, ">",
2910 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2915 cm_return_val_if_fail(msginfo != NULL, -1);
2917 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2918 procheader_get_header_fields(fp, entries);
2922 while (he != NULL && he->name != NULL) {
2924 GtkListStore *model = NULL;
2926 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
2927 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
2928 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
2929 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
2930 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
2937 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2939 GSList *ref_id_list, *cur;
2943 ref_id_list = references_list_append(NULL, ref);
2944 if (!ref_id_list) return NULL;
2945 if (msgid && *msgid)
2946 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2951 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2952 /* "<" + Message-ID + ">" + CR+LF+TAB */
2953 len += strlen((gchar *)cur->data) + 5;
2955 if (len > MAX_REFERENCES_LEN) {
2956 /* remove second message-ID */
2957 if (ref_id_list && ref_id_list->next &&
2958 ref_id_list->next->next) {
2959 g_free(ref_id_list->next->data);
2960 ref_id_list = g_slist_remove
2961 (ref_id_list, ref_id_list->next->data);
2963 slist_free_strings(ref_id_list);
2964 g_slist_free(ref_id_list);
2971 new_ref = g_string_new("");
2972 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2973 if (new_ref->len > 0)
2974 g_string_append(new_ref, "\n\t");
2975 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2978 slist_free_strings(ref_id_list);
2979 g_slist_free(ref_id_list);
2981 new_ref_str = new_ref->str;
2982 g_string_free(new_ref, FALSE);
2987 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2988 const gchar *fmt, const gchar *qmark,
2989 const gchar *body, gboolean rewrap,
2990 gboolean need_unescape,
2991 const gchar *err_msg)
2993 MsgInfo* dummyinfo = NULL;
2994 gchar *quote_str = NULL;
2996 gboolean prev_autowrap;
2997 const gchar *trimmed_body = body;
2998 gint cursor_pos = -1;
2999 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3000 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3005 SIGNAL_BLOCK(buffer);
3008 dummyinfo = compose_msginfo_new_from_compose(compose);
3009 msginfo = dummyinfo;
3012 if (qmark != NULL) {
3014 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3015 compose->gtkaspell);
3017 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3019 quote_fmt_scan_string(qmark);
3022 buf = quote_fmt_get_buffer();
3024 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3026 Xstrdup_a(quote_str, buf, goto error)
3029 if (fmt && *fmt != '\0') {
3032 while (*trimmed_body == '\n')
3036 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3037 compose->gtkaspell);
3039 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3041 if (need_unescape) {
3044 /* decode \-escape sequences in the internal representation of the quote format */
3045 tmp = g_malloc(strlen(fmt)+1);
3046 pref_get_unescaped_pref(tmp, fmt);
3047 quote_fmt_scan_string(tmp);
3051 quote_fmt_scan_string(fmt);
3055 buf = quote_fmt_get_buffer();
3057 gint line = quote_fmt_get_line();
3058 alertpanel_error(err_msg, line);
3064 prev_autowrap = compose->autowrap;
3065 compose->autowrap = FALSE;
3067 mark = gtk_text_buffer_get_insert(buffer);
3068 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3069 if (g_utf8_validate(buf, -1, NULL)) {
3070 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3072 gchar *tmpout = NULL;
3073 tmpout = conv_codeset_strdup
3074 (buf, conv_get_locale_charset_str_no_utf8(),
3076 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3078 tmpout = g_malloc(strlen(buf)*2+1);
3079 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3081 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3085 cursor_pos = quote_fmt_get_cursor_pos();
3086 if (cursor_pos == -1)
3087 cursor_pos = gtk_text_iter_get_offset(&iter);
3088 compose->set_cursor_pos = cursor_pos;
3090 gtk_text_buffer_get_start_iter(buffer, &iter);
3091 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3092 gtk_text_buffer_place_cursor(buffer, &iter);
3094 compose->autowrap = prev_autowrap;
3095 if (compose->autowrap && rewrap)
3096 compose_wrap_all(compose);
3103 SIGNAL_UNBLOCK(buffer);
3105 procmsg_msginfo_free( dummyinfo );
3110 /* if ml_post is of type addr@host and from is of type
3111 * addr-anything@host, return TRUE
3113 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3115 gchar *left_ml = NULL;
3116 gchar *right_ml = NULL;
3117 gchar *left_from = NULL;
3118 gchar *right_from = NULL;
3119 gboolean result = FALSE;
3121 if (!ml_post || !from)
3124 left_ml = g_strdup(ml_post);
3125 if (strstr(left_ml, "@")) {
3126 right_ml = strstr(left_ml, "@")+1;
3127 *(strstr(left_ml, "@")) = '\0';
3130 left_from = g_strdup(from);
3131 if (strstr(left_from, "@")) {
3132 right_from = strstr(left_from, "@")+1;
3133 *(strstr(left_from, "@")) = '\0';
3136 if (left_ml && left_from && right_ml && right_from
3137 && !strncmp(left_from, left_ml, strlen(left_ml))
3138 && !strcmp(right_from, right_ml)) {
3147 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3148 gboolean respect_default_to)
3152 if (!folder || !folder->prefs)
3155 if (respect_default_to && folder->prefs->enable_default_to) {
3156 compose_entry_append(compose, folder->prefs->default_to,
3157 COMPOSE_TO, PREF_FOLDER);
3158 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3160 if (folder->prefs->enable_default_cc)
3161 compose_entry_append(compose, folder->prefs->default_cc,
3162 COMPOSE_CC, PREF_FOLDER);
3163 if (folder->prefs->enable_default_bcc)
3164 compose_entry_append(compose, folder->prefs->default_bcc,
3165 COMPOSE_BCC, PREF_FOLDER);
3166 if (folder->prefs->enable_default_replyto)
3167 compose_entry_append(compose, folder->prefs->default_replyto,
3168 COMPOSE_REPLYTO, PREF_FOLDER);
3171 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3176 if (!compose || !msginfo)
3179 if (msginfo->subject && *msginfo->subject) {
3180 buf = p = g_strdup(msginfo->subject);
3181 p += subject_get_prefix_length(p);
3182 memmove(buf, p, strlen(p) + 1);
3184 buf2 = g_strdup_printf("Re: %s", buf);
3185 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3190 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3193 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3194 gboolean to_all, gboolean to_ml,
3196 gboolean followup_and_reply_to)
3198 GSList *cc_list = NULL;
3201 gchar *replyto = NULL;
3202 gchar *ac_email = NULL;
3204 gboolean reply_to_ml = FALSE;
3205 gboolean default_reply_to = FALSE;
3207 cm_return_if_fail(compose->account != NULL);
3208 cm_return_if_fail(msginfo != NULL);
3210 reply_to_ml = to_ml && compose->ml_post;
3212 default_reply_to = msginfo->folder &&
3213 msginfo->folder->prefs->enable_default_reply_to;
3215 if (compose->account->protocol != A_NNTP) {
3216 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3218 if (reply_to_ml && !default_reply_to) {
3220 gboolean is_subscr = is_subscription(compose->ml_post,
3223 /* normal answer to ml post with a reply-to */
3224 compose_entry_append(compose,
3226 COMPOSE_TO, PREF_ML);
3227 if (compose->replyto)
3228 compose_entry_append(compose,
3230 COMPOSE_CC, PREF_ML);
3232 /* answer to subscription confirmation */
3233 if (compose->replyto)
3234 compose_entry_append(compose,
3236 COMPOSE_TO, PREF_ML);
3237 else if (msginfo->from)
3238 compose_entry_append(compose,
3240 COMPOSE_TO, PREF_ML);
3243 else if (!(to_all || to_sender) && default_reply_to) {
3244 compose_entry_append(compose,
3245 msginfo->folder->prefs->default_reply_to,
3246 COMPOSE_TO, PREF_FOLDER);
3247 compose_entry_mark_default_to(compose,
3248 msginfo->folder->prefs->default_reply_to);
3253 Xstrdup_a(tmp1, msginfo->from, return);
3254 extract_address(tmp1);
3255 if (to_all || to_sender ||
3256 !account_find_from_address(tmp1, FALSE))
3257 compose_entry_append(compose,
3258 (compose->replyto && !to_sender)
3259 ? compose->replyto :
3260 msginfo->from ? msginfo->from : "",
3261 COMPOSE_TO, PREF_NONE);
3262 else if (!to_all && !to_sender) {
3263 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3264 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3265 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3266 if (compose->replyto) {
3267 compose_entry_append(compose,
3269 COMPOSE_TO, PREF_NONE);
3271 compose_entry_append(compose,
3272 msginfo->from ? msginfo->from : "",
3273 COMPOSE_TO, PREF_NONE);
3276 /* replying to own mail, use original recp */
3277 compose_entry_append(compose,
3278 msginfo->to ? msginfo->to : "",
3279 COMPOSE_TO, PREF_NONE);
3280 compose_entry_append(compose,
3281 msginfo->cc ? msginfo->cc : "",
3282 COMPOSE_CC, PREF_NONE);
3287 if (to_sender || (compose->followup_to &&
3288 !strncmp(compose->followup_to, "poster", 6)))
3289 compose_entry_append
3291 (compose->replyto ? compose->replyto :
3292 msginfo->from ? msginfo->from : ""),
3293 COMPOSE_TO, PREF_NONE);
3295 else if (followup_and_reply_to || to_all) {
3296 compose_entry_append
3298 (compose->replyto ? compose->replyto :
3299 msginfo->from ? msginfo->from : ""),
3300 COMPOSE_TO, PREF_NONE);
3302 compose_entry_append
3304 compose->followup_to ? compose->followup_to :
3305 compose->newsgroups ? compose->newsgroups : "",
3306 COMPOSE_NEWSGROUPS, PREF_NONE);
3309 compose_entry_append
3311 compose->followup_to ? compose->followup_to :
3312 compose->newsgroups ? compose->newsgroups : "",
3313 COMPOSE_NEWSGROUPS, PREF_NONE);
3315 compose_reply_set_subject(compose, msginfo);
3317 if (to_ml && compose->ml_post) return;
3318 if (!to_all || compose->account->protocol == A_NNTP) return;
3320 if (compose->replyto) {
3321 Xstrdup_a(replyto, compose->replyto, return);
3322 extract_address(replyto);
3324 if (msginfo->from) {
3325 Xstrdup_a(from, msginfo->from, return);
3326 extract_address(from);
3329 if (replyto && from)
3330 cc_list = address_list_append_with_comments(cc_list, from);
3331 if (to_all && msginfo->folder &&
3332 msginfo->folder->prefs->enable_default_reply_to)
3333 cc_list = address_list_append_with_comments(cc_list,
3334 msginfo->folder->prefs->default_reply_to);
3335 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3336 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3338 ac_email = g_utf8_strdown(compose->account->address, -1);
3341 for (cur = cc_list; cur != NULL; cur = cur->next) {
3342 gchar *addr = g_utf8_strdown(cur->data, -1);
3343 extract_address(addr);
3345 if (strcmp(ac_email, addr))
3346 compose_entry_append(compose, (gchar *)cur->data,
3347 COMPOSE_CC, PREF_NONE);
3349 debug_print("Cc address same as compose account's, ignoring\n");
3354 slist_free_strings(cc_list);
3355 g_slist_free(cc_list);
3361 #define SET_ENTRY(entry, str) \
3364 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3367 #define SET_ADDRESS(type, str) \
3370 compose_entry_append(compose, str, type, PREF_NONE); \
3373 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3375 cm_return_if_fail(msginfo != NULL);
3377 SET_ENTRY(subject_entry, msginfo->subject);
3378 SET_ENTRY(from_name, msginfo->from);
3379 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3380 SET_ADDRESS(COMPOSE_CC, compose->cc);
3381 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3382 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3383 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3384 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3386 compose_update_priority_menu_item(compose);
3387 compose_update_privacy_system_menu_item(compose, FALSE);
3388 compose_show_first_last_header(compose, TRUE);
3394 static void compose_insert_sig(Compose *compose, gboolean replace)
3396 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3397 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3399 GtkTextIter iter, iter_end;
3400 gint cur_pos, ins_pos;
3401 gboolean prev_autowrap;
3402 gboolean found = FALSE;
3403 gboolean exists = FALSE;
3405 cm_return_if_fail(compose->account != NULL);
3409 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3410 G_CALLBACK(compose_changed_cb),
3413 mark = gtk_text_buffer_get_insert(buffer);
3414 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3415 cur_pos = gtk_text_iter_get_offset (&iter);
3418 gtk_text_buffer_get_end_iter(buffer, &iter);
3420 exists = (compose->sig_str != NULL);
3423 GtkTextIter first_iter, start_iter, end_iter;
3425 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3427 if (!exists || compose->sig_str[0] == '\0')
3430 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3431 compose->signature_tag);
3434 /* include previous \n\n */
3435 gtk_text_iter_backward_chars(&first_iter, 1);
3436 start_iter = first_iter;
3437 end_iter = first_iter;
3439 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3440 compose->signature_tag);
3441 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3442 compose->signature_tag);
3444 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3450 g_free(compose->sig_str);
3451 compose->sig_str = account_get_signature_str(compose->account);
3453 cur_pos = gtk_text_iter_get_offset(&iter);
3455 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3456 g_free(compose->sig_str);
3457 compose->sig_str = NULL;
3459 if (compose->sig_inserted == FALSE)
3460 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3461 compose->sig_inserted = TRUE;
3463 cur_pos = gtk_text_iter_get_offset(&iter);
3464 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3466 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3467 gtk_text_iter_forward_chars(&iter, 1);
3468 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3469 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3471 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3472 cur_pos = gtk_text_buffer_get_char_count (buffer);
3475 /* put the cursor where it should be
3476 * either where the quote_fmt says, either where it was */
3477 if (compose->set_cursor_pos < 0)
3478 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3480 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3481 compose->set_cursor_pos);
3483 compose->set_cursor_pos = -1;
3484 gtk_text_buffer_place_cursor(buffer, &iter);
3485 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3486 G_CALLBACK(compose_changed_cb),
3492 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3495 GtkTextBuffer *buffer;
3498 const gchar *cur_encoding;
3499 gchar buf[BUFFSIZE];
3502 gboolean prev_autowrap;
3503 gboolean badtxt = FALSE;
3504 struct stat file_stat;
3507 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3509 /* get the size of the file we are about to insert */
3510 ret = g_stat(file, &file_stat);
3512 gchar *shortfile = g_path_get_basename(file);
3513 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3515 return COMPOSE_INSERT_NO_FILE;
3516 } else if (prefs_common.warn_large_insert == TRUE) {
3518 /* ask user for confirmation if the file is large */
3519 if (prefs_common.warn_large_insert_size < 0 ||
3520 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3524 msg = g_strdup_printf(_("You are about to insert a file of %s "
3525 "in the message body. Are you sure you want to do that?"),
3526 to_human_readable(file_stat.st_size));
3527 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3528 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3531 /* do we ask for confirmation next time? */
3532 if (aval & G_ALERTDISABLE) {
3533 /* no confirmation next time, disable feature in preferences */
3534 aval &= ~G_ALERTDISABLE;
3535 prefs_common.warn_large_insert = FALSE;
3538 /* abort file insertion if user canceled action */
3539 if (aval != G_ALERTALTERNATE) {
3540 return COMPOSE_INSERT_NO_FILE;
3546 if ((fp = g_fopen(file, "rb")) == NULL) {
3547 FILE_OP_ERROR(file, "fopen");
3548 return COMPOSE_INSERT_READ_ERROR;
3551 prev_autowrap = compose->autowrap;
3552 compose->autowrap = FALSE;
3554 text = GTK_TEXT_VIEW(compose->text);
3555 buffer = gtk_text_view_get_buffer(text);
3556 mark = gtk_text_buffer_get_insert(buffer);
3557 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3559 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3560 G_CALLBACK(text_inserted),
3563 cur_encoding = conv_get_locale_charset_str_no_utf8();
3565 while (fgets(buf, sizeof(buf), fp) != NULL) {
3568 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3569 str = g_strdup(buf);
3571 str = conv_codeset_strdup
3572 (buf, cur_encoding, CS_INTERNAL);
3575 /* strip <CR> if DOS/Windows file,
3576 replace <CR> with <LF> if Macintosh file. */
3579 if (len > 0 && str[len - 1] != '\n') {
3581 if (str[len] == '\r') str[len] = '\n';
3584 gtk_text_buffer_insert(buffer, &iter, str, -1);
3588 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3589 G_CALLBACK(text_inserted),
3591 compose->autowrap = prev_autowrap;
3592 if (compose->autowrap)
3593 compose_wrap_all(compose);
3598 return COMPOSE_INSERT_INVALID_CHARACTER;
3600 return COMPOSE_INSERT_SUCCESS;
3603 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3604 const gchar *filename,
3605 const gchar *content_type,
3606 const gchar *charset)
3614 GtkListStore *store;
3616 gboolean has_binary = FALSE;
3618 if (!is_file_exist(file)) {
3619 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3620 gboolean result = FALSE;
3621 if (file_from_uri && is_file_exist(file_from_uri)) {
3622 result = compose_attach_append(
3623 compose, file_from_uri,
3624 filename, content_type,
3627 g_free(file_from_uri);
3630 alertpanel_error("File %s doesn't exist\n", filename);
3633 if ((size = get_file_size(file)) < 0) {
3634 alertpanel_error("Can't get file size of %s\n", filename);
3638 alertpanel_error(_("File %s is empty."), filename);
3641 if ((fp = g_fopen(file, "rb")) == NULL) {
3642 alertpanel_error(_("Can't read %s."), filename);
3647 ainfo = g_new0(AttachInfo, 1);
3648 auto_ainfo = g_auto_pointer_new_with_free
3649 (ainfo, (GFreeFunc) compose_attach_info_free);
3650 ainfo->file = g_strdup(file);
3653 ainfo->content_type = g_strdup(content_type);
3654 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3656 MsgFlags flags = {0, 0};
3658 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3659 ainfo->encoding = ENC_7BIT;
3661 ainfo->encoding = ENC_8BIT;
3663 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3664 if (msginfo && msginfo->subject)
3665 name = g_strdup(msginfo->subject);
3667 name = g_path_get_basename(filename ? filename : file);
3669 ainfo->name = g_strdup_printf(_("Message:&nbs