2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 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 gchar *compose_get_signature_str (Compose *compose);
242 static ComposeInsertResult compose_insert_file (Compose *compose,
245 static gboolean compose_attach_append (Compose *compose,
248 const gchar *content_type);
249 static void compose_attach_parts (Compose *compose,
252 static gboolean compose_beautify_paragraph (Compose *compose,
253 GtkTextIter *par_iter,
255 static void compose_wrap_all (Compose *compose);
256 static void compose_wrap_all_full (Compose *compose,
259 static void compose_set_title (Compose *compose);
260 static void compose_select_account (Compose *compose,
261 PrefsAccount *account,
264 static PrefsAccount *compose_current_mail_account(void);
265 /* static gint compose_send (Compose *compose); */
266 static gboolean compose_check_for_valid_recipient
268 static gboolean compose_check_entries (Compose *compose,
269 gboolean check_everything);
270 static gint compose_write_to_file (Compose *compose,
273 gboolean attach_parts);
274 static gint compose_write_body_to_file (Compose *compose,
276 static gint compose_remove_reedit_target (Compose *compose,
278 static void compose_remove_draft (Compose *compose);
279 static gint compose_queue_sub (Compose *compose,
283 gboolean check_subject,
284 gboolean remove_reedit_target);
285 static void compose_add_attachments (Compose *compose,
287 static gchar *compose_get_header (Compose *compose);
289 static void compose_convert_header (Compose *compose,
294 gboolean addr_field);
296 static void compose_attach_info_free (AttachInfo *ainfo);
297 static void compose_attach_remove_selected (GtkAction *action,
300 static void compose_attach_property (GtkAction *action,
302 static void compose_attach_property_create (gboolean *cancelled);
303 static void attach_property_ok (GtkWidget *widget,
304 gboolean *cancelled);
305 static void attach_property_cancel (GtkWidget *widget,
306 gboolean *cancelled);
307 static gint attach_property_delete_event (GtkWidget *widget,
309 gboolean *cancelled);
310 static gboolean attach_property_key_pressed (GtkWidget *widget,
312 gboolean *cancelled);
314 static void compose_exec_ext_editor (Compose *compose);
316 static gint compose_exec_ext_editor_real (const gchar *file);
317 static gboolean compose_ext_editor_kill (Compose *compose);
318 static gboolean compose_input_cb (GIOChannel *source,
319 GIOCondition condition,
321 static void compose_set_ext_editor_sensitive (Compose *compose,
323 #endif /* G_OS_UNIX */
325 static void compose_undo_state_changed (UndoMain *undostruct,
330 static void compose_create_header_entry (Compose *compose);
331 static void compose_add_header_entry (Compose *compose, const gchar *header, gchar *text);
332 static void compose_remove_header_entries(Compose *compose);
334 static void compose_update_priority_menu_item(Compose * compose);
336 static void compose_spell_menu_changed (void *data);
338 static void compose_add_field_list ( Compose *compose,
339 GList *listAddress );
341 /* callback functions */
343 static gboolean compose_edit_size_alloc (GtkEditable *widget,
344 GtkAllocation *allocation,
345 GtkSHRuler *shruler);
346 static void account_activated (GtkComboBox *optmenu,
348 static void attach_selected (GtkTreeView *tree_view,
349 GtkTreePath *tree_path,
350 GtkTreeViewColumn *column,
352 static gboolean attach_button_pressed (GtkWidget *widget,
353 GdkEventButton *event,
355 static gboolean attach_key_pressed (GtkWidget *widget,
358 static void compose_send_cb (GtkAction *action, gpointer data);
359 static void compose_send_later_cb (GtkAction *action, gpointer data);
361 static void compose_save_cb (GtkAction *action,
364 static void compose_attach_cb (GtkAction *action,
366 static void compose_insert_file_cb (GtkAction *action,
368 static void compose_insert_sig_cb (GtkAction *action,
371 static void compose_close_cb (GtkAction *action,
374 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
376 static void compose_address_cb (GtkAction *action,
378 static void about_show_cb (GtkAction *action,
380 static void compose_template_activate_cb(GtkWidget *widget,
383 static void compose_ext_editor_cb (GtkAction *action,
386 static gint compose_delete_cb (GtkWidget *widget,
390 static void compose_undo_cb (GtkAction *action,
392 static void compose_redo_cb (GtkAction *action,
394 static void compose_cut_cb (GtkAction *action,
396 static void compose_copy_cb (GtkAction *action,
398 static void compose_paste_cb (GtkAction *action,
400 static void compose_paste_as_quote_cb (GtkAction *action,
402 static void compose_paste_no_wrap_cb (GtkAction *action,
404 static void compose_paste_wrap_cb (GtkAction *action,
406 static void compose_allsel_cb (GtkAction *action,
409 static void compose_advanced_action_cb (GtkAction *action,
412 static void compose_grab_focus_cb (GtkWidget *widget,
415 static void compose_changed_cb (GtkTextBuffer *textbuf,
418 static void compose_wrap_cb (GtkAction *action,
420 static void compose_wrap_all_cb (GtkAction *action,
422 static void compose_find_cb (GtkAction *action,
424 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
426 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
429 static void compose_toggle_ruler_cb (GtkToggleAction *action,
431 static void compose_toggle_sign_cb (GtkToggleAction *action,
433 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
435 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
436 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
437 static void activate_privacy_system (Compose *compose,
438 PrefsAccount *account,
440 static void compose_use_signing(Compose *compose, gboolean use_signing);
441 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
442 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
444 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
446 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
447 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
448 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
450 static void compose_attach_drag_received_cb (GtkWidget *widget,
451 GdkDragContext *drag_context,
454 GtkSelectionData *data,
458 static void compose_insert_drag_received_cb (GtkWidget *widget,
459 GdkDragContext *drag_context,
462 GtkSelectionData *data,
466 static void compose_header_drag_received_cb (GtkWidget *widget,
467 GdkDragContext *drag_context,
470 GtkSelectionData *data,
475 static gboolean compose_drag_drop (GtkWidget *widget,
476 GdkDragContext *drag_context,
478 guint time, gpointer user_data);
480 static void text_inserted (GtkTextBuffer *buffer,
485 static Compose *compose_generic_reply(MsgInfo *msginfo,
486 ComposeQuoteMode quote_mode,
490 gboolean followup_and_reply_to,
493 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
494 ComposeHeaderEntry *headerentry);
495 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
497 ComposeHeaderEntry *headerentry);
499 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
501 static void compose_allow_user_actions (Compose *compose, gboolean allow);
503 static void compose_nothing_cb (GtkAction *action, gpointer data)
509 static void compose_check_all (GtkAction *action, gpointer data);
510 static void compose_highlight_all (GtkAction *action, gpointer data);
511 static void compose_check_backwards (GtkAction *action, gpointer data);
512 static void compose_check_forwards_go (GtkAction *action, gpointer data);
515 static gint compose_defer_auto_save_draft (Compose *compose);
516 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
518 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
521 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
522 FolderItem *folder_item);
524 static void compose_attach_update_label(Compose *compose);
526 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data);
528 static GtkActionEntry compose_popup_entries[] =
530 {"Compose", NULL, "Compose" },
531 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
532 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
533 {"Compose/---", NULL, "---", NULL, NULL, NULL },
534 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
537 static GtkActionEntry compose_entries[] =
539 {"Menu", NULL, "Menu" },
541 {"Message", NULL, N_("_Message") },
542 {"Edit", NULL, N_("_Edit") },
544 {"Spelling", NULL, N_("_Spelling") },
546 {"Options", NULL, N_("_Options") },
547 {"Tools", NULL, N_("_Tools") },
548 {"Help", NULL, N_("_Help") },
550 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
551 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
552 {"Message/---", NULL, "---" },
554 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
555 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
556 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
557 /* {"Message/---", NULL, "---" }, */
558 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
559 /* {"Message/---", NULL, "---" }, */
560 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
563 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
564 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
565 {"Edit/---", NULL, "---" },
567 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
568 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
569 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
571 {"Edit/SpecialPaste", NULL, N_("Special paste") },
572 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
573 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
574 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
576 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
578 {"Edit/Advanced", NULL, N_("A_dvanced") },
579 {"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*/
580 {"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*/
581 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
582 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
583 {"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*/
584 {"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*/
585 {"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*/
586 {"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*/
587 {"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*/
588 {"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*/
589 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
590 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
591 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
592 {"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*/
594 /* {"Edit/---", NULL, "---" }, */
595 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
597 /* {"Edit/---", NULL, "---" }, */
598 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
599 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
600 /* {"Edit/---", NULL, "---" }, */
601 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
604 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
605 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
606 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
607 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
609 {"Spelling/---", NULL, "---" },
610 {"Spelling/Options", NULL, N_("_Options") },
615 {"Options/ReplyMode", NULL, N_("Reply _mode") },
616 {"Options/---", NULL, "---" },
617 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
618 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
620 /* {"Options/---", NULL, "---" }, */
622 {"Options/Priority", NULL, N_("_Priority") },
624 {"Options/Encoding", NULL, N_("Character _encoding") },
625 {"Options/Encoding/---", NULL, "---" },
626 #define ENC_ACTION(cs_char,c_char,string) \
627 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
629 {"Options/Encoding/Western", NULL, N_("Western European") },
630 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
631 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
632 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
633 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
634 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
635 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
636 {"Options/Encoding/Korean", NULL, N_("Korean") },
637 {"Options/Encoding/Thai", NULL, N_("Thai") },
640 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
642 {"Tools/Template", NULL, N_("_Template") },
643 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
644 {"Tools/Actions", NULL, N_("Actio_ns") },
645 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
648 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
651 static GtkToggleActionEntry compose_toggle_entries[] =
653 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
654 {"Edit/AutoIndent", NULL, N_("Follow _indentation"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
655 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
656 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
657 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
658 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
659 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
662 static GtkRadioActionEntry compose_radio_rm_entries[] =
664 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
665 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
666 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
667 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
670 static GtkRadioActionEntry compose_radio_prio_entries[] =
672 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
673 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
674 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
675 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
676 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
679 static GtkRadioActionEntry compose_radio_enc_entries[] =
681 ENC_ACTION(CS_AUTO, C_AUTO, "_Automatic"), /* RADIO compose_set_encoding_cb */
682 ENC_ACTION(CS_US_ASCII, C_US_ASCII, "7bit ASCII (US-ASC_II)"), /* RADIO compose_set_encoding_cb */
683 ENC_ACTION(CS_UTF_8, C_UTF_8, "Unicode (_UTF-8)"), /* RADIO compose_set_encoding_cb */
684 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
685 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
686 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
687 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, "Central European (ISO-8859-_2)"), /* RADIO compose_set_encoding_cb */
688 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
689 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
690 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, "Greek (ISO-8859-_7)"), /* RADIO compose_set_encoding_cb */
691 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
692 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
693 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, "Turkish (ISO-8859-_9)"), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
714 static GtkTargetEntry compose_mime_types[] =
716 {"text/uri-list", 0, 0},
717 {"UTF8_STRING", 0, 0},
721 static gboolean compose_put_existing_to_front(MsgInfo *info)
723 GList *compose_list = compose_get_compose_list();
727 for (elem = compose_list; elem != NULL && elem->data != NULL;
729 Compose *c = (Compose*)elem->data;
731 if (!c->targetinfo || !c->targetinfo->msgid ||
735 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
736 gtkut_window_popup(c->window);
744 static GdkColor quote_color1 =
745 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
746 static GdkColor quote_color2 =
747 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
748 static GdkColor quote_color3 =
749 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
751 static GdkColor quote_bgcolor1 =
752 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
753 static GdkColor quote_bgcolor2 =
754 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
755 static GdkColor quote_bgcolor3 =
756 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
758 static GdkColor signature_color = {
765 static GdkColor uri_color = {
772 static void compose_create_tags(GtkTextView *text, Compose *compose)
774 GtkTextBuffer *buffer;
775 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
781 buffer = gtk_text_view_get_buffer(text);
783 if (prefs_common.enable_color) {
784 /* grab the quote colors, converting from an int to a GdkColor */
785 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
787 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
789 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
791 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
793 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
795 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
797 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
799 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
802 signature_color = quote_color1 = quote_color2 = quote_color3 =
803 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
806 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
807 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
808 "foreground-gdk", "e_color1,
809 "paragraph-background-gdk", "e_bgcolor1,
811 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
812 "foreground-gdk", "e_color2,
813 "paragraph-background-gdk", "e_bgcolor2,
815 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
816 "foreground-gdk", "e_color3,
817 "paragraph-background-gdk", "e_bgcolor3,
820 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
821 "foreground-gdk", "e_color1,
823 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
824 "foreground-gdk", "e_color2,
826 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
827 "foreground-gdk", "e_color3,
831 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
832 "foreground-gdk", &signature_color,
835 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
836 "foreground-gdk", &uri_color,
838 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
839 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
841 color[0] = quote_color1;
842 color[1] = quote_color2;
843 color[2] = quote_color3;
844 color[3] = quote_bgcolor1;
845 color[4] = quote_bgcolor2;
846 color[5] = quote_bgcolor3;
847 color[6] = signature_color;
848 color[7] = uri_color;
849 cmap = gdk_drawable_get_colormap(compose->window->window);
850 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
852 for (i = 0; i < 8; i++) {
853 if (success[i] == FALSE) {
856 g_warning("Compose: color allocation failed.\n");
857 style = gtk_widget_get_style(GTK_WIDGET(text));
858 quote_color1 = quote_color2 = quote_color3 =
859 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
860 signature_color = uri_color = black;
865 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
866 GPtrArray *attach_files)
868 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
871 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
873 return compose_generic_new(account, mailto, item, NULL, NULL);
876 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
878 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
881 #define SCROLL_TO_CURSOR(compose) { \
882 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
883 gtk_text_view_get_buffer( \
884 GTK_TEXT_VIEW(compose->text))); \
885 gtk_text_view_scroll_mark_onscreen( \
886 GTK_TEXT_VIEW(compose->text), \
890 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
891 GPtrArray *attach_files, GList *listAddress )
894 GtkTextView *textview;
895 GtkTextBuffer *textbuf;
897 const gchar *subject_format = NULL;
898 const gchar *body_format = NULL;
899 gchar *mailto_from = NULL;
900 PrefsAccount *mailto_account = NULL;
901 MsgInfo* dummyinfo = NULL;
903 /* check if mailto defines a from */
904 if (mailto && *mailto != '\0') {
905 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL);
906 /* mailto defines a from, check if we can get account prefs from it,
907 if not, the account prefs will be guessed using other ways, but we'll keep
910 mailto_account = account_find_from_address(mailto_from, TRUE);
912 account = mailto_account;
915 /* if no account prefs set from mailto, set if from folder prefs (if any) */
916 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
917 account = account_find_from_id(item->prefs->default_account);
919 /* if no account prefs set, fallback to the current one */
920 if (!account) account = cur_account;
921 g_return_val_if_fail(account != NULL, NULL);
923 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
925 /* override from name if mailto asked for it */
927 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
930 /* override from name according to folder properties */
931 if (item && item->prefs &&
932 item->prefs->compose_with_format &&
933 item->prefs->compose_override_from_format &&
934 *item->prefs->compose_override_from_format != '\0') {
939 dummyinfo = compose_msginfo_new_from_compose(compose);
941 /* decode \-escape sequences in the internal representation of the quote format */
942 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
943 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
946 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
949 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
951 quote_fmt_scan_string(tmp);
954 buf = quote_fmt_get_buffer();
956 alertpanel_error(_("New message From format error."));
958 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
959 quote_fmt_reset_vartable();
964 compose->replyinfo = NULL;
965 compose->fwdinfo = NULL;
967 textview = GTK_TEXT_VIEW(compose->text);
968 textbuf = gtk_text_view_get_buffer(textview);
969 compose_create_tags(textview, compose);
971 undo_block(compose->undostruct);
973 compose_set_dictionaries_from_folder_prefs(compose, item);
976 if (account->auto_sig)
977 compose_insert_sig(compose, FALSE);
978 gtk_text_buffer_get_start_iter(textbuf, &iter);
979 gtk_text_buffer_place_cursor(textbuf, &iter);
981 if (account->protocol != A_NNTP) {
982 if (mailto && *mailto != '\0') {
983 compose_entries_set(compose, mailto, COMPOSE_TO);
985 } else if (item && item->prefs->enable_default_to) {
986 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
987 compose_entry_mark_default_to(compose, item->prefs->default_to);
989 if (item && item->ret_rcpt) {
990 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
993 if (mailto && *mailto != '\0') {
994 if (!strchr(mailto, '@'))
995 compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
997 compose_entries_set(compose, mailto, COMPOSE_TO);
998 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
999 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1002 * CLAWS: just don't allow return receipt request, even if the user
1003 * may want to send an email. simple but foolproof.
1005 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1007 compose_add_field_list( compose, listAddress );
1009 if (item && item->prefs && item->prefs->compose_with_format) {
1010 subject_format = item->prefs->compose_subject_format;
1011 body_format = item->prefs->compose_body_format;
1012 } else if (account->compose_with_format) {
1013 subject_format = account->compose_subject_format;
1014 body_format = account->compose_body_format;
1015 } else if (prefs_common.compose_with_format) {
1016 subject_format = prefs_common.compose_subject_format;
1017 body_format = prefs_common.compose_body_format;
1020 if (subject_format || body_format) {
1023 && *subject_format != '\0' )
1025 gchar *subject = NULL;
1030 dummyinfo = compose_msginfo_new_from_compose(compose);
1032 /* decode \-escape sequences in the internal representation of the quote format */
1033 tmp = malloc(strlen(subject_format)+1);
1034 pref_get_unescaped_pref(tmp, subject_format);
1036 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1038 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1039 compose->gtkaspell);
1041 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1043 quote_fmt_scan_string(tmp);
1046 buf = quote_fmt_get_buffer();
1048 alertpanel_error(_("New message subject format error."));
1050 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1051 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1052 quote_fmt_reset_vartable();
1059 && *body_format != '\0' )
1062 GtkTextBuffer *buffer;
1063 GtkTextIter start, end;
1067 dummyinfo = compose_msginfo_new_from_compose(compose);
1069 text = GTK_TEXT_VIEW(compose->text);
1070 buffer = gtk_text_view_get_buffer(text);
1071 gtk_text_buffer_get_start_iter(buffer, &start);
1072 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1073 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1075 compose_quote_fmt(compose, dummyinfo,
1077 NULL, tmp, FALSE, TRUE,
1078 _("New message body format error at line %d."));
1079 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1080 quote_fmt_reset_vartable();
1086 procmsg_msginfo_free( dummyinfo );
1092 for (i = 0; i < attach_files->len; i++) {
1093 file = g_ptr_array_index(attach_files, i);
1094 compose_attach_append(compose, file, file, NULL);
1098 compose_show_first_last_header(compose, TRUE);
1100 /* Set save folder */
1101 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1102 gchar *folderidentifier;
1104 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1105 folderidentifier = folder_item_get_identifier(item);
1106 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1107 g_free(folderidentifier);
1110 gtk_widget_grab_focus(compose->header_last->entry);
1112 undo_unblock(compose->undostruct);
1114 if (prefs_common.auto_exteditor)
1115 compose_exec_ext_editor(compose);
1117 compose->draft_timeout_tag = -1;
1118 SCROLL_TO_CURSOR(compose);
1120 compose->modified = FALSE;
1121 compose_set_title(compose);
1125 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1126 gboolean override_pref)
1128 gchar *privacy = NULL;
1130 g_return_if_fail(compose != NULL);
1131 g_return_if_fail(account != NULL);
1133 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1136 if (account->default_privacy_system
1137 && strlen(account->default_privacy_system)) {
1138 privacy = account->default_privacy_system;
1140 GSList *privacy_avail = privacy_get_system_ids();
1141 if (privacy_avail && g_slist_length(privacy_avail)) {
1142 privacy = (gchar *)(privacy_avail->data);
1145 if (privacy != NULL) {
1146 if (compose->privacy_system == NULL)
1147 compose->privacy_system = g_strdup(privacy);
1148 else if (*(compose->privacy_system) == '\0') {
1149 g_free(compose->privacy_system);
1150 compose->privacy_system = g_strdup(privacy);
1152 compose_update_privacy_system_menu_item(compose, FALSE);
1153 compose_use_encryption(compose, TRUE);
1157 static void compose_force_signing(Compose *compose, PrefsAccount *account)
1159 gchar *privacy = NULL;
1161 if (account->default_privacy_system
1162 && strlen(account->default_privacy_system)) {
1163 privacy = account->default_privacy_system;
1165 GSList *privacy_avail = privacy_get_system_ids();
1166 if (privacy_avail && g_slist_length(privacy_avail)) {
1167 privacy = (gchar *)(privacy_avail->data);
1170 if (privacy != NULL) {
1171 if (compose->privacy_system == NULL)
1172 compose->privacy_system = g_strdup(privacy);
1173 compose_update_privacy_system_menu_item(compose, FALSE);
1174 compose_use_signing(compose, TRUE);
1178 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1182 Compose *compose = NULL;
1184 g_return_val_if_fail(msginfo_list != NULL, NULL);
1186 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1187 g_return_val_if_fail(msginfo != NULL, NULL);
1189 list_len = g_slist_length(msginfo_list);
1193 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1194 FALSE, prefs_common.default_reply_list, FALSE, body);
1196 case COMPOSE_REPLY_WITH_QUOTE:
1197 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1198 FALSE, prefs_common.default_reply_list, FALSE, body);
1200 case COMPOSE_REPLY_WITHOUT_QUOTE:
1201 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1202 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1204 case COMPOSE_REPLY_TO_SENDER:
1205 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1206 FALSE, FALSE, TRUE, body);
1208 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1209 compose = compose_followup_and_reply_to(msginfo,
1210 COMPOSE_QUOTE_CHECK,
1211 FALSE, FALSE, body);
1213 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1214 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1215 FALSE, FALSE, TRUE, body);
1217 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1218 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1219 FALSE, FALSE, TRUE, NULL);
1221 case COMPOSE_REPLY_TO_ALL:
1222 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1223 TRUE, FALSE, FALSE, body);
1225 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1226 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1227 TRUE, FALSE, FALSE, body);
1229 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1230 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1231 TRUE, FALSE, FALSE, NULL);
1233 case COMPOSE_REPLY_TO_LIST:
1234 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1235 FALSE, TRUE, FALSE, body);
1237 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1238 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1239 FALSE, TRUE, FALSE, body);
1241 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1242 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1243 FALSE, TRUE, FALSE, NULL);
1245 case COMPOSE_FORWARD:
1246 if (prefs_common.forward_as_attachment) {
1247 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1250 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1254 case COMPOSE_FORWARD_INLINE:
1255 /* check if we reply to more than one Message */
1256 if (list_len == 1) {
1257 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1260 /* more messages FALL THROUGH */
1261 case COMPOSE_FORWARD_AS_ATTACH:
1262 compose = compose_forward_multiple(NULL, msginfo_list);
1264 case COMPOSE_REDIRECT:
1265 compose = compose_redirect(NULL, msginfo, FALSE);
1268 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1271 if (compose == NULL) {
1272 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1276 compose->rmode = mode;
1277 switch (compose->rmode) {
1279 case COMPOSE_REPLY_WITH_QUOTE:
1280 case COMPOSE_REPLY_WITHOUT_QUOTE:
1281 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1282 debug_print("reply mode Normal\n");
1283 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1284 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1286 case COMPOSE_REPLY_TO_SENDER:
1287 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1288 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1289 debug_print("reply mode Sender\n");
1290 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1292 case COMPOSE_REPLY_TO_ALL:
1293 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1294 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1295 debug_print("reply mode All\n");
1296 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1298 case COMPOSE_REPLY_TO_LIST:
1299 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1300 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1301 debug_print("reply mode List\n");
1302 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1310 static Compose *compose_reply(MsgInfo *msginfo,
1311 ComposeQuoteMode quote_mode,
1317 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1318 to_sender, FALSE, body);
1321 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1322 ComposeQuoteMode quote_mode,
1327 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1328 to_sender, TRUE, body);
1331 static void compose_extract_original_charset(Compose *compose)
1333 MsgInfo *info = NULL;
1334 if (compose->replyinfo) {
1335 info = compose->replyinfo;
1336 } else if (compose->fwdinfo) {
1337 info = compose->fwdinfo;
1338 } else if (compose->targetinfo) {
1339 info = compose->targetinfo;
1342 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1343 MimeInfo *partinfo = mimeinfo;
1344 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1345 partinfo = procmime_mimeinfo_next(partinfo);
1347 compose->orig_charset =
1348 g_strdup(procmime_mimeinfo_get_parameter(
1349 partinfo, "charset"));
1351 procmime_mimeinfo_free_all(mimeinfo);
1355 #define SIGNAL_BLOCK(buffer) { \
1356 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1357 G_CALLBACK(compose_changed_cb), \
1359 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1360 G_CALLBACK(text_inserted), \
1364 #define SIGNAL_UNBLOCK(buffer) { \
1365 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1366 G_CALLBACK(compose_changed_cb), \
1368 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1369 G_CALLBACK(text_inserted), \
1373 static Compose *compose_generic_reply(MsgInfo *msginfo,
1374 ComposeQuoteMode quote_mode,
1375 gboolean to_all, gboolean to_ml,
1377 gboolean followup_and_reply_to,
1381 PrefsAccount *account = NULL;
1382 GtkTextView *textview;
1383 GtkTextBuffer *textbuf;
1384 gboolean quote = FALSE;
1385 const gchar *qmark = NULL;
1386 const gchar *body_fmt = NULL;
1388 g_return_val_if_fail(msginfo != NULL, NULL);
1389 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1391 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1393 g_return_val_if_fail(account != NULL, NULL);
1395 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1397 compose->updating = TRUE;
1399 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1400 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1402 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1403 if (!compose->replyinfo)
1404 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1406 compose_extract_original_charset(compose);
1408 if (msginfo->folder && msginfo->folder->ret_rcpt)
1409 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1411 /* Set save folder */
1412 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1413 gchar *folderidentifier;
1415 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1416 folderidentifier = folder_item_get_identifier(msginfo->folder);
1417 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1418 g_free(folderidentifier);
1421 if (compose_parse_header(compose, msginfo) < 0) {
1422 compose->updating = FALSE;
1423 compose_destroy(compose);
1427 /* override from name according to folder properties */
1428 if (msginfo->folder && msginfo->folder->prefs &&
1429 msginfo->folder->prefs->reply_with_format &&
1430 msginfo->folder->prefs->reply_override_from_format &&
1431 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1436 /* decode \-escape sequences in the internal representation of the quote format */
1437 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1438 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1441 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1442 compose->gtkaspell);
1444 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1446 quote_fmt_scan_string(tmp);
1449 buf = quote_fmt_get_buffer();
1451 alertpanel_error(_("Message reply From format error."));
1453 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1454 quote_fmt_reset_vartable();
1459 textview = (GTK_TEXT_VIEW(compose->text));
1460 textbuf = gtk_text_view_get_buffer(textview);
1461 compose_create_tags(textview, compose);
1463 undo_block(compose->undostruct);
1465 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1468 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1469 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1470 /* use the reply format of folder (if enabled), or the account's one
1471 (if enabled) or fallback to the global reply format, which is always
1472 enabled (even if empty), and use the relevant quotemark */
1474 if (msginfo->folder && msginfo->folder->prefs &&
1475 msginfo->folder->prefs->reply_with_format) {
1476 qmark = msginfo->folder->prefs->reply_quotemark;
1477 body_fmt = msginfo->folder->prefs->reply_body_format;
1479 } else if (account->reply_with_format) {
1480 qmark = account->reply_quotemark;
1481 body_fmt = account->reply_body_format;
1484 qmark = prefs_common.quotemark;
1485 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1486 body_fmt = gettext(prefs_common.quotefmt);
1493 /* empty quotemark is not allowed */
1494 if (qmark == NULL || *qmark == '\0')
1496 compose_quote_fmt(compose, compose->replyinfo,
1497 body_fmt, qmark, body, FALSE, TRUE,
1498 _("Message reply format error at line %d."));
1499 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1500 quote_fmt_reset_vartable();
1503 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1504 compose_force_encryption(compose, account, FALSE);
1507 privacy_msginfo_get_signed_state(compose->replyinfo);
1508 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1509 compose_force_signing(compose, account);
1512 SIGNAL_BLOCK(textbuf);
1514 if (account->auto_sig)
1515 compose_insert_sig(compose, FALSE);
1517 compose_wrap_all(compose);
1519 SIGNAL_UNBLOCK(textbuf);
1521 gtk_widget_grab_focus(compose->text);
1523 undo_unblock(compose->undostruct);
1525 if (prefs_common.auto_exteditor)
1526 compose_exec_ext_editor(compose);
1528 compose->modified = FALSE;
1529 compose_set_title(compose);
1531 compose->updating = FALSE;
1532 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1533 SCROLL_TO_CURSOR(compose);
1535 if (compose->deferred_destroy) {
1536 compose_destroy(compose);
1543 #define INSERT_FW_HEADER(var, hdr) \
1544 if (msginfo->var && *msginfo->var) { \
1545 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1546 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1547 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1550 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1551 gboolean as_attach, const gchar *body,
1552 gboolean no_extedit,
1556 GtkTextView *textview;
1557 GtkTextBuffer *textbuf;
1560 g_return_val_if_fail(msginfo != NULL, NULL);
1561 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1564 !(account = compose_guess_forward_account_from_msginfo
1566 account = cur_account;
1568 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1570 compose->updating = TRUE;
1571 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1572 if (!compose->fwdinfo)
1573 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1575 compose_extract_original_charset(compose);
1577 if (msginfo->subject && *msginfo->subject) {
1578 gchar *buf, *buf2, *p;
1580 buf = p = g_strdup(msginfo->subject);
1581 p += subject_get_prefix_length(p);
1582 memmove(buf, p, strlen(p) + 1);
1584 buf2 = g_strdup_printf("Fw: %s", buf);
1585 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1591 /* override from name according to folder properties */
1592 if (msginfo->folder && msginfo->folder->prefs &&
1593 msginfo->folder->prefs->forward_with_format &&
1594 msginfo->folder->prefs->forward_override_from_format &&
1595 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1599 MsgInfo *full_msginfo = NULL;
1602 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1604 full_msginfo = procmsg_msginfo_copy(msginfo);
1606 /* decode \-escape sequences in the internal representation of the quote format */
1607 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1608 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1611 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1612 compose->gtkaspell);
1614 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1616 quote_fmt_scan_string(tmp);
1619 buf = quote_fmt_get_buffer();
1621 alertpanel_error(_("Message forward From format error."));
1623 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1624 quote_fmt_reset_vartable();
1627 procmsg_msginfo_free(full_msginfo);
1630 textview = GTK_TEXT_VIEW(compose->text);
1631 textbuf = gtk_text_view_get_buffer(textview);
1632 compose_create_tags(textview, compose);
1634 undo_block(compose->undostruct);
1638 msgfile = procmsg_get_message_file(msginfo);
1639 if (!is_file_exist(msgfile))
1640 g_warning("%s: file not exist\n", msgfile);
1642 compose_attach_append(compose, msgfile, msgfile,
1647 const gchar *qmark = NULL;
1648 const gchar *body_fmt = NULL;
1649 MsgInfo *full_msginfo;
1651 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1652 body_fmt = gettext(prefs_common.fw_quotefmt);
1656 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1658 full_msginfo = procmsg_msginfo_copy(msginfo);
1660 /* use the forward format of folder (if enabled), or the account's one
1661 (if enabled) or fallback to the global forward format, which is always
1662 enabled (even if empty), and use the relevant quotemark */
1663 if (msginfo->folder && msginfo->folder->prefs &&
1664 msginfo->folder->prefs->forward_with_format) {
1665 qmark = msginfo->folder->prefs->forward_quotemark;
1666 body_fmt = msginfo->folder->prefs->forward_body_format;
1668 } else if (account->forward_with_format) {
1669 qmark = account->forward_quotemark;
1670 body_fmt = account->forward_body_format;
1673 qmark = prefs_common.fw_quotemark;
1674 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1675 body_fmt = gettext(prefs_common.fw_quotefmt);
1680 /* empty quotemark is not allowed */
1681 if (qmark == NULL || *qmark == '\0')
1684 compose_quote_fmt(compose, full_msginfo,
1685 body_fmt, qmark, body, FALSE, TRUE,
1686 _("Message forward format error at line %d."));
1687 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1688 quote_fmt_reset_vartable();
1689 compose_attach_parts(compose, msginfo);
1691 procmsg_msginfo_free(full_msginfo);
1694 SIGNAL_BLOCK(textbuf);
1696 if (account->auto_sig)
1697 compose_insert_sig(compose, FALSE);
1699 compose_wrap_all(compose);
1701 SIGNAL_UNBLOCK(textbuf);
1703 gtk_text_buffer_get_start_iter(textbuf, &iter);
1704 gtk_text_buffer_place_cursor(textbuf, &iter);
1706 gtk_widget_grab_focus(compose->header_last->entry);
1708 if (!no_extedit && prefs_common.auto_exteditor)
1709 compose_exec_ext_editor(compose);
1712 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1713 gchar *folderidentifier;
1715 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1716 folderidentifier = folder_item_get_identifier(msginfo->folder);
1717 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1718 g_free(folderidentifier);
1721 undo_unblock(compose->undostruct);
1723 compose->modified = FALSE;
1724 compose_set_title(compose);
1726 compose->updating = FALSE;
1727 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1728 SCROLL_TO_CURSOR(compose);
1730 if (compose->deferred_destroy) {
1731 compose_destroy(compose);
1738 #undef INSERT_FW_HEADER
1740 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1743 GtkTextView *textview;
1744 GtkTextBuffer *textbuf;
1748 gboolean single_mail = TRUE;
1750 g_return_val_if_fail(msginfo_list != NULL, NULL);
1752 if (g_slist_length(msginfo_list) > 1)
1753 single_mail = FALSE;
1755 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1756 if (((MsgInfo *)msginfo->data)->folder == NULL)
1759 /* guess account from first selected message */
1761 !(account = compose_guess_forward_account_from_msginfo
1762 (msginfo_list->data)))
1763 account = cur_account;
1765 g_return_val_if_fail(account != NULL, NULL);
1767 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1768 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1769 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1772 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1774 compose->updating = TRUE;
1776 /* override from name according to folder properties */
1777 if (msginfo_list->data) {
1778 MsgInfo *msginfo = msginfo_list->data;
1780 if (msginfo->folder && msginfo->folder->prefs &&
1781 msginfo->folder->prefs->forward_with_format &&
1782 msginfo->folder->prefs->forward_override_from_format &&
1783 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1788 /* decode \-escape sequences in the internal representation of the quote format */
1789 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1790 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1793 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1794 compose->gtkaspell);
1796 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1798 quote_fmt_scan_string(tmp);
1801 buf = quote_fmt_get_buffer();
1803 alertpanel_error(_("Message forward From format error."));
1805 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1806 quote_fmt_reset_vartable();
1812 textview = GTK_TEXT_VIEW(compose->text);
1813 textbuf = gtk_text_view_get_buffer(textview);
1814 compose_create_tags(textview, compose);
1816 undo_block(compose->undostruct);
1817 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1818 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1820 if (!is_file_exist(msgfile))
1821 g_warning("%s: file not exist\n", msgfile);
1823 compose_attach_append(compose, msgfile, msgfile,
1829 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1830 if (info->subject && *info->subject) {
1831 gchar *buf, *buf2, *p;
1833 buf = p = g_strdup(info->subject);
1834 p += subject_get_prefix_length(p);
1835 memmove(buf, p, strlen(p) + 1);
1837 buf2 = g_strdup_printf("Fw: %s", buf);
1838 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1844 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1845 _("Fw: multiple emails"));
1848 SIGNAL_BLOCK(textbuf);
1850 if (account->auto_sig)
1851 compose_insert_sig(compose, FALSE);
1853 compose_wrap_all(compose);
1855 SIGNAL_UNBLOCK(textbuf);
1857 gtk_text_buffer_get_start_iter(textbuf, &iter);
1858 gtk_text_buffer_place_cursor(textbuf, &iter);
1860 gtk_widget_grab_focus(compose->header_last->entry);
1861 undo_unblock(compose->undostruct);
1862 compose->modified = FALSE;
1863 compose_set_title(compose);
1865 compose->updating = FALSE;
1866 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1867 SCROLL_TO_CURSOR(compose);
1869 if (compose->deferred_destroy) {
1870 compose_destroy(compose);
1877 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1879 GtkTextIter start = *iter;
1880 GtkTextIter end_iter;
1881 int start_pos = gtk_text_iter_get_offset(&start);
1883 if (!compose->account->sig_sep)
1886 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1887 start_pos+strlen(compose->account->sig_sep));
1889 /* check sig separator */
1890 str = gtk_text_iter_get_text(&start, &end_iter);
1891 if (!strcmp(str, compose->account->sig_sep)) {
1893 /* check end of line (\n) */
1894 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1895 start_pos+strlen(compose->account->sig_sep));
1896 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1897 start_pos+strlen(compose->account->sig_sep)+1);
1898 tmp = gtk_text_iter_get_text(&start, &end_iter);
1899 if (!strcmp(tmp,"\n")) {
1911 static void compose_colorize_signature(Compose *compose)
1913 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1915 GtkTextIter end_iter;
1916 gtk_text_buffer_get_start_iter(buffer, &iter);
1917 while (gtk_text_iter_forward_line(&iter))
1918 if (compose_is_sig_separator(compose, buffer, &iter)) {
1919 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1920 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1924 #define BLOCK_WRAP() { \
1925 prev_autowrap = compose->autowrap; \
1926 buffer = gtk_text_view_get_buffer( \
1927 GTK_TEXT_VIEW(compose->text)); \
1928 compose->autowrap = FALSE; \
1930 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1931 G_CALLBACK(compose_changed_cb), \
1933 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1934 G_CALLBACK(text_inserted), \
1937 #define UNBLOCK_WRAP() { \
1938 compose->autowrap = prev_autowrap; \
1939 if (compose->autowrap) { \
1940 gint old = compose->draft_timeout_tag; \
1941 compose->draft_timeout_tag = -2; \
1942 compose_wrap_all(compose); \
1943 compose->draft_timeout_tag = old; \
1946 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1947 G_CALLBACK(compose_changed_cb), \
1949 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1950 G_CALLBACK(text_inserted), \
1954 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
1956 Compose *compose = NULL;
1957 PrefsAccount *account = NULL;
1958 GtkTextView *textview;
1959 GtkTextBuffer *textbuf;
1963 gchar buf[BUFFSIZE];
1964 gboolean use_signing = FALSE;
1965 gboolean use_encryption = FALSE;
1966 gchar *privacy_system = NULL;
1967 int priority = PRIORITY_NORMAL;
1968 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
1969 gboolean autowrap = prefs_common.autowrap;
1970 gboolean autoindent = prefs_common.auto_indent;
1972 g_return_val_if_fail(msginfo != NULL, NULL);
1973 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1975 if (compose_put_existing_to_front(msginfo)) {
1979 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
1980 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
1981 gchar queueheader_buf[BUFFSIZE];
1984 /* Select Account from queue headers */
1985 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1986 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
1987 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
1988 account = account_find_from_id(id);
1990 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1991 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
1992 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
1993 account = account_find_from_id(id);
1995 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1996 sizeof(queueheader_buf), "NAID:")) {
1997 id = atoi(&queueheader_buf[strlen("NAID:")]);
1998 account = account_find_from_id(id);
2000 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2001 sizeof(queueheader_buf), "MAID:")) {
2002 id = atoi(&queueheader_buf[strlen("MAID:")]);
2003 account = account_find_from_id(id);
2005 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2006 sizeof(queueheader_buf), "S:")) {
2007 account = account_find_from_address(queueheader_buf, FALSE);
2009 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2010 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2011 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2012 use_signing = param;
2015 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2016 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2017 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2018 use_signing = param;
2021 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2022 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2023 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2024 use_encryption = param;
2026 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2027 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2028 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2029 use_encryption = param;
2031 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2032 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2033 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2036 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2037 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2038 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2041 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2042 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2043 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2045 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2046 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2047 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2049 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2050 sizeof(queueheader_buf), "X-Priority: ")) {
2051 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2054 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2055 sizeof(queueheader_buf), "RMID:")) {
2056 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2057 if (tokens[0] && tokens[1] && tokens[2]) {
2058 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2059 if (orig_item != NULL) {
2060 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2065 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2066 sizeof(queueheader_buf), "FMID:")) {
2067 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2068 if (tokens[0] && tokens[1] && tokens[2]) {
2069 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2070 if (orig_item != NULL) {
2071 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2077 account = msginfo->folder->folder->account;
2080 if (!account && prefs_common.reedit_account_autosel) {
2081 gchar from[BUFFSIZE];
2082 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2083 extract_address(from);
2084 account = account_find_from_address(from, FALSE);
2088 account = cur_account;
2090 g_return_val_if_fail(account != NULL, NULL);
2092 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2094 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2095 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2096 compose->autowrap = autowrap;
2097 compose->replyinfo = replyinfo;
2098 compose->fwdinfo = fwdinfo;
2100 compose->updating = TRUE;
2101 compose->priority = priority;
2103 if (privacy_system != NULL) {
2104 compose->privacy_system = privacy_system;
2105 compose_use_signing(compose, use_signing);
2106 compose_use_encryption(compose, use_encryption);
2107 compose_update_privacy_system_menu_item(compose, FALSE);
2109 activate_privacy_system(compose, account, FALSE);
2112 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2114 compose_extract_original_charset(compose);
2116 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2117 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2118 gchar queueheader_buf[BUFFSIZE];
2120 /* Set message save folder */
2121 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2124 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2125 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
2126 gtk_editable_insert_text(GTK_EDITABLE(compose->savemsg_entry), &queueheader_buf[4], strlen(&queueheader_buf[4]), &startpos);
2128 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2129 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2131 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2136 if (compose_parse_header(compose, msginfo) < 0) {
2137 compose->updating = FALSE;
2138 compose_destroy(compose);
2141 compose_reedit_set_entry(compose, msginfo);
2143 textview = GTK_TEXT_VIEW(compose->text);
2144 textbuf = gtk_text_view_get_buffer(textview);
2145 compose_create_tags(textview, compose);
2147 mark = gtk_text_buffer_get_insert(textbuf);
2148 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2150 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2151 G_CALLBACK(compose_changed_cb),
2154 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2155 fp = procmime_get_first_encrypted_text_content(msginfo);
2157 compose_force_encryption(compose, account, TRUE);
2160 fp = procmime_get_first_text_content(msginfo);
2163 g_warning("Can't get text part\n");
2167 gboolean prev_autowrap = compose->autowrap;
2168 GtkTextBuffer *buffer = textbuf;
2170 while (fgets(buf, sizeof(buf), fp) != NULL) {
2172 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2178 compose_attach_parts(compose, msginfo);
2180 compose_colorize_signature(compose);
2182 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2183 G_CALLBACK(compose_changed_cb),
2186 gtk_widget_grab_focus(compose->text);
2188 if (prefs_common.auto_exteditor) {
2189 compose_exec_ext_editor(compose);
2191 compose->modified = FALSE;
2192 compose_set_title(compose);
2194 compose->updating = FALSE;
2195 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2196 SCROLL_TO_CURSOR(compose);
2198 if (compose->deferred_destroy) {
2199 compose_destroy(compose);
2203 compose->sig_str = compose_get_signature_str(compose);
2208 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2215 g_return_val_if_fail(msginfo != NULL, NULL);
2218 account = account_get_reply_account(msginfo,
2219 prefs_common.reply_account_autosel);
2220 g_return_val_if_fail(account != NULL, NULL);
2222 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2224 compose->updating = TRUE;
2226 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2227 compose->replyinfo = NULL;
2228 compose->fwdinfo = NULL;
2230 compose_show_first_last_header(compose, TRUE);
2232 gtk_widget_grab_focus(compose->header_last->entry);
2234 filename = procmsg_get_message_file(msginfo);
2236 if (filename == NULL) {
2237 compose->updating = FALSE;
2238 compose_destroy(compose);
2243 compose->redirect_filename = filename;
2245 /* Set save folder */
2246 item = msginfo->folder;
2247 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2248 gchar *folderidentifier;
2250 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2251 folderidentifier = folder_item_get_identifier(item);
2252 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
2253 g_free(folderidentifier);
2256 compose_attach_parts(compose, msginfo);
2258 if (msginfo->subject)
2259 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2261 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2263 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2264 _("Message redirect format error at line %d."));
2265 quote_fmt_reset_vartable();
2266 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2268 compose_colorize_signature(compose);
2271 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2272 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2273 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2275 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2276 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2277 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2278 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2279 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2280 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2281 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2282 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2284 if (compose->toolbar->draft_btn)
2285 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2286 if (compose->toolbar->insert_btn)
2287 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2288 if (compose->toolbar->attach_btn)
2289 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2290 if (compose->toolbar->sig_btn)
2291 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2292 if (compose->toolbar->exteditor_btn)
2293 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2294 if (compose->toolbar->linewrap_current_btn)
2295 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2296 if (compose->toolbar->linewrap_all_btn)
2297 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2299 compose->modified = FALSE;
2300 compose_set_title(compose);
2301 compose->updating = FALSE;
2302 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2303 SCROLL_TO_CURSOR(compose);
2305 if (compose->deferred_destroy) {
2306 compose_destroy(compose);
2313 GList *compose_get_compose_list(void)
2315 return compose_list;
2318 void compose_entry_append(Compose *compose, const gchar *address,
2319 ComposeEntryType type)
2321 const gchar *header;
2323 gboolean in_quote = FALSE;
2324 if (!address || *address == '\0') return;
2331 header = N_("Bcc:");
2333 case COMPOSE_REPLYTO:
2334 header = N_("Reply-To:");
2336 case COMPOSE_NEWSGROUPS:
2337 header = N_("Newsgroups:");
2339 case COMPOSE_FOLLOWUPTO:
2340 header = N_( "Followup-To:");
2347 header = prefs_common_translated_header_name(header);
2349 cur = begin = (gchar *)address;
2351 /* we separate the line by commas, but not if we're inside a quoted
2353 while (*cur != '\0') {
2355 in_quote = !in_quote;
2356 if (*cur == ',' && !in_quote) {
2357 gchar *tmp = g_strdup(begin);
2359 tmp[cur-begin]='\0';
2362 while (*tmp == ' ' || *tmp == '\t')
2364 compose_add_header_entry(compose, header, tmp);
2371 gchar *tmp = g_strdup(begin);
2373 tmp[cur-begin]='\0';
2376 while (*tmp == ' ' || *tmp == '\t')
2378 compose_add_header_entry(compose, header, tmp);
2383 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2385 static GdkColor yellow;
2386 static GdkColor black;
2387 static gboolean yellow_initialised = FALSE;
2391 if (!yellow_initialised) {
2392 gdk_color_parse("#f5f6be", &yellow);
2393 gdk_color_parse("#000000", &black);
2394 yellow_initialised = gdk_colormap_alloc_color(
2395 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2396 yellow_initialised &= gdk_colormap_alloc_color(
2397 gdk_colormap_get_system(), &black, FALSE, TRUE);
2400 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2401 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2402 if (gtk_entry_get_text(entry) &&
2403 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2404 if (yellow_initialised) {
2405 gtk_widget_modify_base(
2406 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2407 GTK_STATE_NORMAL, &yellow);
2408 gtk_widget_modify_text(
2409 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2410 GTK_STATE_NORMAL, &black);
2416 void compose_toolbar_cb(gint action, gpointer data)
2418 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2419 Compose *compose = (Compose*)toolbar_item->parent;
2421 g_return_if_fail(compose != NULL);
2425 compose_send_cb(NULL, compose);
2428 compose_send_later_cb(NULL, compose);
2431 compose_draft(compose, COMPOSE_QUIT_EDITING);
2434 compose_insert_file_cb(NULL, compose);
2437 compose_attach_cb(NULL, compose);
2440 compose_insert_sig(compose, FALSE);
2443 compose_ext_editor_cb(NULL, compose);
2445 case A_LINEWRAP_CURRENT:
2446 compose_beautify_paragraph(compose, NULL, TRUE);
2448 case A_LINEWRAP_ALL:
2449 compose_wrap_all_full(compose, TRUE);
2452 compose_address_cb(NULL, compose);
2455 case A_CHECK_SPELLING:
2456 compose_check_all(NULL, compose);
2464 static void compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2469 gchar *subject = NULL;
2473 gchar **attach = NULL;
2475 /* get mailto parts but skip from */
2476 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2479 compose_entry_append(compose, to, to_type);
2481 compose_entry_append(compose, cc, COMPOSE_CC);
2483 compose_entry_append(compose, bcc, COMPOSE_BCC);
2485 if (!g_utf8_validate (subject, -1, NULL)) {
2486 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2487 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2490 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2494 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2495 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2498 gboolean prev_autowrap = compose->autowrap;
2500 compose->autowrap = FALSE;
2502 mark = gtk_text_buffer_get_insert(buffer);
2503 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2505 if (!g_utf8_validate (body, -1, NULL)) {
2506 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2507 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2510 gtk_text_buffer_insert(buffer, &iter, body, -1);
2512 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2514 compose->autowrap = prev_autowrap;
2515 if (compose->autowrap)
2516 compose_wrap_all(compose);
2520 gint i = 0, att = 0;
2521 gchar *warn_files = NULL;
2522 while (attach[i] != NULL) {
2523 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2524 if (utf8_filename) {
2525 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2526 gchar *tmp = g_strdup_printf("%s%s\n",
2527 warn_files?warn_files:"",
2533 g_free(utf8_filename);
2535 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2540 alertpanel_notice(ngettext(
2541 "The following file has been attached: \n%s",
2542 "The following files have been attached: \n%s", att), warn_files);
2554 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2556 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2557 {"Cc:", NULL, TRUE},
2558 {"References:", NULL, FALSE},
2559 {"Bcc:", NULL, TRUE},
2560 {"Newsgroups:", NULL, TRUE},
2561 {"Followup-To:", NULL, TRUE},
2562 {"List-Post:", NULL, FALSE},
2563 {"X-Priority:", NULL, FALSE},
2564 {NULL, NULL, FALSE}};
2580 g_return_val_if_fail(msginfo != NULL, -1);
2582 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2583 procheader_get_header_fields(fp, hentry);
2586 if (hentry[H_REPLY_TO].body != NULL) {
2587 if (hentry[H_REPLY_TO].body[0] != '\0') {
2589 conv_unmime_header(hentry[H_REPLY_TO].body,
2592 g_free(hentry[H_REPLY_TO].body);
2593 hentry[H_REPLY_TO].body = NULL;
2595 if (hentry[H_CC].body != NULL) {
2596 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2597 g_free(hentry[H_CC].body);
2598 hentry[H_CC].body = NULL;
2600 if (hentry[H_REFERENCES].body != NULL) {
2601 if (compose->mode == COMPOSE_REEDIT)
2602 compose->references = hentry[H_REFERENCES].body;
2604 compose->references = compose_parse_references
2605 (hentry[H_REFERENCES].body, msginfo->msgid);
2606 g_free(hentry[H_REFERENCES].body);
2608 hentry[H_REFERENCES].body = NULL;
2610 if (hentry[H_BCC].body != NULL) {
2611 if (compose->mode == COMPOSE_REEDIT)
2613 conv_unmime_header(hentry[H_BCC].body, NULL);
2614 g_free(hentry[H_BCC].body);
2615 hentry[H_BCC].body = NULL;
2617 if (hentry[H_NEWSGROUPS].body != NULL) {
2618 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2619 hentry[H_NEWSGROUPS].body = NULL;
2621 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2622 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2623 compose->followup_to =
2624 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2627 g_free(hentry[H_FOLLOWUP_TO].body);
2628 hentry[H_FOLLOWUP_TO].body = NULL;
2630 if (hentry[H_LIST_POST].body != NULL) {
2633 extract_address(hentry[H_LIST_POST].body);
2634 if (hentry[H_LIST_POST].body[0] != '\0') {
2635 scan_mailto_url(hentry[H_LIST_POST].body,
2636 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2638 g_free(compose->ml_post);
2639 compose->ml_post = to;
2642 g_free(hentry[H_LIST_POST].body);
2643 hentry[H_LIST_POST].body = NULL;
2646 /* CLAWS - X-Priority */
2647 if (compose->mode == COMPOSE_REEDIT)
2648 if (hentry[H_X_PRIORITY].body != NULL) {
2651 priority = atoi(hentry[H_X_PRIORITY].body);
2652 g_free(hentry[H_X_PRIORITY].body);
2654 hentry[H_X_PRIORITY].body = NULL;
2656 if (priority < PRIORITY_HIGHEST ||
2657 priority > PRIORITY_LOWEST)
2658 priority = PRIORITY_NORMAL;
2660 compose->priority = priority;
2663 if (compose->mode == COMPOSE_REEDIT) {
2664 if (msginfo->inreplyto && *msginfo->inreplyto)
2665 compose->inreplyto = g_strdup(msginfo->inreplyto);
2669 if (msginfo->msgid && *msginfo->msgid)
2670 compose->inreplyto = g_strdup(msginfo->msgid);
2672 if (!compose->references) {
2673 if (msginfo->msgid && *msginfo->msgid) {
2674 if (msginfo->inreplyto && *msginfo->inreplyto)
2675 compose->references =
2676 g_strdup_printf("<%s>\n\t<%s>",
2680 compose->references =
2681 g_strconcat("<", msginfo->msgid, ">",
2683 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2684 compose->references =
2685 g_strconcat("<", msginfo->inreplyto, ">",
2693 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2695 GSList *ref_id_list, *cur;
2699 ref_id_list = references_list_append(NULL, ref);
2700 if (!ref_id_list) return NULL;
2701 if (msgid && *msgid)
2702 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2707 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2708 /* "<" + Message-ID + ">" + CR+LF+TAB */
2709 len += strlen((gchar *)cur->data) + 5;
2711 if (len > MAX_REFERENCES_LEN) {
2712 /* remove second message-ID */
2713 if (ref_id_list && ref_id_list->next &&
2714 ref_id_list->next->next) {
2715 g_free(ref_id_list->next->data);
2716 ref_id_list = g_slist_remove
2717 (ref_id_list, ref_id_list->next->data);
2719 slist_free_strings(ref_id_list);
2720 g_slist_free(ref_id_list);
2727 new_ref = g_string_new("");
2728 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2729 if (new_ref->len > 0)
2730 g_string_append(new_ref, "\n\t");
2731 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2734 slist_free_strings(ref_id_list);
2735 g_slist_free(ref_id_list);
2737 new_ref_str = new_ref->str;
2738 g_string_free(new_ref, FALSE);
2743 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2744 const gchar *fmt, const gchar *qmark,
2745 const gchar *body, gboolean rewrap,
2746 gboolean need_unescape,
2747 const gchar *err_msg)
2749 MsgInfo* dummyinfo = NULL;
2750 gchar *quote_str = NULL;
2752 gboolean prev_autowrap;
2753 const gchar *trimmed_body = body;
2754 gint cursor_pos = -1;
2755 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2756 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2761 SIGNAL_BLOCK(buffer);
2764 dummyinfo = compose_msginfo_new_from_compose(compose);
2765 msginfo = dummyinfo;
2768 if (qmark != NULL) {
2770 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2771 compose->gtkaspell);
2773 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2775 quote_fmt_scan_string(qmark);
2778 buf = quote_fmt_get_buffer();
2780 alertpanel_error(_("Quote mark format error."));
2782 Xstrdup_a(quote_str, buf, goto error)
2785 if (fmt && *fmt != '\0') {
2788 while (*trimmed_body == '\n')
2792 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2793 compose->gtkaspell);
2795 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2797 if (need_unescape) {
2800 /* decode \-escape sequences in the internal representation of the quote format */
2801 tmp = malloc(strlen(fmt)+1);
2802 pref_get_unescaped_pref(tmp, fmt);
2803 quote_fmt_scan_string(tmp);
2807 quote_fmt_scan_string(fmt);
2811 buf = quote_fmt_get_buffer();
2813 gint line = quote_fmt_get_line();
2814 alertpanel_error(err_msg, line);
2820 prev_autowrap = compose->autowrap;
2821 compose->autowrap = FALSE;
2823 mark = gtk_text_buffer_get_insert(buffer);
2824 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2825 if (g_utf8_validate(buf, -1, NULL)) {
2826 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2828 gchar *tmpout = NULL;
2829 tmpout = conv_codeset_strdup
2830 (buf, conv_get_locale_charset_str_no_utf8(),
2832 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2834 tmpout = g_malloc(strlen(buf)*2+1);
2835 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2837 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2841 cursor_pos = quote_fmt_get_cursor_pos();
2842 if (cursor_pos == -1)
2843 cursor_pos = gtk_text_iter_get_offset(&iter);
2844 compose->set_cursor_pos = cursor_pos;
2846 gtk_text_buffer_get_start_iter(buffer, &iter);
2847 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2848 gtk_text_buffer_place_cursor(buffer, &iter);
2850 compose->autowrap = prev_autowrap;
2851 if (compose->autowrap && rewrap)
2852 compose_wrap_all(compose);
2859 SIGNAL_UNBLOCK(buffer);
2861 procmsg_msginfo_free( dummyinfo );
2866 /* if ml_post is of type addr@host and from is of type
2867 * addr-anything@host, return TRUE
2869 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2871 gchar *left_ml = NULL;
2872 gchar *right_ml = NULL;
2873 gchar *left_from = NULL;
2874 gchar *right_from = NULL;
2875 gboolean result = FALSE;
2877 if (!ml_post || !from)
2880 left_ml = g_strdup(ml_post);
2881 if (strstr(left_ml, "@")) {
2882 right_ml = strstr(left_ml, "@")+1;
2883 *(strstr(left_ml, "@")) = '\0';
2886 left_from = g_strdup(from);
2887 if (strstr(left_from, "@")) {
2888 right_from = strstr(left_from, "@")+1;
2889 *(strstr(left_from, "@")) = '\0';
2892 if (left_ml && left_from && right_ml && right_from
2893 && !strncmp(left_from, left_ml, strlen(left_ml))
2894 && !strcmp(right_from, right_ml)) {
2903 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2905 gchar *my_addr1, *my_addr2;
2907 if (!addr1 || !addr2)
2910 Xstrdup_a(my_addr1, addr1, return FALSE);
2911 Xstrdup_a(my_addr2, addr2, return FALSE);
2913 extract_address(my_addr1);
2914 extract_address(my_addr2);
2916 return !strcasecmp(my_addr1, my_addr2);
2919 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2920 gboolean to_all, gboolean to_ml,
2922 gboolean followup_and_reply_to)
2924 GSList *cc_list = NULL;
2927 gchar *replyto = NULL;
2928 GHashTable *to_table;
2930 gboolean reply_to_ml = FALSE;
2931 gboolean default_reply_to = FALSE;
2933 g_return_if_fail(compose->account != NULL);
2934 g_return_if_fail(msginfo != NULL);
2936 reply_to_ml = to_ml && compose->ml_post;
2938 default_reply_to = msginfo->folder &&
2939 msginfo->folder->prefs->enable_default_reply_to;
2941 if (compose->account->protocol != A_NNTP) {
2942 if (reply_to_ml && !default_reply_to) {
2944 gboolean is_subscr = is_subscription(compose->ml_post,
2947 /* normal answer to ml post with a reply-to */
2948 compose_entry_append(compose,
2951 if (compose->replyto
2952 && !same_address(compose->ml_post, compose->replyto))
2953 compose_entry_append(compose,
2957 /* answer to subscription confirmation */
2958 if (compose->replyto)
2959 compose_entry_append(compose,
2962 else if (msginfo->from)
2963 compose_entry_append(compose,
2968 else if (!(to_all || to_sender) && default_reply_to) {
2969 compose_entry_append(compose,
2970 msginfo->folder->prefs->default_reply_to,
2972 compose_entry_mark_default_to(compose,
2973 msginfo->folder->prefs->default_reply_to);
2978 Xstrdup_a(tmp1, msginfo->from, return);
2979 extract_address(tmp1);
2980 if (to_all || to_sender ||
2981 !account_find_from_address(tmp1, FALSE))
2982 compose_entry_append(compose,
2983 (compose->replyto && !to_sender)
2984 ? compose->replyto :
2985 msginfo->from ? msginfo->from : "",
2987 else if (!to_all && !to_sender) {
2988 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2989 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
2990 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2991 if (compose->replyto) {
2992 compose_entry_append(compose,
2996 compose_entry_append(compose,
2997 msginfo->from ? msginfo->from : "",
3001 /* replying to own mail, use original recp */
3002 compose_entry_append(compose,
3003 msginfo->to ? msginfo->to : "",
3005 compose_entry_append(compose,
3006 msginfo->cc ? msginfo->cc : "",
3012 if (to_sender || (compose->followup_to &&
3013 !strncmp(compose->followup_to, "poster", 6)))
3014 compose_entry_append
3016 (compose->replyto ? compose->replyto :
3017 msginfo->from ? msginfo->from : ""),
3020 else if (followup_and_reply_to || to_all) {
3021 compose_entry_append
3023 (compose->replyto ? compose->replyto :
3024 msginfo->from ? msginfo->from : ""),
3027 compose_entry_append
3029 compose->followup_to ? compose->followup_to :
3030 compose->newsgroups ? compose->newsgroups : "",
3031 COMPOSE_NEWSGROUPS);
3034 compose_entry_append
3036 compose->followup_to ? compose->followup_to :
3037 compose->newsgroups ? compose->newsgroups : "",
3038 COMPOSE_NEWSGROUPS);
3041 if (msginfo->subject && *msginfo->subject) {
3045 buf = p = g_strdup(msginfo->subject);
3046 p += subject_get_prefix_length(p);
3047 memmove(buf, p, strlen(p) + 1);
3049 buf2 = g_strdup_printf("Re: %s", buf);
3050 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3055 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3057 if (to_ml && compose->ml_post) return;
3058 if (!to_all || compose->account->protocol == A_NNTP) return;
3060 if (compose->replyto) {
3061 Xstrdup_a(replyto, compose->replyto, return);
3062 extract_address(replyto);
3064 if (msginfo->from) {
3065 Xstrdup_a(from, msginfo->from, return);
3066 extract_address(from);
3069 if (replyto && from)
3070 cc_list = address_list_append_with_comments(cc_list, from);
3071 if (to_all && msginfo->folder &&
3072 msginfo->folder->prefs->enable_default_reply_to)
3073 cc_list = address_list_append_with_comments(cc_list,
3074 msginfo->folder->prefs->default_reply_to);
3075 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3076 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3078 to_table = g_hash_table_new(g_str_hash, g_str_equal);
3080 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
3081 if (compose->account) {
3082 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
3083 GINT_TO_POINTER(1));
3085 /* remove address on To: and that of current account */
3086 for (cur = cc_list; cur != NULL; ) {
3087 GSList *next = cur->next;
3090 addr = g_utf8_strdown(cur->data, -1);
3091 extract_address(addr);
3093 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3094 cc_list = g_slist_remove(cc_list, cur->data);
3096 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3100 hash_free_strings(to_table);
3101 g_hash_table_destroy(to_table);
3104 for (cur = cc_list; cur != NULL; cur = cur->next)
3105 compose_entry_append(compose, (gchar *)cur->data,
3107 slist_free_strings(cc_list);
3108 g_slist_free(cc_list);
3113 #define SET_ENTRY(entry, str) \
3116 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3119 #define SET_ADDRESS(type, str) \
3122 compose_entry_append(compose, str, type); \
3125 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3127 g_return_if_fail(msginfo != NULL);
3129 SET_ENTRY(subject_entry, msginfo->subject);
3130 SET_ENTRY(from_name, msginfo->from);
3131 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3132 SET_ADDRESS(COMPOSE_CC, compose->cc);
3133 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3134 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3135 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3136 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3138 compose_update_priority_menu_item(compose);
3139 compose_update_privacy_system_menu_item(compose, FALSE);
3140 compose_show_first_last_header(compose, TRUE);
3146 static void compose_insert_sig(Compose *compose, gboolean replace)
3148 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3149 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3151 GtkTextIter iter, iter_end;
3152 gint cur_pos, ins_pos;
3153 gboolean prev_autowrap;
3154 gboolean found = FALSE;
3155 gboolean exists = FALSE;
3157 g_return_if_fail(compose->account != NULL);
3161 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3162 G_CALLBACK(compose_changed_cb),
3165 mark = gtk_text_buffer_get_insert(buffer);
3166 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3167 cur_pos = gtk_text_iter_get_offset (&iter);