2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2008 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"
87 #include "alertpanel.h"
88 #include "manage_window.h"
89 #include "gtkshruler.h"
91 #include "addr_compl.h"
92 #include "quote_fmt.h"
94 #include "foldersel.h"
97 #include "message_search.h"
102 #include "autofaces.h"
114 #define N_ATTACH_COLS (N_COL_COLUMNS)
118 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
119 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
127 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
132 } ComposeCallAdvancedAction;
136 PRIORITY_HIGHEST = 1,
145 COMPOSE_INSERT_SUCCESS,
146 COMPOSE_INSERT_READ_ERROR,
147 COMPOSE_INSERT_INVALID_CHARACTER,
148 COMPOSE_INSERT_NO_FILE
149 } ComposeInsertResult;
153 COMPOSE_WRITE_FOR_SEND,
154 COMPOSE_WRITE_FOR_STORE
159 COMPOSE_QUOTE_FORCED,
164 #define B64_LINE_SIZE 57
165 #define B64_BUFFSIZE 77
167 #define MAX_REFERENCES_LEN 999
169 static GList *compose_list = NULL;
171 static Compose *compose_generic_new (PrefsAccount *account,
174 GPtrArray *attach_files,
175 GList *listAddress );
177 static Compose *compose_create (PrefsAccount *account,
182 static void compose_entry_mark_default_to (Compose *compose,
183 const gchar *address);
184 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
185 ComposeQuoteMode quote_mode,
189 static Compose *compose_forward_multiple (PrefsAccount *account,
190 GSList *msginfo_list);
191 static Compose *compose_reply (MsgInfo *msginfo,
192 ComposeQuoteMode quote_mode,
197 static Compose *compose_reply_mode (ComposeMode mode,
198 GSList *msginfo_list,
200 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
201 static void compose_update_privacy_systems_menu(Compose *compose);
203 static GtkWidget *compose_account_option_menu_create
205 static void compose_set_out_encoding (Compose *compose);
206 static void compose_set_template_menu (Compose *compose);
207 static void compose_template_apply (Compose *compose,
210 static void compose_destroy (Compose *compose);
212 static void compose_entries_set (Compose *compose,
214 ComposeEntryType to_type);
215 static gint compose_parse_header (Compose *compose,
217 static gchar *compose_parse_references (const gchar *ref,
220 static gchar *compose_quote_fmt (Compose *compose,
226 gboolean need_unescape,
227 const gchar *err_msg);
229 static void compose_reply_set_entry (Compose *compose,
235 followup_and_reply_to);
236 static void compose_reedit_set_entry (Compose *compose,
239 static void compose_insert_sig (Compose *compose,
241 static ComposeInsertResult compose_insert_file (Compose *compose,
244 static gboolean compose_attach_append (Compose *compose,
247 const gchar *content_type);
248 static void compose_attach_parts (Compose *compose,
251 static gboolean compose_beautify_paragraph (Compose *compose,
252 GtkTextIter *par_iter,
254 static void compose_wrap_all (Compose *compose);
255 static void compose_wrap_all_full (Compose *compose,
258 static void compose_set_title (Compose *compose);
259 static void compose_select_account (Compose *compose,
260 PrefsAccount *account,
263 static PrefsAccount *compose_current_mail_account(void);
264 /* static gint compose_send (Compose *compose); */
265 static gboolean compose_check_for_valid_recipient
267 static gboolean compose_check_entries (Compose *compose,
268 gboolean check_everything);
269 static gint compose_write_to_file (Compose *compose,
272 gboolean attach_parts);
273 static gint compose_write_body_to_file (Compose *compose,
275 static gint compose_remove_reedit_target (Compose *compose,
277 static void compose_remove_draft (Compose *compose);
278 static gint compose_queue_sub (Compose *compose,
282 gboolean check_subject,
283 gboolean remove_reedit_target);
284 static void compose_add_attachments (Compose *compose,
286 static gchar *compose_get_header (Compose *compose);
288 static void compose_convert_header (Compose *compose,
293 gboolean addr_field);
295 static void compose_attach_info_free (AttachInfo *ainfo);
296 static void compose_attach_remove_selected (GtkAction *action,
299 static void compose_attach_property (GtkAction *action,
301 static void compose_attach_property_create (gboolean *cancelled);
302 static void attach_property_ok (GtkWidget *widget,
303 gboolean *cancelled);
304 static void attach_property_cancel (GtkWidget *widget,
305 gboolean *cancelled);
306 static gint attach_property_delete_event (GtkWidget *widget,
308 gboolean *cancelled);
309 static gboolean attach_property_key_pressed (GtkWidget *widget,
311 gboolean *cancelled);
313 static void compose_exec_ext_editor (Compose *compose);
315 static gint compose_exec_ext_editor_real (const gchar *file);
316 static gboolean compose_ext_editor_kill (Compose *compose);
317 static gboolean compose_input_cb (GIOChannel *source,
318 GIOCondition condition,
320 static void compose_set_ext_editor_sensitive (Compose *compose,
322 #endif /* G_OS_UNIX */
324 static void compose_undo_state_changed (UndoMain *undostruct,
329 static void compose_create_header_entry (Compose *compose);
330 static void compose_add_header_entry (Compose *compose, const gchar *header, gchar *text);
331 static void compose_remove_header_entries(Compose *compose);
333 static void compose_update_priority_menu_item(Compose * compose);
335 static void compose_spell_menu_changed (void *data);
337 static void compose_add_field_list ( Compose *compose,
338 GList *listAddress );
340 /* callback functions */
342 static gboolean compose_edit_size_alloc (GtkEditable *widget,
343 GtkAllocation *allocation,
344 GtkSHRuler *shruler);
345 static void account_activated (GtkComboBox *optmenu,
347 static void attach_selected (GtkTreeView *tree_view,
348 GtkTreePath *tree_path,
349 GtkTreeViewColumn *column,
351 static gboolean attach_button_pressed (GtkWidget *widget,
352 GdkEventButton *event,
354 static gboolean attach_key_pressed (GtkWidget *widget,
357 static void compose_send_cb (GtkAction *action, gpointer data);
358 static void compose_send_later_cb (GtkAction *action, gpointer data);
360 static void compose_save_cb (GtkAction *action,
363 static void compose_attach_cb (GtkAction *action,
365 static void compose_insert_file_cb (GtkAction *action,
367 static void compose_insert_sig_cb (GtkAction *action,
370 static void compose_close_cb (GtkAction *action,
373 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
375 static void compose_address_cb (GtkAction *action,
377 static void about_show_cb (GtkAction *action,
379 static void compose_template_activate_cb(GtkWidget *widget,
382 static void compose_ext_editor_cb (GtkAction *action,
385 static gint compose_delete_cb (GtkWidget *widget,
389 static void compose_undo_cb (GtkAction *action,
391 static void compose_redo_cb (GtkAction *action,
393 static void compose_cut_cb (GtkAction *action,
395 static void compose_copy_cb (GtkAction *action,
397 static void compose_paste_cb (GtkAction *action,
399 static void compose_paste_as_quote_cb (GtkAction *action,
401 static void compose_paste_no_wrap_cb (GtkAction *action,
403 static void compose_paste_wrap_cb (GtkAction *action,
405 static void compose_allsel_cb (GtkAction *action,
408 static void compose_advanced_action_cb (GtkAction *action,
411 static void compose_grab_focus_cb (GtkWidget *widget,
414 static void compose_changed_cb (GtkTextBuffer *textbuf,
417 static void compose_wrap_cb (GtkAction *action,
419 static void compose_wrap_all_cb (GtkAction *action,
421 static void compose_find_cb (GtkAction *action,
423 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
425 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
428 static void compose_toggle_ruler_cb (GtkToggleAction *action,
430 static void compose_toggle_sign_cb (GtkToggleAction *action,
432 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
434 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
435 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
436 static void activate_privacy_system (Compose *compose,
437 PrefsAccount *account,
439 static void compose_use_signing(Compose *compose, gboolean use_signing);
440 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
441 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
443 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
445 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
446 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
447 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
449 static void compose_attach_drag_received_cb (GtkWidget *widget,
450 GdkDragContext *drag_context,
453 GtkSelectionData *data,
457 static void compose_insert_drag_received_cb (GtkWidget *widget,
458 GdkDragContext *drag_context,
461 GtkSelectionData *data,
465 static void compose_header_drag_received_cb (GtkWidget *widget,
466 GdkDragContext *drag_context,
469 GtkSelectionData *data,
474 static gboolean compose_drag_drop (GtkWidget *widget,
475 GdkDragContext *drag_context,
477 guint time, gpointer user_data);
479 static void text_inserted (GtkTextBuffer *buffer,
484 static Compose *compose_generic_reply(MsgInfo *msginfo,
485 ComposeQuoteMode quote_mode,
489 gboolean followup_and_reply_to,
492 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
493 ComposeHeaderEntry *headerentry);
494 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
496 ComposeHeaderEntry *headerentry);
498 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
500 static void compose_allow_user_actions (Compose *compose, gboolean allow);
502 static void compose_nothing_cb (GtkAction *action, gpointer data)
508 static void compose_check_all (GtkAction *action, gpointer data);
509 static void compose_highlight_all (GtkAction *action, gpointer data);
510 static void compose_check_backwards (GtkAction *action, gpointer data);
511 static void compose_check_forwards_go (GtkAction *action, gpointer data);
514 static gint compose_defer_auto_save_draft (Compose *compose);
515 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
517 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
520 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
521 FolderItem *folder_item);
523 static void compose_attach_update_label(Compose *compose);
525 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data);
527 static GtkActionEntry compose_popup_entries[] =
529 {"Compose", NULL, "Compose" },
530 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
531 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
532 {"Compose/---", NULL, "---", NULL, NULL, NULL },
533 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
536 static GtkActionEntry compose_entries[] =
538 {"Menu", NULL, "Menu" },
540 {"Message", NULL, N_("_Message") },
541 {"Edit", NULL, N_("_Edit") },
543 {"Spelling", NULL, N_("_Spelling") },
545 {"Options", NULL, N_("_Options") },
546 {"Tools", NULL, N_("_Tools") },
547 {"Help", NULL, N_("_Help") },
549 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
550 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
551 {"Message/---", NULL, "---" },
553 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
554 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
555 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
556 /* {"Message/---", NULL, "---" }, */
557 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
558 /* {"Message/---", NULL, "---" }, */
559 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
562 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
563 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
564 {"Edit/---", NULL, "---" },
566 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
567 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
568 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
570 {"Edit/SpecialPaste", NULL, N_("Special paste") },
571 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
572 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
573 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
575 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
577 {"Edit/Advanced", NULL, N_("A_dvanced") },
578 {"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*/
579 {"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*/
580 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
581 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
582 {"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*/
583 {"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*/
584 {"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*/
585 {"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*/
586 {"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*/
587 {"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*/
588 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
589 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
590 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
591 {"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*/
593 /* {"Edit/---", NULL, "---" }, */
594 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
596 /* {"Edit/---", NULL, "---" }, */
597 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
598 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
599 /* {"Edit/---", NULL, "---" }, */
600 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
603 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
604 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
605 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
606 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
608 {"Spelling/---", NULL, "---" },
609 {"Spelling/Options", NULL, N_("_Options") },
614 {"Options/ReplyMode", NULL, N_("Reply _mode") },
615 {"Options/---", NULL, "---" },
616 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
617 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
619 /* {"Options/---", NULL, "---" }, */
621 {"Options/Priority", NULL, N_("_Priority") },
623 {"Options/Encoding", NULL, N_("Character _encoding") },
624 {"Options/Encoding/---", NULL, "---" },
625 #define ENC_ACTION(cs_char,c_char,string) \
626 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
628 {"Options/Encoding/Western", NULL, N_("Western European") },
629 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
630 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
631 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
632 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
633 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
634 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
635 {"Options/Encoding/Korean", NULL, N_("Korean") },
636 {"Options/Encoding/Thai", NULL, N_("Thai") },
639 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
641 {"Tools/Template", NULL, N_("_Template") },
642 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
643 {"Tools/Actions", NULL, N_("Actio_ns") },
644 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
647 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
650 static GtkToggleActionEntry compose_toggle_entries[] =
652 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
653 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
654 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
655 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
656 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
657 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
658 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
661 static GtkRadioActionEntry compose_radio_rm_entries[] =
663 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
664 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
665 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
666 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
669 static GtkRadioActionEntry compose_radio_prio_entries[] =
671 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
672 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
673 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
674 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
675 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
678 static GtkRadioActionEntry compose_radio_enc_entries[] =
680 ENC_ACTION(CS_AUTO, C_AUTO, "_Automatic"), /* RADIO compose_set_encoding_cb */
681 ENC_ACTION(CS_US_ASCII, C_US_ASCII, "7bit ASCII (US-ASC_II)"), /* RADIO compose_set_encoding_cb */
682 ENC_ACTION(CS_UTF_8, C_UTF_8, "Unicode (_UTF-8)"), /* RADIO compose_set_encoding_cb */
683 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
684 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
685 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
686 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, "Central European (ISO-8859-_2)"), /* RADIO compose_set_encoding_cb */
687 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
688 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
689 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, "Greek (ISO-8859-_7)"), /* RADIO compose_set_encoding_cb */
690 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
691 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
692 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
693 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, "Turkish (ISO-8859-_9)"), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
713 static GtkTargetEntry compose_mime_types[] =
715 {"text/uri-list", 0, 0},
716 {"UTF8_STRING", 0, 0},
720 static gboolean compose_put_existing_to_front(MsgInfo *info)
722 GList *compose_list = compose_get_compose_list();
726 for (elem = compose_list; elem != NULL && elem->data != NULL;
728 Compose *c = (Compose*)elem->data;
730 if (!c->targetinfo || !c->targetinfo->msgid ||
734 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
735 gtkut_window_popup(c->window);
743 static GdkColor quote_color1 =
744 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
745 static GdkColor quote_color2 =
746 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
747 static GdkColor quote_color3 =
748 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
750 static GdkColor quote_bgcolor1 =
751 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
752 static GdkColor quote_bgcolor2 =
753 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
754 static GdkColor quote_bgcolor3 =
755 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
757 static GdkColor signature_color = {
764 static GdkColor uri_color = {
771 static void compose_create_tags(GtkTextView *text, Compose *compose)
773 GtkTextBuffer *buffer;
774 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
780 buffer = gtk_text_view_get_buffer(text);
782 if (prefs_common.enable_color) {
783 /* grab the quote colors, converting from an int to a GdkColor */
784 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
786 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
788 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
790 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
792 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
794 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
796 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
798 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
801 signature_color = quote_color1 = quote_color2 = quote_color3 =
802 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
805 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
806 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
807 "foreground-gdk", "e_color1,
808 "paragraph-background-gdk", "e_bgcolor1,
810 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
811 "foreground-gdk", "e_color2,
812 "paragraph-background-gdk", "e_bgcolor2,
814 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
815 "foreground-gdk", "e_color3,
816 "paragraph-background-gdk", "e_bgcolor3,
819 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
820 "foreground-gdk", "e_color1,
822 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
823 "foreground-gdk", "e_color2,
825 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
826 "foreground-gdk", "e_color3,
830 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
831 "foreground-gdk", &signature_color,
834 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
835 "foreground-gdk", &uri_color,
837 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
838 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
840 color[0] = quote_color1;
841 color[1] = quote_color2;
842 color[2] = quote_color3;
843 color[3] = quote_bgcolor1;
844 color[4] = quote_bgcolor2;
845 color[5] = quote_bgcolor3;
846 color[6] = signature_color;
847 color[7] = uri_color;
848 cmap = gdk_drawable_get_colormap(compose->window->window);
849 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
851 for (i = 0; i < 8; i++) {
852 if (success[i] == FALSE) {
855 g_warning("Compose: color allocation failed.\n");
856 style = gtk_widget_get_style(GTK_WIDGET(text));
857 quote_color1 = quote_color2 = quote_color3 =
858 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
859 signature_color = uri_color = black;
864 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
865 GPtrArray *attach_files)
867 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
870 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
872 return compose_generic_new(account, mailto, item, NULL, NULL);
875 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
877 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
880 #define SCROLL_TO_CURSOR(compose) { \
881 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
882 gtk_text_view_get_buffer( \
883 GTK_TEXT_VIEW(compose->text))); \
884 gtk_text_view_scroll_mark_onscreen( \
885 GTK_TEXT_VIEW(compose->text), \
889 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
890 GPtrArray *attach_files, GList *listAddress )
893 GtkTextView *textview;
894 GtkTextBuffer *textbuf;
896 const gchar *subject_format = NULL;
897 const gchar *body_format = NULL;
898 gchar *mailto_from = NULL;
899 PrefsAccount *mailto_account = NULL;
900 MsgInfo* dummyinfo = NULL;
902 /* check if mailto defines a from */
903 if (mailto && *mailto != '\0') {
904 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL);
905 /* mailto defines a from, check if we can get account prefs from it,
906 if not, the account prefs will be guessed using other ways, but we'll keep
909 mailto_account = account_find_from_address(mailto_from, TRUE);
911 account = mailto_account;
914 /* if no account prefs set from mailto, set if from folder prefs (if any) */
915 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
916 account = account_find_from_id(item->prefs->default_account);
918 /* if no account prefs set, fallback to the current one */
919 if (!account) account = cur_account;
920 g_return_val_if_fail(account != NULL, NULL);
922 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
924 /* override from name if mailto asked for it */
926 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
929 /* override from name according to folder properties */
930 if (item && item->prefs &&
931 item->prefs->compose_with_format &&
932 item->prefs->compose_override_from_format &&
933 *item->prefs->compose_override_from_format != '\0') {
938 dummyinfo = compose_msginfo_new_from_compose(compose);
940 /* decode \-escape sequences in the internal representation of the quote format */
941 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
942 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
945 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
948 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
950 quote_fmt_scan_string(tmp);
953 buf = quote_fmt_get_buffer();
955 alertpanel_error(_("New message From format error."));
957 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
958 quote_fmt_reset_vartable();
963 compose->replyinfo = NULL;
964 compose->fwdinfo = NULL;
966 textview = GTK_TEXT_VIEW(compose->text);
967 textbuf = gtk_text_view_get_buffer(textview);
968 compose_create_tags(textview, compose);
970 undo_block(compose->undostruct);
972 compose_set_dictionaries_from_folder_prefs(compose, item);
975 if (account->auto_sig)
976 compose_insert_sig(compose, FALSE);
977 gtk_text_buffer_get_start_iter(textbuf, &iter);
978 gtk_text_buffer_place_cursor(textbuf, &iter);
980 if (account->protocol != A_NNTP) {
981 if (mailto && *mailto != '\0') {
982 compose_entries_set(compose, mailto, COMPOSE_TO);
984 } else if (item && item->prefs->enable_default_to) {
985 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
986 compose_entry_mark_default_to(compose, item->prefs->default_to);
988 if (item && item->ret_rcpt) {
989 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
992 if (mailto && *mailto != '\0') {
993 if (!strchr(mailto, '@'))
994 compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
996 compose_entries_set(compose, mailto, COMPOSE_TO);
997 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
998 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1001 * CLAWS: just don't allow return receipt request, even if the user
1002 * may want to send an email. simple but foolproof.
1004 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1006 compose_add_field_list( compose, listAddress );
1008 if (item && item->prefs && item->prefs->compose_with_format) {
1009 subject_format = item->prefs->compose_subject_format;
1010 body_format = item->prefs->compose_body_format;
1011 } else if (account->compose_with_format) {
1012 subject_format = account->compose_subject_format;
1013 body_format = account->compose_body_format;
1014 } else if (prefs_common.compose_with_format) {
1015 subject_format = prefs_common.compose_subject_format;
1016 body_format = prefs_common.compose_body_format;
1019 if (subject_format || body_format) {
1022 && *subject_format != '\0' )
1024 gchar *subject = NULL;
1029 dummyinfo = compose_msginfo_new_from_compose(compose);
1031 /* decode \-escape sequences in the internal representation of the quote format */
1032 tmp = malloc(strlen(subject_format)+1);
1033 pref_get_unescaped_pref(tmp, subject_format);
1035 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1037 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1038 compose->gtkaspell);
1040 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1042 quote_fmt_scan_string(tmp);
1045 buf = quote_fmt_get_buffer();
1047 alertpanel_error(_("New message subject format error."));
1049 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1050 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1051 quote_fmt_reset_vartable();
1058 && *body_format != '\0' )
1061 GtkTextBuffer *buffer;
1062 GtkTextIter start, end;
1066 dummyinfo = compose_msginfo_new_from_compose(compose);
1068 text = GTK_TEXT_VIEW(compose->text);
1069 buffer = gtk_text_view_get_buffer(text);
1070 gtk_text_buffer_get_start_iter(buffer, &start);
1071 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1072 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1074 compose_quote_fmt(compose, dummyinfo,
1076 NULL, tmp, FALSE, TRUE,
1077 _("New message body format error at line %d."));
1078 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1079 quote_fmt_reset_vartable();
1085 procmsg_msginfo_free( dummyinfo );
1091 for (i = 0; i < attach_files->len; i++) {
1092 file = g_ptr_array_index(attach_files, i);
1093 compose_attach_append(compose, file, file, NULL);
1097 compose_show_first_last_header(compose, TRUE);
1099 /* Set save folder */
1100 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1101 gchar *folderidentifier;
1103 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1104 folderidentifier = folder_item_get_identifier(item);
1105 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1106 g_free(folderidentifier);
1109 gtk_widget_grab_focus(compose->header_last->entry);
1111 undo_unblock(compose->undostruct);
1113 if (prefs_common.auto_exteditor)
1114 compose_exec_ext_editor(compose);
1116 compose->draft_timeout_tag = -1;
1117 SCROLL_TO_CURSOR(compose);
1119 compose->modified = FALSE;
1120 compose_set_title(compose);
1124 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1125 gboolean override_pref)
1127 gchar *privacy = NULL;
1129 g_return_if_fail(compose != NULL);
1130 g_return_if_fail(account != NULL);
1132 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1135 if (account->default_privacy_system
1136 && strlen(account->default_privacy_system)) {
1137 privacy = account->default_privacy_system;
1139 GSList *privacy_avail = privacy_get_system_ids();
1140 if (privacy_avail && g_slist_length(privacy_avail)) {
1141 privacy = (gchar *)(privacy_avail->data);
1144 if (privacy != NULL) {
1145 if (compose->privacy_system == NULL)
1146 compose->privacy_system = g_strdup(privacy);
1147 else if (*(compose->privacy_system) == '\0') {
1148 g_free(compose->privacy_system);
1149 compose->privacy_system = g_strdup(privacy);
1151 compose_update_privacy_system_menu_item(compose, FALSE);
1152 compose_use_encryption(compose, TRUE);
1156 static void compose_force_signing(Compose *compose, PrefsAccount *account)
1158 gchar *privacy = NULL;
1160 if (account->default_privacy_system
1161 && strlen(account->default_privacy_system)) {
1162 privacy = account->default_privacy_system;
1164 GSList *privacy_avail = privacy_get_system_ids();
1165 if (privacy_avail && g_slist_length(privacy_avail)) {
1166 privacy = (gchar *)(privacy_avail->data);
1169 if (privacy != NULL) {
1170 if (compose->privacy_system == NULL)
1171 compose->privacy_system = g_strdup(privacy);
1172 compose_update_privacy_system_menu_item(compose, FALSE);
1173 compose_use_signing(compose, TRUE);
1177 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1181 Compose *compose = NULL;
1183 g_return_val_if_fail(msginfo_list != NULL, NULL);
1185 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1186 g_return_val_if_fail(msginfo != NULL, NULL);
1188 list_len = g_slist_length(msginfo_list);
1192 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1193 FALSE, prefs_common.default_reply_list, FALSE, body);
1195 case COMPOSE_REPLY_WITH_QUOTE:
1196 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1197 FALSE, prefs_common.default_reply_list, FALSE, body);
1199 case COMPOSE_REPLY_WITHOUT_QUOTE:
1200 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1201 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1203 case COMPOSE_REPLY_TO_SENDER:
1204 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1205 FALSE, FALSE, TRUE, body);
1207 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1208 compose = compose_followup_and_reply_to(msginfo,
1209 COMPOSE_QUOTE_CHECK,
1210 FALSE, FALSE, body);
1212 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1213 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1214 FALSE, FALSE, TRUE, body);
1216 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1217 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1218 FALSE, FALSE, TRUE, NULL);
1220 case COMPOSE_REPLY_TO_ALL:
1221 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1222 TRUE, FALSE, FALSE, body);
1224 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1225 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1226 TRUE, FALSE, FALSE, body);
1228 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1229 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1230 TRUE, FALSE, FALSE, NULL);
1232 case COMPOSE_REPLY_TO_LIST:
1233 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1234 FALSE, TRUE, FALSE, body);
1236 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1237 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1238 FALSE, TRUE, FALSE, body);
1240 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1241 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1242 FALSE, TRUE, FALSE, NULL);
1244 case COMPOSE_FORWARD:
1245 if (prefs_common.forward_as_attachment) {
1246 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1249 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1253 case COMPOSE_FORWARD_INLINE:
1254 /* check if we reply to more than one Message */
1255 if (list_len == 1) {
1256 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1259 /* more messages FALL THROUGH */
1260 case COMPOSE_FORWARD_AS_ATTACH:
1261 compose = compose_forward_multiple(NULL, msginfo_list);
1263 case COMPOSE_REDIRECT:
1264 compose = compose_redirect(NULL, msginfo, FALSE);
1267 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1270 if (compose == NULL) {
1271 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1275 compose->rmode = mode;
1276 switch (compose->rmode) {
1278 case COMPOSE_REPLY_WITH_QUOTE:
1279 case COMPOSE_REPLY_WITHOUT_QUOTE:
1280 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1281 debug_print("reply mode Normal\n");
1282 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1283 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1285 case COMPOSE_REPLY_TO_SENDER:
1286 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1287 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1288 debug_print("reply mode Sender\n");
1289 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1291 case COMPOSE_REPLY_TO_ALL:
1292 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1293 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1294 debug_print("reply mode All\n");
1295 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1297 case COMPOSE_REPLY_TO_LIST:
1298 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1299 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1300 debug_print("reply mode List\n");
1301 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1309 static Compose *compose_reply(MsgInfo *msginfo,
1310 ComposeQuoteMode quote_mode,
1316 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1317 to_sender, FALSE, body);
1320 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1321 ComposeQuoteMode quote_mode,
1326 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1327 to_sender, TRUE, body);
1330 static void compose_extract_original_charset(Compose *compose)
1332 MsgInfo *info = NULL;
1333 if (compose->replyinfo) {
1334 info = compose->replyinfo;
1335 } else if (compose->fwdinfo) {
1336 info = compose->fwdinfo;
1337 } else if (compose->targetinfo) {
1338 info = compose->targetinfo;
1341 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1342 MimeInfo *partinfo = mimeinfo;
1343 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1344 partinfo = procmime_mimeinfo_next(partinfo);
1346 compose->orig_charset =
1347 g_strdup(procmime_mimeinfo_get_parameter(
1348 partinfo, "charset"));
1350 procmime_mimeinfo_free_all(mimeinfo);
1354 #define SIGNAL_BLOCK(buffer) { \
1355 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1356 G_CALLBACK(compose_changed_cb), \
1358 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1359 G_CALLBACK(text_inserted), \
1363 #define SIGNAL_UNBLOCK(buffer) { \
1364 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1365 G_CALLBACK(compose_changed_cb), \
1367 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1368 G_CALLBACK(text_inserted), \
1372 static Compose *compose_generic_reply(MsgInfo *msginfo,
1373 ComposeQuoteMode quote_mode,
1374 gboolean to_all, gboolean to_ml,
1376 gboolean followup_and_reply_to,
1380 PrefsAccount *account = NULL;
1381 GtkTextView *textview;
1382 GtkTextBuffer *textbuf;
1383 gboolean quote = FALSE;
1384 const gchar *qmark = NULL;
1385 const gchar *body_fmt = NULL;
1387 g_return_val_if_fail(msginfo != NULL, NULL);
1388 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1390 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1392 g_return_val_if_fail(account != NULL, NULL);
1394 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1396 compose->updating = TRUE;
1398 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1399 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1401 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1402 if (!compose->replyinfo)
1403 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1405 compose_extract_original_charset(compose);
1407 if (msginfo->folder && msginfo->folder->ret_rcpt)
1408 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1410 /* Set save folder */
1411 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1412 gchar *folderidentifier;
1414 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1415 folderidentifier = folder_item_get_identifier(msginfo->folder);
1416 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1417 g_free(folderidentifier);
1420 if (compose_parse_header(compose, msginfo) < 0) {
1421 compose->updating = FALSE;
1422 compose_destroy(compose);
1426 /* override from name according to folder properties */
1427 if (msginfo->folder && msginfo->folder->prefs &&
1428 msginfo->folder->prefs->reply_with_format &&
1429 msginfo->folder->prefs->reply_override_from_format &&
1430 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1435 /* decode \-escape sequences in the internal representation of the quote format */
1436 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1437 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1440 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1441 compose->gtkaspell);
1443 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1445 quote_fmt_scan_string(tmp);
1448 buf = quote_fmt_get_buffer();
1450 alertpanel_error(_("Message reply From format error."));
1452 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1453 quote_fmt_reset_vartable();
1458 textview = (GTK_TEXT_VIEW(compose->text));
1459 textbuf = gtk_text_view_get_buffer(textview);
1460 compose_create_tags(textview, compose);
1462 undo_block(compose->undostruct);
1464 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1467 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1468 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1469 /* use the reply format of folder (if enabled), or the account's one
1470 (if enabled) or fallback to the global reply format, which is always
1471 enabled (even if empty), and use the relevant quotemark */
1473 if (msginfo->folder && msginfo->folder->prefs &&
1474 msginfo->folder->prefs->reply_with_format) {
1475 qmark = msginfo->folder->prefs->reply_quotemark;
1476 body_fmt = msginfo->folder->prefs->reply_body_format;
1478 } else if (account->reply_with_format) {
1479 qmark = account->reply_quotemark;
1480 body_fmt = account->reply_body_format;
1483 qmark = prefs_common.quotemark;
1484 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1485 body_fmt = gettext(prefs_common.quotefmt);
1492 /* empty quotemark is not allowed */
1493 if (qmark == NULL || *qmark == '\0')
1495 compose_quote_fmt(compose, compose->replyinfo,
1496 body_fmt, qmark, body, FALSE, TRUE,
1497 _("Message reply format error at line %d."));
1498 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1499 quote_fmt_reset_vartable();
1502 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1503 compose_force_encryption(compose, account, FALSE);
1506 privacy_msginfo_get_signed_state(compose->replyinfo);
1507 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1508 compose_force_signing(compose, account);
1511 SIGNAL_BLOCK(textbuf);
1513 if (account->auto_sig)
1514 compose_insert_sig(compose, FALSE);
1516 compose_wrap_all(compose);
1518 SIGNAL_UNBLOCK(textbuf);
1520 gtk_widget_grab_focus(compose->text);
1522 undo_unblock(compose->undostruct);
1524 if (prefs_common.auto_exteditor)
1525 compose_exec_ext_editor(compose);
1527 compose->modified = FALSE;
1528 compose_set_title(compose);
1530 compose->updating = FALSE;
1531 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1532 SCROLL_TO_CURSOR(compose);
1534 if (compose->deferred_destroy) {
1535 compose_destroy(compose);
1542 #define INSERT_FW_HEADER(var, hdr) \
1543 if (msginfo->var && *msginfo->var) { \
1544 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1545 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1546 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1549 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1550 gboolean as_attach, const gchar *body,
1551 gboolean no_extedit,
1555 GtkTextView *textview;
1556 GtkTextBuffer *textbuf;
1559 g_return_val_if_fail(msginfo != NULL, NULL);
1560 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1563 !(account = compose_guess_forward_account_from_msginfo
1565 account = cur_account;
1567 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1569 compose->updating = TRUE;
1570 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1571 if (!compose->fwdinfo)
1572 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1574 compose_extract_original_charset(compose);
1576 if (msginfo->subject && *msginfo->subject) {
1577 gchar *buf, *buf2, *p;
1579 buf = p = g_strdup(msginfo->subject);
1580 p += subject_get_prefix_length(p);
1581 memmove(buf, p, strlen(p) + 1);
1583 buf2 = g_strdup_printf("Fw: %s", buf);
1584 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1590 /* override from name according to folder properties */
1591 if (msginfo->folder && msginfo->folder->prefs &&
1592 msginfo->folder->prefs->forward_with_format &&
1593 msginfo->folder->prefs->forward_override_from_format &&
1594 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1598 MsgInfo *full_msginfo = NULL;
1601 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1603 full_msginfo = procmsg_msginfo_copy(msginfo);
1605 /* decode \-escape sequences in the internal representation of the quote format */
1606 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1607 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1610 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1611 compose->gtkaspell);
1613 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1615 quote_fmt_scan_string(tmp);
1618 buf = quote_fmt_get_buffer();
1620 alertpanel_error(_("Message forward From format error."));
1622 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1623 quote_fmt_reset_vartable();
1626 procmsg_msginfo_free(full_msginfo);
1629 textview = GTK_TEXT_VIEW(compose->text);
1630 textbuf = gtk_text_view_get_buffer(textview);
1631 compose_create_tags(textview, compose);
1633 undo_block(compose->undostruct);
1637 msgfile = procmsg_get_message_file(msginfo);
1638 if (!is_file_exist(msgfile))
1639 g_warning("%s: file not exist\n", msgfile);
1641 compose_attach_append(compose, msgfile, msgfile,
1646 const gchar *qmark = NULL;
1647 const gchar *body_fmt = NULL;
1648 MsgInfo *full_msginfo;
1650 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1651 body_fmt = gettext(prefs_common.fw_quotefmt);
1655 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1657 full_msginfo = procmsg_msginfo_copy(msginfo);
1659 /* use the forward format of folder (if enabled), or the account's one
1660 (if enabled) or fallback to the global forward format, which is always
1661 enabled (even if empty), and use the relevant quotemark */
1662 if (msginfo->folder && msginfo->folder->prefs &&
1663 msginfo->folder->prefs->forward_with_format) {
1664 qmark = msginfo->folder->prefs->forward_quotemark;
1665 body_fmt = msginfo->folder->prefs->forward_body_format;
1667 } else if (account->forward_with_format) {
1668 qmark = account->forward_quotemark;
1669 body_fmt = account->forward_body_format;
1672 qmark = prefs_common.fw_quotemark;
1673 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1674 body_fmt = gettext(prefs_common.fw_quotefmt);
1679 /* empty quotemark is not allowed */
1680 if (qmark == NULL || *qmark == '\0')
1683 compose_quote_fmt(compose, full_msginfo,
1684 body_fmt, qmark, body, FALSE, TRUE,
1685 _("Message forward format error at line %d."));
1686 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1687 quote_fmt_reset_vartable();
1688 compose_attach_parts(compose, msginfo);
1690 procmsg_msginfo_free(full_msginfo);
1693 SIGNAL_BLOCK(textbuf);
1695 if (account->auto_sig)
1696 compose_insert_sig(compose, FALSE);
1698 compose_wrap_all(compose);
1700 SIGNAL_UNBLOCK(textbuf);
1702 gtk_text_buffer_get_start_iter(textbuf, &iter);
1703 gtk_text_buffer_place_cursor(textbuf, &iter);
1705 gtk_widget_grab_focus(compose->header_last->entry);
1707 if (!no_extedit && prefs_common.auto_exteditor)
1708 compose_exec_ext_editor(compose);
1711 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1712 gchar *folderidentifier;
1714 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1715 folderidentifier = folder_item_get_identifier(msginfo->folder);
1716 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1717 g_free(folderidentifier);
1720 undo_unblock(compose->undostruct);
1722 compose->modified = FALSE;
1723 compose_set_title(compose);
1725 compose->updating = FALSE;
1726 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1727 SCROLL_TO_CURSOR(compose);
1729 if (compose->deferred_destroy) {
1730 compose_destroy(compose);
1737 #undef INSERT_FW_HEADER
1739 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1742 GtkTextView *textview;
1743 GtkTextBuffer *textbuf;
1747 gboolean single_mail = TRUE;
1749 g_return_val_if_fail(msginfo_list != NULL, NULL);
1751 if (g_slist_length(msginfo_list) > 1)
1752 single_mail = FALSE;
1754 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1755 if (((MsgInfo *)msginfo->data)->folder == NULL)
1758 /* guess account from first selected message */
1760 !(account = compose_guess_forward_account_from_msginfo
1761 (msginfo_list->data)))
1762 account = cur_account;
1764 g_return_val_if_fail(account != NULL, NULL);
1766 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1767 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1768 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1771 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1773 compose->updating = TRUE;
1775 /* override from name according to folder properties */
1776 if (msginfo_list->data) {
1777 MsgInfo *msginfo = msginfo_list->data;
1779 if (msginfo->folder && msginfo->folder->prefs &&
1780 msginfo->folder->prefs->forward_with_format &&
1781 msginfo->folder->prefs->forward_override_from_format &&
1782 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1787 /* decode \-escape sequences in the internal representation of the quote format */
1788 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1789 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1792 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1793 compose->gtkaspell);
1795 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1797 quote_fmt_scan_string(tmp);
1800 buf = quote_fmt_get_buffer();
1802 alertpanel_error(_("Message forward From format error."));
1804 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1805 quote_fmt_reset_vartable();
1811 textview = GTK_TEXT_VIEW(compose->text);
1812 textbuf = gtk_text_view_get_buffer(textview);
1813 compose_create_tags(textview, compose);
1815 undo_block(compose->undostruct);
1816 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1817 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1819 if (!is_file_exist(msgfile))
1820 g_warning("%s: file not exist\n", msgfile);
1822 compose_attach_append(compose, msgfile, msgfile,
1828 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1829 if (info->subject && *info->subject) {
1830 gchar *buf, *buf2, *p;
1832 buf = p = g_strdup(info->subject);
1833 p += subject_get_prefix_length(p);
1834 memmove(buf, p, strlen(p) + 1);
1836 buf2 = g_strdup_printf("Fw: %s", buf);
1837 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1843 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1844 _("Fw: multiple emails"));
1847 SIGNAL_BLOCK(textbuf);
1849 if (account->auto_sig)
1850 compose_insert_sig(compose, FALSE);
1852 compose_wrap_all(compose);
1854 SIGNAL_UNBLOCK(textbuf);
1856 gtk_text_buffer_get_start_iter(textbuf, &iter);
1857 gtk_text_buffer_place_cursor(textbuf, &iter);
1859 gtk_widget_grab_focus(compose->header_last->entry);
1860 undo_unblock(compose->undostruct);
1861 compose->modified = FALSE;
1862 compose_set_title(compose);
1864 compose->updating = FALSE;
1865 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1866 SCROLL_TO_CURSOR(compose);
1868 if (compose->deferred_destroy) {
1869 compose_destroy(compose);
1876 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1878 GtkTextIter start = *iter;
1879 GtkTextIter end_iter;
1880 int start_pos = gtk_text_iter_get_offset(&start);
1882 if (!compose->account->sig_sep)
1885 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1886 start_pos+strlen(compose->account->sig_sep));
1888 /* check sig separator */
1889 str = gtk_text_iter_get_text(&start, &end_iter);
1890 if (!strcmp(str, compose->account->sig_sep)) {
1892 /* check end of line (\n) */
1893 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1894 start_pos+strlen(compose->account->sig_sep));
1895 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1896 start_pos+strlen(compose->account->sig_sep)+1);
1897 tmp = gtk_text_iter_get_text(&start, &end_iter);
1898 if (!strcmp(tmp,"\n")) {
1910 static void compose_colorize_signature(Compose *compose)
1912 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1914 GtkTextIter end_iter;
1915 gtk_text_buffer_get_start_iter(buffer, &iter);
1916 while (gtk_text_iter_forward_line(&iter))
1917 if (compose_is_sig_separator(compose, buffer, &iter)) {
1918 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1919 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1923 #define BLOCK_WRAP() { \
1924 prev_autowrap = compose->autowrap; \
1925 buffer = gtk_text_view_get_buffer( \
1926 GTK_TEXT_VIEW(compose->text)); \
1927 compose->autowrap = FALSE; \
1929 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1930 G_CALLBACK(compose_changed_cb), \
1932 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1933 G_CALLBACK(text_inserted), \
1936 #define UNBLOCK_WRAP() { \
1937 compose->autowrap = prev_autowrap; \
1938 if (compose->autowrap) { \
1939 gint old = compose->draft_timeout_tag; \
1940 compose->draft_timeout_tag = -2; \
1941 compose_wrap_all(compose); \
1942 compose->draft_timeout_tag = old; \
1945 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1946 G_CALLBACK(compose_changed_cb), \
1948 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1949 G_CALLBACK(text_inserted), \
1953 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
1955 Compose *compose = NULL;
1956 PrefsAccount *account = NULL;
1957 GtkTextView *textview;
1958 GtkTextBuffer *textbuf;
1962 gchar buf[BUFFSIZE];
1963 gboolean use_signing = FALSE;
1964 gboolean use_encryption = FALSE;
1965 gchar *privacy_system = NULL;
1966 int priority = PRIORITY_NORMAL;
1967 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
1968 gboolean autowrap = prefs_common.autowrap;
1969 gboolean autoindent = prefs_common.auto_indent;
1971 g_return_val_if_fail(msginfo != NULL, NULL);
1972 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1974 if (compose_put_existing_to_front(msginfo)) {
1978 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
1979 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
1980 gchar queueheader_buf[BUFFSIZE];
1983 /* Select Account from queue headers */
1984 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1985 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
1986 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
1987 account = account_find_from_id(id);
1989 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1990 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
1991 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
1992 account = account_find_from_id(id);
1994 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1995 sizeof(queueheader_buf), "NAID:")) {
1996 id = atoi(&queueheader_buf[strlen("NAID:")]);
1997 account = account_find_from_id(id);
1999 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2000 sizeof(queueheader_buf), "MAID:")) {
2001 id = atoi(&queueheader_buf[strlen("MAID:")]);
2002 account = account_find_from_id(id);
2004 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2005 sizeof(queueheader_buf), "S:")) {
2006 account = account_find_from_address(queueheader_buf, FALSE);
2008 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2009 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2010 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2011 use_signing = param;
2014 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2015 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2016 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2017 use_signing = param;
2020 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2021 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2022 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2023 use_encryption = param;
2025 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2026 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2027 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2028 use_encryption = param;
2030 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2031 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2032 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2035 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2036 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2037 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2040 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2041 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2042 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2044 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2045 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2046 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2048 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2049 sizeof(queueheader_buf), "X-Priority: ")) {
2050 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2053 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2054 sizeof(queueheader_buf), "RMID:")) {
2055 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2056 if (tokens[0] && tokens[1] && tokens[2]) {
2057 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2058 if (orig_item != NULL) {
2059 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2064 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2065 sizeof(queueheader_buf), "FMID:")) {
2066 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2067 if (tokens[0] && tokens[1] && tokens[2]) {
2068 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2069 if (orig_item != NULL) {
2070 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2076 account = msginfo->folder->folder->account;
2079 if (!account && prefs_common.reedit_account_autosel) {
2080 gchar from[BUFFSIZE];
2081 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2082 extract_address(from);
2083 account = account_find_from_address(from, FALSE);
2087 account = cur_account;
2089 g_return_val_if_fail(account != NULL, NULL);
2091 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2093 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2094 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2095 compose->autowrap = autowrap;
2096 compose->replyinfo = replyinfo;
2097 compose->fwdinfo = fwdinfo;
2099 compose->updating = TRUE;
2100 compose->priority = priority;
2102 if (privacy_system != NULL) {
2103 compose->privacy_system = privacy_system;
2104 compose_use_signing(compose, use_signing);
2105 compose_use_encryption(compose, use_encryption);
2106 compose_update_privacy_system_menu_item(compose, FALSE);
2108 activate_privacy_system(compose, account, FALSE);
2111 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2113 compose_extract_original_charset(compose);
2115 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2116 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2117 gchar queueheader_buf[BUFFSIZE];
2119 /* Set message save folder */
2120 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2123 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2124 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
2125 gtk_editable_insert_text(GTK_EDITABLE(compose->savemsg_entry), &queueheader_buf[4], strlen(&queueheader_buf[4]), &startpos);
2127 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2128 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2130 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2135 if (compose_parse_header(compose, msginfo) < 0) {
2136 compose->updating = FALSE;
2137 compose_destroy(compose);
2140 compose_reedit_set_entry(compose, msginfo);
2142 textview = GTK_TEXT_VIEW(compose->text);
2143 textbuf = gtk_text_view_get_buffer(textview);
2144 compose_create_tags(textview, compose);
2146 mark = gtk_text_buffer_get_insert(textbuf);
2147 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2149 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2150 G_CALLBACK(compose_changed_cb),
2153 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2154 fp = procmime_get_first_encrypted_text_content(msginfo);
2156 compose_force_encryption(compose, account, TRUE);
2159 fp = procmime_get_first_text_content(msginfo);
2162 g_warning("Can't get text part\n");
2166 gboolean prev_autowrap = compose->autowrap;
2167 GtkTextBuffer *buffer = textbuf;
2169 while (fgets(buf, sizeof(buf), fp) != NULL) {
2171 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2177 compose_attach_parts(compose, msginfo);
2179 compose_colorize_signature(compose);
2181 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2182 G_CALLBACK(compose_changed_cb),
2185 gtk_widget_grab_focus(compose->text);
2187 if (prefs_common.auto_exteditor) {
2188 compose_exec_ext_editor(compose);
2190 compose->modified = FALSE;
2191 compose_set_title(compose);
2193 compose->updating = FALSE;
2194 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2195 SCROLL_TO_CURSOR(compose);
2197 if (compose->deferred_destroy) {
2198 compose_destroy(compose);
2202 compose->sig_str = account_get_signature_str(compose->account);
2207 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2214 g_return_val_if_fail(msginfo != NULL, NULL);
2217 account = account_get_reply_account(msginfo,
2218 prefs_common.reply_account_autosel);
2219 g_return_val_if_fail(account != NULL, NULL);
2221 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2223 compose->updating = TRUE;
2225 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2226 compose->replyinfo = NULL;
2227 compose->fwdinfo = NULL;
2229 compose_show_first_last_header(compose, TRUE);
2231 gtk_widget_grab_focus(compose->header_last->entry);
2233 filename = procmsg_get_message_file(msginfo);
2235 if (filename == NULL) {
2236 compose->updating = FALSE;
2237 compose_destroy(compose);
2242 compose->redirect_filename = filename;
2244 /* Set save folder */
2245 item = msginfo->folder;
2246 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2247 gchar *folderidentifier;
2249 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2250 folderidentifier = folder_item_get_identifier(item);
2251 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
2252 g_free(folderidentifier);
2255 compose_attach_parts(compose, msginfo);
2257 if (msginfo->subject)
2258 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2260 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2262 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2263 _("Message redirect format error at line %d."));
2264 quote_fmt_reset_vartable();
2265 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2267 compose_colorize_signature(compose);
2270 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2271 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2272 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2274 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2275 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2276 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2277 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2278 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2279 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2280 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2281 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2283 if (compose->toolbar->draft_btn)
2284 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2285 if (compose->toolbar->insert_btn)
2286 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2287 if (compose->toolbar->attach_btn)
2288 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2289 if (compose->toolbar->sig_btn)
2290 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2291 if (compose->toolbar->exteditor_btn)
2292 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2293 if (compose->toolbar->linewrap_current_btn)
2294 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2295 if (compose->toolbar->linewrap_all_btn)
2296 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2298 compose->modified = FALSE;
2299 compose_set_title(compose);
2300 compose->updating = FALSE;
2301 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2302 SCROLL_TO_CURSOR(compose);
2304 if (compose->deferred_destroy) {
2305 compose_destroy(compose);
2312 GList *compose_get_compose_list(void)
2314 return compose_list;
2317 void compose_entry_append(Compose *compose, const gchar *address,
2318 ComposeEntryType type)
2320 const gchar *header;
2322 gboolean in_quote = FALSE;
2323 if (!address || *address == '\0') return;
2330 header = N_("Bcc:");
2332 case COMPOSE_REPLYTO:
2333 header = N_("Reply-To:");
2335 case COMPOSE_NEWSGROUPS:
2336 header = N_("Newsgroups:");
2338 case COMPOSE_FOLLOWUPTO:
2339 header = N_( "Followup-To:");
2346 header = prefs_common_translated_header_name(header);
2348 cur = begin = (gchar *)address;
2350 /* we separate the line by commas, but not if we're inside a quoted
2352 while (*cur != '\0') {
2354 in_quote = !in_quote;
2355 if (*cur == ',' && !in_quote) {
2356 gchar *tmp = g_strdup(begin);
2358 tmp[cur-begin]='\0';
2361 while (*tmp == ' ' || *tmp == '\t')
2363 compose_add_header_entry(compose, header, tmp);
2370 gchar *tmp = g_strdup(begin);
2372 tmp[cur-begin]='\0';
2375 while (*tmp == ' ' || *tmp == '\t')
2377 compose_add_header_entry(compose, header, tmp);
2382 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2384 static GdkColor yellow;
2385 static GdkColor black;
2386 static gboolean yellow_initialised = FALSE;
2390 if (!yellow_initialised) {
2391 gdk_color_parse("#f5f6be", &yellow);
2392 gdk_color_parse("#000000", &black);
2393 yellow_initialised = gdk_colormap_alloc_color(
2394 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2395 yellow_initialised &= gdk_colormap_alloc_color(
2396 gdk_colormap_get_system(), &black, FALSE, TRUE);
2399 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2400 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2401 if (gtk_entry_get_text(entry) &&
2402 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2403 if (yellow_initialised) {
2404 gtk_widget_modify_base(
2405 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2406 GTK_STATE_NORMAL, &yellow);
2407 gtk_widget_modify_text(
2408 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2409 GTK_STATE_NORMAL, &black);
2415 void compose_toolbar_cb(gint action, gpointer data)
2417 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2418 Compose *compose = (Compose*)toolbar_item->parent;
2420 g_return_if_fail(compose != NULL);
2424 compose_send_cb(NULL, compose);
2427 compose_send_later_cb(NULL, compose);
2430 compose_draft(compose, COMPOSE_QUIT_EDITING);
2433 compose_insert_file_cb(NULL, compose);
2436 compose_attach_cb(NULL, compose);
2439 compose_insert_sig(compose, FALSE);
2442 compose_ext_editor_cb(NULL, compose);
2444 case A_LINEWRAP_CURRENT:
2445 compose_beautify_paragraph(compose, NULL, TRUE);
2447 case A_LINEWRAP_ALL:
2448 compose_wrap_all_full(compose, TRUE);
2451 compose_address_cb(NULL, compose);
2454 case A_CHECK_SPELLING:
2455 compose_check_all(NULL, compose);
2463 static void compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2468 gchar *subject = NULL;
2472 gchar **attach = NULL;
2474 /* get mailto parts but skip from */
2475 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2478 compose_entry_append(compose, to, to_type);
2480 compose_entry_append(compose, cc, COMPOSE_CC);
2482 compose_entry_append(compose, bcc, COMPOSE_BCC);
2484 if (!g_utf8_validate (subject, -1, NULL)) {
2485 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2486 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2489 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2493 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2494 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2497 gboolean prev_autowrap = compose->autowrap;
2499 compose->autowrap = FALSE;
2501 mark = gtk_text_buffer_get_insert(buffer);
2502 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2504 if (!g_utf8_validate (body, -1, NULL)) {
2505 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2506 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2509 gtk_text_buffer_insert(buffer, &iter, body, -1);
2511 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2513 compose->autowrap = prev_autowrap;
2514 if (compose->autowrap)
2515 compose_wrap_all(compose);
2519 gint i = 0, att = 0;
2520 gchar *warn_files = NULL;
2521 while (attach[i] != NULL) {
2522 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2523 if (utf8_filename) {
2524 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2525 gchar *tmp = g_strdup_printf("%s%s\n",
2526 warn_files?warn_files:"",
2532 g_free(utf8_filename);
2534 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2539 alertpanel_notice(ngettext(
2540 "The following file has been attached: \n%s",
2541 "The following files have been attached: \n%s", att), warn_files);
2553 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2555 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2556 {"Cc:", NULL, TRUE},
2557 {"References:", NULL, FALSE},
2558 {"Bcc:", NULL, TRUE},
2559 {"Newsgroups:", NULL, TRUE},
2560 {"Followup-To:", NULL, TRUE},
2561 {"List-Post:", NULL, FALSE},
2562 {"X-Priority:", NULL, FALSE},
2563 {NULL, NULL, FALSE}};
2579 g_return_val_if_fail(msginfo != NULL, -1);
2581 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2582 procheader_get_header_fields(fp, hentry);
2585 if (hentry[H_REPLY_TO].body != NULL) {
2586 if (hentry[H_REPLY_TO].body[0] != '\0') {
2588 conv_unmime_header(hentry[H_REPLY_TO].body,
2591 g_free(hentry[H_REPLY_TO].body);
2592 hentry[H_REPLY_TO].body = NULL;
2594 if (hentry[H_CC].body != NULL) {
2595 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2596 g_free(hentry[H_CC].body);
2597 hentry[H_CC].body = NULL;
2599 if (hentry[H_REFERENCES].body != NULL) {
2600 if (compose->mode == COMPOSE_REEDIT)
2601 compose->references = hentry[H_REFERENCES].body;
2603 compose->references = compose_parse_references
2604 (hentry[H_REFERENCES].body, msginfo->msgid);
2605 g_free(hentry[H_REFERENCES].body);
2607 hentry[H_REFERENCES].body = NULL;
2609 if (hentry[H_BCC].body != NULL) {
2610 if (compose->mode == COMPOSE_REEDIT)
2612 conv_unmime_header(hentry[H_BCC].body, NULL);
2613 g_free(hentry[H_BCC].body);
2614 hentry[H_BCC].body = NULL;
2616 if (hentry[H_NEWSGROUPS].body != NULL) {
2617 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2618 hentry[H_NEWSGROUPS].body = NULL;
2620 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2621 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2622 compose->followup_to =
2623 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2626 g_free(hentry[H_FOLLOWUP_TO].body);
2627 hentry[H_FOLLOWUP_TO].body = NULL;
2629 if (hentry[H_LIST_POST].body != NULL) {
2632 extract_address(hentry[H_LIST_POST].body);
2633 if (hentry[H_LIST_POST].body[0] != '\0') {
2634 scan_mailto_url(hentry[H_LIST_POST].body,
2635 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2637 g_free(compose->ml_post);
2638 compose->ml_post = to;
2641 g_free(hentry[H_LIST_POST].body);
2642 hentry[H_LIST_POST].body = NULL;
2645 /* CLAWS - X-Priority */
2646 if (compose->mode == COMPOSE_REEDIT)
2647 if (hentry[H_X_PRIORITY].body != NULL) {
2650 priority = atoi(hentry[H_X_PRIORITY].body);
2651 g_free(hentry[H_X_PRIORITY].body);
2653 hentry[H_X_PRIORITY].body = NULL;
2655 if (priority < PRIORITY_HIGHEST ||
2656 priority > PRIORITY_LOWEST)
2657 priority = PRIORITY_NORMAL;
2659 compose->priority = priority;
2662 if (compose->mode == COMPOSE_REEDIT) {
2663 if (msginfo->inreplyto && *msginfo->inreplyto)
2664 compose->inreplyto = g_strdup(msginfo->inreplyto);
2668 if (msginfo->msgid && *msginfo->msgid)
2669 compose->inreplyto = g_strdup(msginfo->msgid);
2671 if (!compose->references) {
2672 if (msginfo->msgid && *msginfo->msgid) {
2673 if (msginfo->inreplyto && *msginfo->inreplyto)
2674 compose->references =
2675 g_strdup_printf("<%s>\n\t<%s>",
2679 compose->references =
2680 g_strconcat("<", msginfo->msgid, ">",
2682 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2683 compose->references =
2684 g_strconcat("<", msginfo->inreplyto, ">",
2692 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2694 GSList *ref_id_list, *cur;
2698 ref_id_list = references_list_append(NULL, ref);
2699 if (!ref_id_list) return NULL;
2700 if (msgid && *msgid)
2701 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2706 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2707 /* "<" + Message-ID + ">" + CR+LF+TAB */
2708 len += strlen((gchar *)cur->data) + 5;
2710 if (len > MAX_REFERENCES_LEN) {
2711 /* remove second message-ID */
2712 if (ref_id_list && ref_id_list->next &&
2713 ref_id_list->next->next) {
2714 g_free(ref_id_list->next->data);
2715 ref_id_list = g_slist_remove
2716 (ref_id_list, ref_id_list->next->data);
2718 slist_free_strings(ref_id_list);
2719 g_slist_free(ref_id_list);
2726 new_ref = g_string_new("");
2727 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2728 if (new_ref->len > 0)
2729 g_string_append(new_ref, "\n\t");
2730 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2733 slist_free_strings(ref_id_list);
2734 g_slist_free(ref_id_list);
2736 new_ref_str = new_ref->str;
2737 g_string_free(new_ref, FALSE);
2742 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2743 const gchar *fmt, const gchar *qmark,
2744 const gchar *body, gboolean rewrap,
2745 gboolean need_unescape,
2746 const gchar *err_msg)
2748 MsgInfo* dummyinfo = NULL;
2749 gchar *quote_str = NULL;
2751 gboolean prev_autowrap;
2752 const gchar *trimmed_body = body;
2753 gint cursor_pos = -1;
2754 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2755 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2760 SIGNAL_BLOCK(buffer);
2763 dummyinfo = compose_msginfo_new_from_compose(compose);
2764 msginfo = dummyinfo;
2767 if (qmark != NULL) {
2769 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2770 compose->gtkaspell);
2772 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2774 quote_fmt_scan_string(qmark);
2777 buf = quote_fmt_get_buffer();
2779 alertpanel_error(_("Quote mark format error."));
2781 Xstrdup_a(quote_str, buf, goto error)
2784 if (fmt && *fmt != '\0') {
2787 while (*trimmed_body == '\n')
2791 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2792 compose->gtkaspell);
2794 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2796 if (need_unescape) {
2799 /* decode \-escape sequences in the internal representation of the quote format */
2800 tmp = malloc(strlen(fmt)+1);
2801 pref_get_unescaped_pref(tmp, fmt);
2802 quote_fmt_scan_string(tmp);
2806 quote_fmt_scan_string(fmt);
2810 buf = quote_fmt_get_buffer();
2812 gint line = quote_fmt_get_line();
2813 alertpanel_error(err_msg, line);
2819 prev_autowrap = compose->autowrap;
2820 compose->autowrap = FALSE;
2822 mark = gtk_text_buffer_get_insert(buffer);
2823 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2824 if (g_utf8_validate(buf, -1, NULL)) {
2825 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2827 gchar *tmpout = NULL;
2828 tmpout = conv_codeset_strdup
2829 (buf, conv_get_locale_charset_str_no_utf8(),
2831 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2833 tmpout = g_malloc(strlen(buf)*2+1);
2834 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2836 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2840 cursor_pos = quote_fmt_get_cursor_pos();
2841 if (cursor_pos == -1)
2842 cursor_pos = gtk_text_iter_get_offset(&iter);
2843 compose->set_cursor_pos = cursor_pos;
2845 gtk_text_buffer_get_start_iter(buffer, &iter);
2846 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2847 gtk_text_buffer_place_cursor(buffer, &iter);
2849 compose->autowrap = prev_autowrap;
2850 if (compose->autowrap && rewrap)
2851 compose_wrap_all(compose);
2858 SIGNAL_UNBLOCK(buffer);
2860 procmsg_msginfo_free( dummyinfo );
2865 /* if ml_post is of type addr@host and from is of type
2866 * addr-anything@host, return TRUE
2868 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2870 gchar *left_ml = NULL;
2871 gchar *right_ml = NULL;
2872 gchar *left_from = NULL;
2873 gchar *right_from = NULL;
2874 gboolean result = FALSE;
2876 if (!ml_post || !from)
2879 left_ml = g_strdup(ml_post);
2880 if (strstr(left_ml, "@")) {
2881 right_ml = strstr(left_ml, "@")+1;
2882 *(strstr(left_ml, "@")) = '\0';
2885 left_from = g_strdup(from);
2886 if (strstr(left_from, "@")) {
2887 right_from = strstr(left_from, "@")+1;
2888 *(strstr(left_from, "@")) = '\0';
2891 if (left_ml && left_from && right_ml && right_from
2892 && !strncmp(left_from, left_ml, strlen(left_ml))
2893 && !strcmp(right_from, right_ml)) {
2902 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2904 gchar *my_addr1, *my_addr2;
2906 if (!addr1 || !addr2)
2909 Xstrdup_a(my_addr1, addr1, return FALSE);
2910 Xstrdup_a(my_addr2, addr2, return FALSE);
2912 extract_address(my_addr1);
2913 extract_address(my_addr2);
2915 return !strcasecmp(my_addr1, my_addr2);
2918 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2919 gboolean to_all, gboolean to_ml,
2921 gboolean followup_and_reply_to)
2923 GSList *cc_list = NULL;
2926 gchar *replyto = NULL;
2927 GHashTable *to_table;
2929 gboolean reply_to_ml = FALSE;
2930 gboolean default_reply_to = FALSE;
2932 g_return_if_fail(compose->account != NULL);
2933 g_return_if_fail(msginfo != NULL);
2935 reply_to_ml = to_ml && compose->ml_post;
2937 default_reply_to = msginfo->folder &&
2938 msginfo->folder->prefs->enable_default_reply_to;
2940 if (compose->account->protocol != A_NNTP) {
2941 if (reply_to_ml && !default_reply_to) {
2943 gboolean is_subscr = is_subscription(compose->ml_post,
2946 /* normal answer to ml post with a reply-to */
2947 compose_entry_append(compose,
2950 if (compose->replyto
2951 && !same_address(compose->ml_post, compose->replyto))
2952 compose_entry_append(compose,
2956 /* answer to subscription confirmation */
2957 if (compose->replyto)
2958 compose_entry_append(compose,
2961 else if (msginfo->from)
2962 compose_entry_append(compose,
2967 else if (!(to_all || to_sender) && default_reply_to) {
2968 compose_entry_append(compose,
2969 msginfo->folder->prefs->default_reply_to,
2971 compose_entry_mark_default_to(compose,
2972 msginfo->folder->prefs->default_reply_to);
2977 Xstrdup_a(tmp1, msginfo->from, return);
2978 extract_address(tmp1);
2979 if (to_all || to_sender ||
2980 !account_find_from_address(tmp1, FALSE))
2981 compose_entry_append(compose,
2982 (compose->replyto && !to_sender)
2983 ? compose->replyto :
2984 msginfo->from ? msginfo->from : "",
2986 else if (!to_all && !to_sender) {
2987 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2988 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
2989 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2990 if (compose->replyto) {
2991 compose_entry_append(compose,
2995 compose_entry_append(compose,
2996 msginfo->from ? msginfo->from : "",
3000 /* replying to own mail, use original recp */
3001 compose_entry_append(compose,
3002 msginfo->to ? msginfo->to : "",
3004 compose_entry_append(compose,
3005 msginfo->cc ? msginfo->cc : "",
3011 if (to_sender || (compose->followup_to &&
3012 !strncmp(compose->followup_to, "poster", 6)))
3013 compose_entry_append
3015 (compose->replyto ? compose->replyto :
3016 msginfo->from ? msginfo->from : ""),
3019 else if (followup_and_reply_to || to_all) {
3020 compose_entry_append
3022 (compose->replyto ? compose->replyto :
3023 msginfo->from ? msginfo->from : ""),
3026 compose_entry_append
3028 compose->followup_to ? compose->followup_to :
3029 compose->newsgroups ? compose->newsgroups : "",
3030 COMPOSE_NEWSGROUPS);
3033 compose_entry_append
3035 compose->followup_to ? compose->followup_to :
3036 compose->newsgroups ? compose->newsgroups : "",
3037 COMPOSE_NEWSGROUPS);
3040 if (msginfo->subject && *msginfo->subject) {
3044 buf = p = g_strdup(msginfo->subject);
3045 p += subject_get_prefix_length(p);
3046 memmove(buf, p, strlen(p) + 1);
3048 buf2 = g_strdup_printf("Re: %s", buf);
3049 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3054 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3056 if (to_ml && compose->ml_post) return;
3057 if (!to_all || compose->account->protocol == A_NNTP) return;
3059 if (compose->replyto) {
3060 Xstrdup_a(replyto, compose->replyto, return);
3061 extract_address(replyto);
3063 if (msginfo->from) {
3064 Xstrdup_a(from, msginfo->from, return);
3065 extract_address(from);
3068 if (replyto && from)
3069 cc_list = address_list_append_with_comments(cc_list, from);
3070 if (to_all && msginfo->folder &&
3071 msginfo->folder->prefs->enable_default_reply_to)
3072 cc_list = address_list_append_with_comments(cc_list,
3073 msginfo->folder->prefs->default_reply_to);
3074 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3075 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3077 to_table = g_hash_table_new(g_str_hash, g_str_equal);
3079 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
3080 if (compose->account) {
3081 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
3082 GINT_TO_POINTER(1));
3084 /* remove address on To: and that of current account */
3085 for (cur = cc_list; cur != NULL; ) {
3086 GSList *next = cur->next;
3089 addr = g_utf8_strdown(cur->data, -1);
3090 extract_address(addr);
3092 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3093 cc_list = g_slist_remove(cc_list, cur->data);
3095 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3099 hash_free_strings(to_table);
3100 g_hash_table_destroy(to_table);
3103 for (cur = cc_list; cur != NULL; cur = cur->next)
3104 compose_entry_append(compose, (gchar *)cur->data,
3106 slist_free_strings(cc_list);
3107 g_slist_free(cc_list);
3112 #define SET_ENTRY(entry, str) \
3115 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3118 #define SET_ADDRESS(type, str) \
3121 compose_entry_append(compose, str, type); \
3124 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3126 g_return_if_fail(msginfo != NULL);
3128 SET_ENTRY(subject_entry, msginfo->subject);
3129 SET_ENTRY(from_name, msginfo->from);
3130 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3131 SET_ADDRESS(COMPOSE_CC, compose->cc);
3132 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3133 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3134 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3135 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3137 compose_update_priority_menu_item(compose);
3138 compose_update_privacy_system_menu_item(compose, FALSE);
3139 compose_show_first_last_header(compose, TRUE);
3145 static void compose_insert_sig(Compose *compose, gboolean replace)
3147 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3148 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3150 GtkTextIter iter, iter_end;
3151 gint cur_pos, ins_pos;
3152 gboolean prev_autowrap;
3153 gboolean found = FALSE;
3154 gboolean exists = FALSE;
3156 g_return_if_fail(compose->account != NULL);
3160 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3161 G_CALLBACK(compose_changed_cb),
3164 mark = gtk_text_buffer_get_insert(buffer);
3165 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3166 cur_pos = gtk_text_iter_get_offset (&iter);
3169 gtk_text_buffer_get_end_iter(buffer, &iter);