2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2009 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"
103 #include "spell_entry.h"
115 #define N_ATTACH_COLS (N_COL_COLUMNS)
119 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
127 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
132 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
133 } ComposeCallAdvancedAction;
137 PRIORITY_HIGHEST = 1,
146 COMPOSE_INSERT_SUCCESS,
147 COMPOSE_INSERT_READ_ERROR,
148 COMPOSE_INSERT_INVALID_CHARACTER,
149 COMPOSE_INSERT_NO_FILE
150 } ComposeInsertResult;
154 COMPOSE_WRITE_FOR_SEND,
155 COMPOSE_WRITE_FOR_STORE
160 COMPOSE_QUOTE_FORCED,
165 #define B64_LINE_SIZE 57
166 #define B64_BUFFSIZE 77
168 #define MAX_REFERENCES_LEN 999
170 static GList *compose_list = NULL;
172 static Compose *compose_generic_new (PrefsAccount *account,
175 GPtrArray *attach_files,
176 GList *listAddress );
178 static Compose *compose_create (PrefsAccount *account,
183 static void compose_entry_mark_default_to (Compose *compose,
184 const gchar *address);
185 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
186 ComposeQuoteMode quote_mode,
190 static Compose *compose_forward_multiple (PrefsAccount *account,
191 GSList *msginfo_list);
192 static Compose *compose_reply (MsgInfo *msginfo,
193 ComposeQuoteMode quote_mode,
198 static Compose *compose_reply_mode (ComposeMode mode,
199 GSList *msginfo_list,
201 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
202 static void compose_update_privacy_systems_menu(Compose *compose);
204 static GtkWidget *compose_account_option_menu_create
206 static void compose_set_out_encoding (Compose *compose);
207 static void compose_set_template_menu (Compose *compose);
208 static void compose_template_apply (Compose *compose,
211 static void compose_destroy (Compose *compose);
213 static void compose_entries_set (Compose *compose,
215 ComposeEntryType to_type);
216 static gint compose_parse_header (Compose *compose,
218 static gchar *compose_parse_references (const gchar *ref,
221 static gchar *compose_quote_fmt (Compose *compose,
227 gboolean need_unescape,
228 const gchar *err_msg);
230 static void compose_reply_set_entry (Compose *compose,
236 followup_and_reply_to);
237 static void compose_reedit_set_entry (Compose *compose,
240 static void compose_insert_sig (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);
337 static void compose_dict_changed (void *data);
339 static void compose_add_field_list ( Compose *compose,
340 GList *listAddress );
342 /* callback functions */
344 static gboolean compose_edit_size_alloc (GtkEditable *widget,
345 GtkAllocation *allocation,
346 GtkSHRuler *shruler);
347 static void account_activated (GtkComboBox *optmenu,
349 static void attach_selected (GtkTreeView *tree_view,
350 GtkTreePath *tree_path,
351 GtkTreeViewColumn *column,
353 static gboolean attach_button_pressed (GtkWidget *widget,
354 GdkEventButton *event,
356 static gboolean attach_key_pressed (GtkWidget *widget,
359 static void compose_send_cb (GtkAction *action, gpointer data);
360 static void compose_send_later_cb (GtkAction *action, gpointer data);
362 static void compose_save_cb (GtkAction *action,
365 static void compose_attach_cb (GtkAction *action,
367 static void compose_insert_file_cb (GtkAction *action,
369 static void compose_insert_sig_cb (GtkAction *action,
372 static void compose_close_cb (GtkAction *action,
375 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
377 static void compose_address_cb (GtkAction *action,
379 static void about_show_cb (GtkAction *action,
381 static void compose_template_activate_cb(GtkWidget *widget,
384 static void compose_ext_editor_cb (GtkAction *action,
387 static gint compose_delete_cb (GtkWidget *widget,
391 static void compose_undo_cb (GtkAction *action,
393 static void compose_redo_cb (GtkAction *action,
395 static void compose_cut_cb (GtkAction *action,
397 static void compose_copy_cb (GtkAction *action,
399 static void compose_paste_cb (GtkAction *action,
401 static void compose_paste_as_quote_cb (GtkAction *action,
403 static void compose_paste_no_wrap_cb (GtkAction *action,
405 static void compose_paste_wrap_cb (GtkAction *action,
407 static void compose_allsel_cb (GtkAction *action,
410 static void compose_advanced_action_cb (GtkAction *action,
413 static void compose_grab_focus_cb (GtkWidget *widget,
416 static void compose_changed_cb (GtkTextBuffer *textbuf,
419 static void compose_wrap_cb (GtkAction *action,
421 static void compose_wrap_all_cb (GtkAction *action,
423 static void compose_find_cb (GtkAction *action,
425 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
427 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
430 static void compose_toggle_ruler_cb (GtkToggleAction *action,
432 static void compose_toggle_sign_cb (GtkToggleAction *action,
434 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
436 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
437 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
438 static void activate_privacy_system (Compose *compose,
439 PrefsAccount *account,
441 static void compose_use_signing(Compose *compose, gboolean use_signing);
442 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
443 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
445 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
447 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
448 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
449 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
451 static void compose_attach_drag_received_cb (GtkWidget *widget,
452 GdkDragContext *drag_context,
455 GtkSelectionData *data,
459 static void compose_insert_drag_received_cb (GtkWidget *widget,
460 GdkDragContext *drag_context,
463 GtkSelectionData *data,
467 static void compose_header_drag_received_cb (GtkWidget *widget,
468 GdkDragContext *drag_context,
471 GtkSelectionData *data,
476 static gboolean compose_drag_drop (GtkWidget *widget,
477 GdkDragContext *drag_context,
479 guint time, gpointer user_data);
481 static void text_inserted (GtkTextBuffer *buffer,
486 static Compose *compose_generic_reply(MsgInfo *msginfo,
487 ComposeQuoteMode quote_mode,
491 gboolean followup_and_reply_to,
494 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
495 ComposeHeaderEntry *headerentry);
496 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
498 ComposeHeaderEntry *headerentry);
500 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
502 static void compose_allow_user_actions (Compose *compose, gboolean allow);
504 static void compose_nothing_cb (GtkAction *action, gpointer data)
510 static void compose_check_all (GtkAction *action, gpointer data);
511 static void compose_highlight_all (GtkAction *action, gpointer data);
512 static void compose_check_backwards (GtkAction *action, gpointer data);
513 static void compose_check_forwards_go (GtkAction *action, gpointer data);
516 static gint compose_defer_auto_save_draft (Compose *compose);
517 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
519 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
522 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
523 FolderItem *folder_item);
525 static void compose_attach_update_label(Compose *compose);
527 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data);
529 static GtkActionEntry compose_popup_entries[] =
531 {"Compose", NULL, "Compose" },
532 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
533 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
534 {"Compose/---", NULL, "---", NULL, NULL, NULL },
535 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
538 static GtkActionEntry compose_entries[] =
540 {"Menu", NULL, "Menu" },
542 {"Message", NULL, N_("_Message") },
543 {"Edit", NULL, N_("_Edit") },
545 {"Spelling", NULL, N_("_Spelling") },
547 {"Options", NULL, N_("_Options") },
548 {"Tools", NULL, N_("_Tools") },
549 {"Help", NULL, N_("_Help") },
551 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
552 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
553 {"Message/---", NULL, "---" },
555 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
556 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
557 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
558 /* {"Message/---", NULL, "---" }, */
559 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
560 /* {"Message/---", NULL, "---" }, */
561 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
564 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
565 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
566 {"Edit/---", NULL, "---" },
568 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
569 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
570 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
572 {"Edit/SpecialPaste", NULL, N_("Special paste") },
573 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
574 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
575 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
577 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
579 {"Edit/Advanced", NULL, N_("A_dvanced") },
580 {"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*/
581 {"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*/
582 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
583 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
584 {"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*/
585 {"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*/
586 {"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*/
587 {"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*/
588 {"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*/
589 {"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*/
590 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
591 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
592 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
593 {"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*/
595 /* {"Edit/---", NULL, "---" }, */
596 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
598 /* {"Edit/---", NULL, "---" }, */
599 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
600 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
601 /* {"Edit/---", NULL, "---" }, */
602 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
605 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
606 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
607 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
608 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
610 {"Spelling/---", NULL, "---" },
611 {"Spelling/Options", NULL, N_("_Options") },
616 {"Options/ReplyMode", NULL, N_("Reply _mode") },
617 {"Options/---", NULL, "---" },
618 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
619 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
621 /* {"Options/---", NULL, "---" }, */
623 {"Options/Priority", NULL, N_("_Priority") },
625 {"Options/Encoding", NULL, N_("Character _encoding") },
626 {"Options/Encoding/---", NULL, "---" },
627 #define ENC_ACTION(cs_char,c_char,string) \
628 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
630 {"Options/Encoding/Western", NULL, N_("Western European") },
631 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
632 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
633 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
634 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
635 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
636 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
637 {"Options/Encoding/Korean", NULL, N_("Korean") },
638 {"Options/Encoding/Thai", NULL, N_("Thai") },
641 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
643 {"Tools/Template", NULL, N_("_Template") },
644 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
645 {"Tools/Actions", NULL, N_("Actio_ns") },
646 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
649 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
652 static GtkToggleActionEntry compose_toggle_entries[] =
654 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
655 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
656 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
657 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
658 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
659 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
660 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
663 static GtkRadioActionEntry compose_radio_rm_entries[] =
665 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
666 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
667 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
668 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
671 static GtkRadioActionEntry compose_radio_prio_entries[] =
673 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
674 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
675 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
676 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
677 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
680 static GtkRadioActionEntry compose_radio_enc_entries[] =
682 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
683 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
684 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
685 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
686 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
687 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
688 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
689 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
690 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
691 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
692 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
693 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
715 static GtkTargetEntry compose_mime_types[] =
717 {"text/uri-list", 0, 0},
718 {"UTF8_STRING", 0, 0},
722 static gboolean compose_put_existing_to_front(MsgInfo *info)
724 GList *compose_list = compose_get_compose_list();
728 for (elem = compose_list; elem != NULL && elem->data != NULL;
730 Compose *c = (Compose*)elem->data;
732 if (!c->targetinfo || !c->targetinfo->msgid ||
736 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
737 gtkut_window_popup(c->window);
745 static GdkColor quote_color1 =
746 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
747 static GdkColor quote_color2 =
748 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
749 static GdkColor quote_color3 =
750 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
752 static GdkColor quote_bgcolor1 =
753 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
754 static GdkColor quote_bgcolor2 =
755 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
756 static GdkColor quote_bgcolor3 =
757 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
759 static GdkColor signature_color = {
766 static GdkColor uri_color = {
773 static void compose_create_tags(GtkTextView *text, Compose *compose)
775 GtkTextBuffer *buffer;
776 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
782 buffer = gtk_text_view_get_buffer(text);
784 if (prefs_common.enable_color) {
785 /* grab the quote colors, converting from an int to a GdkColor */
786 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
788 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
790 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
792 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
794 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
796 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
798 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
800 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
803 signature_color = quote_color1 = quote_color2 = quote_color3 =
804 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
807 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
808 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
809 "foreground-gdk", "e_color1,
810 "paragraph-background-gdk", "e_bgcolor1,
812 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
813 "foreground-gdk", "e_color2,
814 "paragraph-background-gdk", "e_bgcolor2,
816 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
817 "foreground-gdk", "e_color3,
818 "paragraph-background-gdk", "e_bgcolor3,
821 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
822 "foreground-gdk", "e_color1,
824 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
825 "foreground-gdk", "e_color2,
827 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
828 "foreground-gdk", "e_color3,
832 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
833 "foreground-gdk", &signature_color,
836 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
837 "foreground-gdk", &uri_color,
839 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
840 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
842 color[0] = quote_color1;
843 color[1] = quote_color2;
844 color[2] = quote_color3;
845 color[3] = quote_bgcolor1;
846 color[4] = quote_bgcolor2;
847 color[5] = quote_bgcolor3;
848 color[6] = signature_color;
849 color[7] = uri_color;
850 cmap = gdk_drawable_get_colormap(compose->window->window);
851 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
853 for (i = 0; i < 8; i++) {
854 if (success[i] == FALSE) {
857 g_warning("Compose: color allocation failed.\n");
858 style = gtk_widget_get_style(GTK_WIDGET(text));
859 quote_color1 = quote_color2 = quote_color3 =
860 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
861 signature_color = uri_color = black;
866 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
867 GPtrArray *attach_files)
869 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
872 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
874 return compose_generic_new(account, mailto, item, NULL, NULL);
877 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
879 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
882 #define SCROLL_TO_CURSOR(compose) { \
883 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
884 gtk_text_view_get_buffer( \
885 GTK_TEXT_VIEW(compose->text))); \
886 gtk_text_view_scroll_mark_onscreen( \
887 GTK_TEXT_VIEW(compose->text), \
891 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
894 if (folderidentifier) {
895 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
896 prefs_common.compose_save_to_history = add_history(
897 prefs_common.compose_save_to_history, folderidentifier);
898 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
899 prefs_common.compose_save_to_history);
902 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
903 if (folderidentifier)
904 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
906 gtk_entry_set_text(GTK_ENTRY(entry), "");
909 static gchar *compose_get_save_to(Compose *compose)
912 gchar *result = NULL;
913 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
914 result = gtk_editable_get_chars(entry, 0, -1);
917 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
918 prefs_common.compose_save_to_history = add_history(
919 prefs_common.compose_save_to_history, result);
920 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
921 prefs_common.compose_save_to_history);
926 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
927 GPtrArray *attach_files, GList *listAddress )
930 GtkTextView *textview;
931 GtkTextBuffer *textbuf;
933 const gchar *subject_format = NULL;
934 const gchar *body_format = NULL;
935 gchar *mailto_from = NULL;
936 PrefsAccount *mailto_account = NULL;
937 MsgInfo* dummyinfo = NULL;
939 /* check if mailto defines a from */
940 if (mailto && *mailto != '\0') {
941 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL);
942 /* mailto defines a from, check if we can get account prefs from it,
943 if not, the account prefs will be guessed using other ways, but we'll keep
946 mailto_account = account_find_from_address(mailto_from, TRUE);
948 account = mailto_account;
951 /* if no account prefs set from mailto, set if from folder prefs (if any) */
952 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
953 account = account_find_from_id(item->prefs->default_account);
955 /* if no account prefs set, fallback to the current one */
956 if (!account) account = cur_account;
957 g_return_val_if_fail(account != NULL, NULL);
959 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
961 /* override from name if mailto asked for it */
963 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
966 /* override from name according to folder properties */
967 if (item && item->prefs &&
968 item->prefs->compose_with_format &&
969 item->prefs->compose_override_from_format &&
970 *item->prefs->compose_override_from_format != '\0') {
975 dummyinfo = compose_msginfo_new_from_compose(compose);
977 /* decode \-escape sequences in the internal representation of the quote format */
978 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
979 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
982 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
985 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
987 quote_fmt_scan_string(tmp);
990 buf = quote_fmt_get_buffer();
992 alertpanel_error(_("New message From format error."));
994 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
995 quote_fmt_reset_vartable();
1000 compose->replyinfo = NULL;
1001 compose->fwdinfo = NULL;
1003 textview = GTK_TEXT_VIEW(compose->text);
1004 textbuf = gtk_text_view_get_buffer(textview);
1005 compose_create_tags(textview, compose);
1007 undo_block(compose->undostruct);
1009 compose_set_dictionaries_from_folder_prefs(compose, item);
1012 if (account->auto_sig)
1013 compose_insert_sig(compose, FALSE);
1014 gtk_text_buffer_get_start_iter(textbuf, &iter);
1015 gtk_text_buffer_place_cursor(textbuf, &iter);
1017 if (account->protocol != A_NNTP) {
1018 if (mailto && *mailto != '\0') {
1019 compose_entries_set(compose, mailto, COMPOSE_TO);
1021 } else if (item && item->prefs->enable_default_to) {
1022 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
1023 compose_entry_mark_default_to(compose, item->prefs->default_to);
1025 if (item && item->ret_rcpt) {
1026 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1029 if (mailto && *mailto != '\0') {
1030 if (!strchr(mailto, '@'))
1031 compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1033 compose_entries_set(compose, mailto, COMPOSE_TO);
1034 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1035 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1038 * CLAWS: just don't allow return receipt request, even if the user
1039 * may want to send an email. simple but foolproof.
1041 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1043 compose_add_field_list( compose, listAddress );
1045 if (item && item->prefs && item->prefs->compose_with_format) {
1046 subject_format = item->prefs->compose_subject_format;
1047 body_format = item->prefs->compose_body_format;
1048 } else if (account->compose_with_format) {
1049 subject_format = account->compose_subject_format;
1050 body_format = account->compose_body_format;
1051 } else if (prefs_common.compose_with_format) {
1052 subject_format = prefs_common.compose_subject_format;
1053 body_format = prefs_common.compose_body_format;
1056 if (subject_format || body_format) {
1059 && *subject_format != '\0' )
1061 gchar *subject = NULL;
1066 dummyinfo = compose_msginfo_new_from_compose(compose);
1068 /* decode \-escape sequences in the internal representation of the quote format */
1069 tmp = malloc(strlen(subject_format)+1);
1070 pref_get_unescaped_pref(tmp, subject_format);
1072 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1074 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1075 compose->gtkaspell);
1077 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1079 quote_fmt_scan_string(tmp);
1082 buf = quote_fmt_get_buffer();
1084 alertpanel_error(_("New message subject format error."));
1086 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1087 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1088 quote_fmt_reset_vartable();
1095 && *body_format != '\0' )
1098 GtkTextBuffer *buffer;
1099 GtkTextIter start, end;
1103 dummyinfo = compose_msginfo_new_from_compose(compose);
1105 text = GTK_TEXT_VIEW(compose->text);
1106 buffer = gtk_text_view_get_buffer(text);
1107 gtk_text_buffer_get_start_iter(buffer, &start);
1108 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1109 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1111 compose_quote_fmt(compose, dummyinfo,
1113 NULL, tmp, FALSE, TRUE,
1114 _("New message body format error at line %d."));
1115 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1116 quote_fmt_reset_vartable();
1122 procmsg_msginfo_free( dummyinfo );
1128 for (i = 0; i < attach_files->len; i++) {
1129 file = g_ptr_array_index(attach_files, i);
1130 compose_attach_append(compose, file, file, NULL);
1134 compose_show_first_last_header(compose, TRUE);
1136 /* Set save folder */
1137 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1138 gchar *folderidentifier;
1140 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1141 folderidentifier = folder_item_get_identifier(item);
1142 compose_set_save_to(compose, folderidentifier);
1143 g_free(folderidentifier);
1146 gtk_widget_grab_focus(compose->header_last->entry);
1148 undo_unblock(compose->undostruct);
1150 if (prefs_common.auto_exteditor)
1151 compose_exec_ext_editor(compose);
1153 compose->draft_timeout_tag = -1;
1154 SCROLL_TO_CURSOR(compose);
1156 compose->modified = FALSE;
1157 compose_set_title(compose);
1161 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1162 gboolean override_pref, const gchar *system)
1164 const gchar *privacy = NULL;
1166 g_return_if_fail(compose != NULL);
1167 g_return_if_fail(account != NULL);
1169 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1174 else if (account->default_privacy_system
1175 && strlen(account->default_privacy_system)) {
1176 privacy = account->default_privacy_system;
1178 GSList *privacy_avail = privacy_get_system_ids();
1179 if (privacy_avail && g_slist_length(privacy_avail)) {
1180 privacy = (gchar *)(privacy_avail->data);
1183 if (privacy != NULL) {
1185 g_free(compose->privacy_system);
1186 compose->privacy_system = NULL;
1188 if (compose->privacy_system == NULL)
1189 compose->privacy_system = g_strdup(privacy);
1190 else if (*(compose->privacy_system) == '\0') {
1191 g_free(compose->privacy_system);
1192 compose->privacy_system = g_strdup(privacy);
1194 compose_update_privacy_system_menu_item(compose, FALSE);
1195 compose_use_encryption(compose, TRUE);
1199 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1201 gchar *privacy = NULL;
1205 else if (account->default_privacy_system
1206 && strlen(account->default_privacy_system)) {
1207 privacy = account->default_privacy_system;
1209 GSList *privacy_avail = privacy_get_system_ids();
1210 if (privacy_avail && g_slist_length(privacy_avail)) {
1211 privacy = (gchar *)(privacy_avail->data);
1215 if (privacy != NULL) {
1217 g_free(compose->privacy_system);
1218 compose->privacy_system = NULL;
1220 if (compose->privacy_system == NULL)
1221 compose->privacy_system = g_strdup(privacy);
1222 compose_update_privacy_system_menu_item(compose, FALSE);
1223 compose_use_signing(compose, TRUE);
1227 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1231 Compose *compose = NULL;
1233 g_return_val_if_fail(msginfo_list != NULL, NULL);
1235 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1236 g_return_val_if_fail(msginfo != NULL, NULL);
1238 list_len = g_slist_length(msginfo_list);
1242 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1243 FALSE, prefs_common.default_reply_list, FALSE, body);
1245 case COMPOSE_REPLY_WITH_QUOTE:
1246 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1247 FALSE, prefs_common.default_reply_list, FALSE, body);
1249 case COMPOSE_REPLY_WITHOUT_QUOTE:
1250 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1251 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1253 case COMPOSE_REPLY_TO_SENDER:
1254 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1255 FALSE, FALSE, TRUE, body);
1257 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1258 compose = compose_followup_and_reply_to(msginfo,
1259 COMPOSE_QUOTE_CHECK,
1260 FALSE, FALSE, body);
1262 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1263 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1264 FALSE, FALSE, TRUE, body);
1266 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1267 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1268 FALSE, FALSE, TRUE, NULL);
1270 case COMPOSE_REPLY_TO_ALL:
1271 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1272 TRUE, FALSE, FALSE, body);
1274 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1275 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1276 TRUE, FALSE, FALSE, body);
1278 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1279 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1280 TRUE, FALSE, FALSE, NULL);
1282 case COMPOSE_REPLY_TO_LIST:
1283 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1284 FALSE, TRUE, FALSE, body);
1286 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1287 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1288 FALSE, TRUE, FALSE, body);
1290 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1291 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1292 FALSE, TRUE, FALSE, NULL);
1294 case COMPOSE_FORWARD:
1295 if (prefs_common.forward_as_attachment) {
1296 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1299 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1303 case COMPOSE_FORWARD_INLINE:
1304 /* check if we reply to more than one Message */
1305 if (list_len == 1) {
1306 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1309 /* more messages FALL THROUGH */
1310 case COMPOSE_FORWARD_AS_ATTACH:
1311 compose = compose_forward_multiple(NULL, msginfo_list);
1313 case COMPOSE_REDIRECT:
1314 compose = compose_redirect(NULL, msginfo, FALSE);
1317 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1320 if (compose == NULL) {
1321 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1325 compose->rmode = mode;
1326 switch (compose->rmode) {
1328 case COMPOSE_REPLY_WITH_QUOTE:
1329 case COMPOSE_REPLY_WITHOUT_QUOTE:
1330 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1331 debug_print("reply mode Normal\n");
1332 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1333 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1335 case COMPOSE_REPLY_TO_SENDER:
1336 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1337 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1338 debug_print("reply mode Sender\n");
1339 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1341 case COMPOSE_REPLY_TO_ALL:
1342 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1343 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1344 debug_print("reply mode All\n");
1345 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1347 case COMPOSE_REPLY_TO_LIST:
1348 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1349 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1350 debug_print("reply mode List\n");
1351 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1359 static Compose *compose_reply(MsgInfo *msginfo,
1360 ComposeQuoteMode quote_mode,
1366 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1367 to_sender, FALSE, body);
1370 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1371 ComposeQuoteMode quote_mode,
1376 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1377 to_sender, TRUE, body);
1380 static void compose_extract_original_charset(Compose *compose)
1382 MsgInfo *info = NULL;
1383 if (compose->replyinfo) {
1384 info = compose->replyinfo;
1385 } else if (compose->fwdinfo) {
1386 info = compose->fwdinfo;
1387 } else if (compose->targetinfo) {
1388 info = compose->targetinfo;
1391 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1392 MimeInfo *partinfo = mimeinfo;
1393 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1394 partinfo = procmime_mimeinfo_next(partinfo);
1396 compose->orig_charset =
1397 g_strdup(procmime_mimeinfo_get_parameter(
1398 partinfo, "charset"));
1400 procmime_mimeinfo_free_all(mimeinfo);
1404 #define SIGNAL_BLOCK(buffer) { \
1405 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1406 G_CALLBACK(compose_changed_cb), \
1408 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1409 G_CALLBACK(text_inserted), \
1413 #define SIGNAL_UNBLOCK(buffer) { \
1414 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1415 G_CALLBACK(compose_changed_cb), \
1417 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1418 G_CALLBACK(text_inserted), \
1422 static Compose *compose_generic_reply(MsgInfo *msginfo,
1423 ComposeQuoteMode quote_mode,
1424 gboolean to_all, gboolean to_ml,
1426 gboolean followup_and_reply_to,
1430 PrefsAccount *account = NULL;
1431 GtkTextView *textview;
1432 GtkTextBuffer *textbuf;
1433 gboolean quote = FALSE;
1434 const gchar *qmark = NULL;
1435 const gchar *body_fmt = NULL;
1436 gchar *s_system = NULL;
1438 g_return_val_if_fail(msginfo != NULL, NULL);
1439 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1441 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1443 g_return_val_if_fail(account != NULL, NULL);
1445 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1447 compose->updating = TRUE;
1449 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1450 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1452 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1453 if (!compose->replyinfo)
1454 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1456 compose_extract_original_charset(compose);
1458 if (msginfo->folder && msginfo->folder->ret_rcpt)
1459 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1461 /* Set save folder */
1462 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1463 gchar *folderidentifier;
1465 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1466 folderidentifier = folder_item_get_identifier(msginfo->folder);
1467 compose_set_save_to(compose, folderidentifier);
1468 g_free(folderidentifier);
1471 if (compose_parse_header(compose, msginfo) < 0) {
1472 compose->updating = FALSE;
1473 compose_destroy(compose);
1477 /* override from name according to folder properties */
1478 if (msginfo->folder && msginfo->folder->prefs &&
1479 msginfo->folder->prefs->reply_with_format &&
1480 msginfo->folder->prefs->reply_override_from_format &&
1481 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1486 /* decode \-escape sequences in the internal representation of the quote format */
1487 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1488 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1491 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1492 compose->gtkaspell);
1494 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1496 quote_fmt_scan_string(tmp);
1499 buf = quote_fmt_get_buffer();
1501 alertpanel_error(_("Message reply From format error."));
1503 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1504 quote_fmt_reset_vartable();
1509 textview = (GTK_TEXT_VIEW(compose->text));
1510 textbuf = gtk_text_view_get_buffer(textview);
1511 compose_create_tags(textview, compose);
1513 undo_block(compose->undostruct);
1515 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1518 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1519 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1520 /* use the reply format of folder (if enabled), or the account's one
1521 (if enabled) or fallback to the global reply format, which is always
1522 enabled (even if empty), and use the relevant quotemark */
1524 if (msginfo->folder && msginfo->folder->prefs &&
1525 msginfo->folder->prefs->reply_with_format) {
1526 qmark = msginfo->folder->prefs->reply_quotemark;
1527 body_fmt = msginfo->folder->prefs->reply_body_format;
1529 } else if (account->reply_with_format) {
1530 qmark = account->reply_quotemark;
1531 body_fmt = account->reply_body_format;
1534 qmark = prefs_common.quotemark;
1535 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1536 body_fmt = gettext(prefs_common.quotefmt);
1543 /* empty quotemark is not allowed */
1544 if (qmark == NULL || *qmark == '\0')
1546 compose_quote_fmt(compose, compose->replyinfo,
1547 body_fmt, qmark, body, FALSE, TRUE,
1548 _("Message reply format error at line %d."));
1549 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1550 quote_fmt_reset_vartable();
1553 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1554 compose_force_encryption(compose, account, FALSE, s_system);
1557 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1558 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1559 compose_force_signing(compose, account, s_system);
1563 SIGNAL_BLOCK(textbuf);
1565 if (account->auto_sig)
1566 compose_insert_sig(compose, FALSE);
1568 compose_wrap_all(compose);
1570 SIGNAL_UNBLOCK(textbuf);
1572 gtk_widget_grab_focus(compose->text);
1574 undo_unblock(compose->undostruct);
1576 if (prefs_common.auto_exteditor)
1577 compose_exec_ext_editor(compose);
1579 compose->modified = FALSE;
1580 compose_set_title(compose);
1582 compose->updating = FALSE;
1583 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1584 SCROLL_TO_CURSOR(compose);
1586 if (compose->deferred_destroy) {
1587 compose_destroy(compose);
1594 #define INSERT_FW_HEADER(var, hdr) \
1595 if (msginfo->var && *msginfo->var) { \
1596 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1597 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1598 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1601 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1602 gboolean as_attach, const gchar *body,
1603 gboolean no_extedit,
1607 GtkTextView *textview;
1608 GtkTextBuffer *textbuf;
1611 g_return_val_if_fail(msginfo != NULL, NULL);
1612 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1615 !(account = compose_guess_forward_account_from_msginfo
1617 account = cur_account;
1619 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1621 compose->updating = TRUE;
1622 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1623 if (!compose->fwdinfo)
1624 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1626 compose_extract_original_charset(compose);
1628 if (msginfo->subject && *msginfo->subject) {
1629 gchar *buf, *buf2, *p;
1631 buf = p = g_strdup(msginfo->subject);
1632 p += subject_get_prefix_length(p);
1633 memmove(buf, p, strlen(p) + 1);
1635 buf2 = g_strdup_printf("Fw: %s", buf);
1636 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1642 /* override from name according to folder properties */
1643 if (msginfo->folder && msginfo->folder->prefs &&
1644 msginfo->folder->prefs->forward_with_format &&
1645 msginfo->folder->prefs->forward_override_from_format &&
1646 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1650 MsgInfo *full_msginfo = NULL;
1653 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1655 full_msginfo = procmsg_msginfo_copy(msginfo);
1657 /* decode \-escape sequences in the internal representation of the quote format */
1658 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1659 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1662 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1663 compose->gtkaspell);
1665 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1667 quote_fmt_scan_string(tmp);
1670 buf = quote_fmt_get_buffer();
1672 alertpanel_error(_("Message forward From format error."));
1674 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1675 quote_fmt_reset_vartable();
1678 procmsg_msginfo_free(full_msginfo);
1681 textview = GTK_TEXT_VIEW(compose->text);
1682 textbuf = gtk_text_view_get_buffer(textview);
1683 compose_create_tags(textview, compose);
1685 undo_block(compose->undostruct);
1689 msgfile = procmsg_get_message_file(msginfo);
1690 if (!is_file_exist(msgfile))
1691 g_warning("%s: file not exist\n", msgfile);
1693 compose_attach_append(compose, msgfile, msgfile,
1698 const gchar *qmark = NULL;
1699 const gchar *body_fmt = NULL;
1700 MsgInfo *full_msginfo;
1702 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1703 body_fmt = gettext(prefs_common.fw_quotefmt);
1707 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1709 full_msginfo = procmsg_msginfo_copy(msginfo);
1711 /* use the forward format of folder (if enabled), or the account's one
1712 (if enabled) or fallback to the global forward format, which is always
1713 enabled (even if empty), and use the relevant quotemark */
1714 if (msginfo->folder && msginfo->folder->prefs &&
1715 msginfo->folder->prefs->forward_with_format) {
1716 qmark = msginfo->folder->prefs->forward_quotemark;
1717 body_fmt = msginfo->folder->prefs->forward_body_format;
1719 } else if (account->forward_with_format) {
1720 qmark = account->forward_quotemark;
1721 body_fmt = account->forward_body_format;
1724 qmark = prefs_common.fw_quotemark;
1725 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1726 body_fmt = gettext(prefs_common.fw_quotefmt);
1731 /* empty quotemark is not allowed */
1732 if (qmark == NULL || *qmark == '\0')
1735 compose_quote_fmt(compose, full_msginfo,
1736 body_fmt, qmark, body, FALSE, TRUE,
1737 _("Message forward format error at line %d."));
1738 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1739 quote_fmt_reset_vartable();
1740 compose_attach_parts(compose, msginfo);
1742 procmsg_msginfo_free(full_msginfo);
1745 SIGNAL_BLOCK(textbuf);
1747 if (account->auto_sig)
1748 compose_insert_sig(compose, FALSE);
1750 compose_wrap_all(compose);
1752 SIGNAL_UNBLOCK(textbuf);
1754 gtk_text_buffer_get_start_iter(textbuf, &iter);
1755 gtk_text_buffer_place_cursor(textbuf, &iter);
1757 gtk_widget_grab_focus(compose->header_last->entry);
1759 if (!no_extedit && prefs_common.auto_exteditor)
1760 compose_exec_ext_editor(compose);
1763 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1764 gchar *folderidentifier;
1766 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1767 folderidentifier = folder_item_get_identifier(msginfo->folder);
1768 compose_set_save_to(compose, folderidentifier);
1769 g_free(folderidentifier);
1772 undo_unblock(compose->undostruct);
1774 compose->modified = FALSE;
1775 compose_set_title(compose);
1777 compose->updating = FALSE;
1778 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1779 SCROLL_TO_CURSOR(compose);
1781 if (compose->deferred_destroy) {
1782 compose_destroy(compose);
1789 #undef INSERT_FW_HEADER
1791 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1794 GtkTextView *textview;
1795 GtkTextBuffer *textbuf;
1799 gboolean single_mail = TRUE;
1801 g_return_val_if_fail(msginfo_list != NULL, NULL);
1803 if (g_slist_length(msginfo_list) > 1)
1804 single_mail = FALSE;
1806 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1807 if (((MsgInfo *)msginfo->data)->folder == NULL)
1810 /* guess account from first selected message */
1812 !(account = compose_guess_forward_account_from_msginfo
1813 (msginfo_list->data)))
1814 account = cur_account;
1816 g_return_val_if_fail(account != NULL, NULL);
1818 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1819 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1820 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1823 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1825 compose->updating = TRUE;
1827 /* override from name according to folder properties */
1828 if (msginfo_list->data) {
1829 MsgInfo *msginfo = msginfo_list->data;
1831 if (msginfo->folder && msginfo->folder->prefs &&
1832 msginfo->folder->prefs->forward_with_format &&
1833 msginfo->folder->prefs->forward_override_from_format &&
1834 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1839 /* decode \-escape sequences in the internal representation of the quote format */
1840 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1841 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1844 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1845 compose->gtkaspell);
1847 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1849 quote_fmt_scan_string(tmp);
1852 buf = quote_fmt_get_buffer();
1854 alertpanel_error(_("Message forward From format error."));
1856 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1857 quote_fmt_reset_vartable();
1863 textview = GTK_TEXT_VIEW(compose->text);
1864 textbuf = gtk_text_view_get_buffer(textview);
1865 compose_create_tags(textview, compose);
1867 undo_block(compose->undostruct);
1868 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1869 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1871 if (!is_file_exist(msgfile))
1872 g_warning("%s: file not exist\n", msgfile);
1874 compose_attach_append(compose, msgfile, msgfile,
1880 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1881 if (info->subject && *info->subject) {
1882 gchar *buf, *buf2, *p;
1884 buf = p = g_strdup(info->subject);
1885 p += subject_get_prefix_length(p);
1886 memmove(buf, p, strlen(p) + 1);
1888 buf2 = g_strdup_printf("Fw: %s", buf);
1889 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1895 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1896 _("Fw: multiple emails"));
1899 SIGNAL_BLOCK(textbuf);
1901 if (account->auto_sig)
1902 compose_insert_sig(compose, FALSE);
1904 compose_wrap_all(compose);
1906 SIGNAL_UNBLOCK(textbuf);
1908 gtk_text_buffer_get_start_iter(textbuf, &iter);
1909 gtk_text_buffer_place_cursor(textbuf, &iter);
1911 gtk_widget_grab_focus(compose->header_last->entry);
1912 undo_unblock(compose->undostruct);
1913 compose->modified = FALSE;
1914 compose_set_title(compose);
1916 compose->updating = FALSE;
1917 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1918 SCROLL_TO_CURSOR(compose);
1920 if (compose->deferred_destroy) {
1921 compose_destroy(compose);
1928 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1930 GtkTextIter start = *iter;
1931 GtkTextIter end_iter;
1932 int start_pos = gtk_text_iter_get_offset(&start);
1934 if (!compose->account->sig_sep)
1937 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1938 start_pos+strlen(compose->account->sig_sep));
1940 /* check sig separator */
1941 str = gtk_text_iter_get_text(&start, &end_iter);
1942 if (!strcmp(str, compose->account->sig_sep)) {
1944 /* check end of line (\n) */
1945 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1946 start_pos+strlen(compose->account->sig_sep));
1947 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1948 start_pos+strlen(compose->account->sig_sep)+1);
1949 tmp = gtk_text_iter_get_text(&start, &end_iter);
1950 if (!strcmp(tmp,"\n")) {
1962 static void compose_colorize_signature(Compose *compose)
1964 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1966 GtkTextIter end_iter;
1967 gtk_text_buffer_get_start_iter(buffer, &iter);
1968 while (gtk_text_iter_forward_line(&iter))
1969 if (compose_is_sig_separator(compose, buffer, &iter)) {
1970 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1971 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1975 #define BLOCK_WRAP() { \
1976 prev_autowrap = compose->autowrap; \
1977 buffer = gtk_text_view_get_buffer( \
1978 GTK_TEXT_VIEW(compose->text)); \
1979 compose->autowrap = FALSE; \
1981 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1982 G_CALLBACK(compose_changed_cb), \
1984 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1985 G_CALLBACK(text_inserted), \
1988 #define UNBLOCK_WRAP() { \
1989 compose->autowrap = prev_autowrap; \
1990 if (compose->autowrap) { \
1991 gint old = compose->draft_timeout_tag; \
1992 compose->draft_timeout_tag = -2; \
1993 compose_wrap_all(compose); \
1994 compose->draft_timeout_tag = old; \
1997 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1998 G_CALLBACK(compose_changed_cb), \
2000 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2001 G_CALLBACK(text_inserted), \
2005 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2007 Compose *compose = NULL;
2008 PrefsAccount *account = NULL;
2009 GtkTextView *textview;
2010 GtkTextBuffer *textbuf;
2014 gchar buf[BUFFSIZE];
2015 gboolean use_signing = FALSE;
2016 gboolean use_encryption = FALSE;
2017 gchar *privacy_system = NULL;
2018 int priority = PRIORITY_NORMAL;
2019 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2020 gboolean autowrap = prefs_common.autowrap;
2021 gboolean autoindent = prefs_common.auto_indent;
2023 g_return_val_if_fail(msginfo != NULL, NULL);
2024 g_return_val_if_fail(msginfo->folder != NULL, NULL);
2026 if (compose_put_existing_to_front(msginfo)) {
2030 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2031 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2032 gchar queueheader_buf[BUFFSIZE];
2035 /* Select Account from queue headers */
2036 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2037 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2038 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2039 account = account_find_from_id(id);
2041 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2042 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2043 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2044 account = account_find_from_id(id);
2046 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2047 sizeof(queueheader_buf), "NAID:")) {
2048 id = atoi(&queueheader_buf[strlen("NAID:")]);
2049 account = account_find_from_id(id);
2051 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2052 sizeof(queueheader_buf), "MAID:")) {
2053 id = atoi(&queueheader_buf[strlen("MAID:")]);
2054 account = account_find_from_id(id);
2056 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2057 sizeof(queueheader_buf), "S:")) {
2058 account = account_find_from_address(queueheader_buf, FALSE);
2060 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2061 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2062 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2063 use_signing = param;
2066 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2067 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2068 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2069 use_signing = param;
2072 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2073 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2074 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2075 use_encryption = param;
2077 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2078 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2079 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2080 use_encryption = param;
2082 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2083 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2084 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2087 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2088 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2089 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2092 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2093 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2094 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2096 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2097 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2098 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2100 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2101 sizeof(queueheader_buf), "X-Priority: ")) {
2102 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2105 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2106 sizeof(queueheader_buf), "RMID:")) {
2107 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2108 if (tokens[0] && tokens[1] && tokens[2]) {
2109 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2110 if (orig_item != NULL) {
2111 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2116 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2117 sizeof(queueheader_buf), "FMID:")) {
2118 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2119 if (tokens[0] && tokens[1] && tokens[2]) {
2120 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2121 if (orig_item != NULL) {
2122 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2128 account = msginfo->folder->folder->account;
2131 if (!account && prefs_common.reedit_account_autosel) {
2132 gchar from[BUFFSIZE];
2133 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2134 extract_address(from);
2135 account = account_find_from_address(from, FALSE);
2139 account = cur_account;
2141 g_return_val_if_fail(account != NULL, NULL);
2143 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2145 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2146 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2147 compose->autowrap = autowrap;
2148 compose->replyinfo = replyinfo;
2149 compose->fwdinfo = fwdinfo;
2151 compose->updating = TRUE;
2152 compose->priority = priority;
2154 if (privacy_system != NULL) {
2155 compose->privacy_system = privacy_system;
2156 compose_use_signing(compose, use_signing);
2157 compose_use_encryption(compose, use_encryption);
2158 compose_update_privacy_system_menu_item(compose, FALSE);
2160 activate_privacy_system(compose, account, FALSE);
2163 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2165 compose_extract_original_charset(compose);
2167 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2168 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2169 gchar queueheader_buf[BUFFSIZE];
2171 /* Set message save folder */
2172 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2173 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2174 compose_set_save_to(compose, &queueheader_buf[4]);
2176 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2177 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2179 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2184 if (compose_parse_header(compose, msginfo) < 0) {
2185 compose->updating = FALSE;
2186 compose_destroy(compose);
2189 compose_reedit_set_entry(compose, msginfo);
2191 textview = GTK_TEXT_VIEW(compose->text);
2192 textbuf = gtk_text_view_get_buffer(textview);
2193 compose_create_tags(textview, compose);
2195 mark = gtk_text_buffer_get_insert(textbuf);
2196 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2198 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2199 G_CALLBACK(compose_changed_cb),
2202 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2203 fp = procmime_get_first_encrypted_text_content(msginfo);
2205 compose_force_encryption(compose, account, TRUE, NULL);
2208 fp = procmime_get_first_text_content(msginfo);
2211 g_warning("Can't get text part\n");
2215 gboolean prev_autowrap = compose->autowrap;
2216 GtkTextBuffer *buffer = textbuf;
2218 while (fgets(buf, sizeof(buf), fp) != NULL) {
2220 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2226 compose_attach_parts(compose, msginfo);
2228 compose_colorize_signature(compose);
2230 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2231 G_CALLBACK(compose_changed_cb),
2234 gtk_widget_grab_focus(compose->text);
2236 if (prefs_common.auto_exteditor) {
2237 compose_exec_ext_editor(compose);
2239 compose->modified = FALSE;
2240 compose_set_title(compose);
2242 compose->updating = FALSE;
2243 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2244 SCROLL_TO_CURSOR(compose);
2246 if (compose->deferred_destroy) {
2247 compose_destroy(compose);
2251 compose->sig_str = account_get_signature_str(compose->account);
2256 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2263 g_return_val_if_fail(msginfo != NULL, NULL);
2266 account = account_get_reply_account(msginfo,
2267 prefs_common.reply_account_autosel);
2268 g_return_val_if_fail(account != NULL, NULL);
2270 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2272 compose->updating = TRUE;
2274 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2275 compose->replyinfo = NULL;
2276 compose->fwdinfo = NULL;
2278 compose_show_first_last_header(compose, TRUE);
2280 gtk_widget_grab_focus(compose->header_last->entry);
2282 filename = procmsg_get_message_file(msginfo);
2284 if (filename == NULL) {
2285 compose->updating = FALSE;
2286 compose_destroy(compose);
2291 compose->redirect_filename = filename;
2293 /* Set save folder */
2294 item = msginfo->folder;
2295 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2296 gchar *folderidentifier;
2298 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2299 folderidentifier = folder_item_get_identifier(item);
2300 compose_set_save_to(compose, folderidentifier);
2301 g_free(folderidentifier);
2304 compose_attach_parts(compose, msginfo);
2306 if (msginfo->subject)
2307 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2309 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2311 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2312 _("Message redirect format error at line %d."));
2313 quote_fmt_reset_vartable();
2314 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2316 compose_colorize_signature(compose);
2319 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2320 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2321 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2323 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2324 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2325 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2326 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2327 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2328 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2329 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2330 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2332 if (compose->toolbar->draft_btn)
2333 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2334 if (compose->toolbar->insert_btn)
2335 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2336 if (compose->toolbar->attach_btn)
2337 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2338 if (compose->toolbar->sig_btn)
2339 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2340 if (compose->toolbar->exteditor_btn)
2341 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2342 if (compose->toolbar->linewrap_current_btn)
2343 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2344 if (compose->toolbar->linewrap_all_btn)
2345 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2347 compose->modified = FALSE;
2348 compose_set_title(compose);
2349 compose->updating = FALSE;
2350 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2351 SCROLL_TO_CURSOR(compose);
2353 if (compose->deferred_destroy) {
2354 compose_destroy(compose);
2361 GList *compose_get_compose_list(void)
2363 return compose_list;
2366 void compose_entry_append(Compose *compose, const gchar *address,
2367 ComposeEntryType type)
2369 const gchar *header;
2371 gboolean in_quote = FALSE;
2372 if (!address || *address == '\0') return;
2379 header = N_("Bcc:");
2381 case COMPOSE_REPLYTO:
2382 header = N_("Reply-To:");
2384 case COMPOSE_NEWSGROUPS:
2385 header = N_("Newsgroups:");
2387 case COMPOSE_FOLLOWUPTO:
2388 header = N_( "Followup-To:");
2395 header = prefs_common_translated_header_name(header);
2397 cur = begin = (gchar *)address;
2399 /* we separate the line by commas, but not if we're inside a quoted
2401 while (*cur != '\0') {
2403 in_quote = !in_quote;
2404 if (*cur == ',' && !in_quote) {
2405 gchar *tmp = g_strdup(begin);
2407 tmp[cur-begin]='\0';
2410 while (*tmp == ' ' || *tmp == '\t')
2412 compose_add_header_entry(compose, header, tmp);
2419 gchar *tmp = g_strdup(begin);
2421 tmp[cur-begin]='\0';
2424 while (*tmp == ' ' || *tmp == '\t')
2426 compose_add_header_entry(compose, header, tmp);
2431 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2433 static GdkColor yellow;
2434 static GdkColor black;
2435 static gboolean yellow_initialised = FALSE;
2439 if (!yellow_initialised) {
2440 gdk_color_parse("#f5f6be", &yellow);
2441 gdk_color_parse("#000000", &black);
2442 yellow_initialised = gdk_colormap_alloc_color(
2443 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2444 yellow_initialised &= gdk_colormap_alloc_color(
2445 gdk_colormap_get_system(), &black, FALSE, TRUE);
2448 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2449 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2450 if (gtk_entry_get_text(entry) &&
2451 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2452 if (yellow_initialised) {
2453 gtk_widget_modify_base(
2454 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2455 GTK_STATE_NORMAL, &yellow);
2456 gtk_widget_modify_text(
2457 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2458 GTK_STATE_NORMAL, &black);
2464 void compose_toolbar_cb(gint action, gpointer data)
2466 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2467 Compose *compose = (Compose*)toolbar_item->parent;
2469 g_return_if_fail(compose != NULL);
2473 compose_send_cb(NULL, compose);
2476 compose_send_later_cb(NULL, compose);
2479 compose_draft(compose, COMPOSE_QUIT_EDITING);
2482 compose_insert_file_cb(NULL, compose);
2485 compose_attach_cb(NULL, compose);
2488 compose_insert_sig(compose, FALSE);
2491 compose_ext_editor_cb(NULL, compose);
2493 case A_LINEWRAP_CURRENT:
2494 compose_beautify_paragraph(compose, NULL, TRUE);
2496 case A_LINEWRAP_ALL:
2497 compose_wrap_all_full(compose, TRUE);
2500 compose_address_cb(NULL, compose);
2503 case A_CHECK_SPELLING:
2504 compose_check_all(NULL, compose);
2512 static void compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2517 gchar *subject = NULL;
2521 gchar **attach = NULL;
2523 /* get mailto parts but skip from */
2524 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2527 compose_entry_append(compose, to, to_type);
2529 compose_entry_append(compose, cc, COMPOSE_CC);
2531 compose_entry_append(compose, bcc, COMPOSE_BCC);
2533 if (!g_utf8_validate (subject, -1, NULL)) {
2534 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2535 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2538 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2542 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2543 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2546 gboolean prev_autowrap = compose->autowrap;
2548 compose->autowrap = FALSE;
2550 mark = gtk_text_buffer_get_insert(buffer);
2551 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2553 if (!g_utf8_validate (body, -1, NULL)) {
2554 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2555 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2558 gtk_text_buffer_insert(buffer, &iter, body, -1);
2560 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2562 compose->autowrap = prev_autowrap;
2563 if (compose->autowrap)
2564 compose_wrap_all(compose);
2568 gint i = 0, att = 0;
2569 gchar *warn_files = NULL;
2570 while (attach[i] != NULL) {
2571 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2572 if (utf8_filename) {
2573 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2574 gchar *tmp = g_strdup_printf("%s%s\n",
2575 warn_files?warn_files:"",
2581 g_free(utf8_filename);
2583 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2588 alertpanel_notice(ngettext(
2589 "The following file has been attached: \n%s",
2590 "The following files have been attached: \n%s", att), warn_files);
2602 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2604 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2605 {"Cc:", NULL, TRUE},
2606 {"References:", NULL, FALSE},
2607 {"Bcc:", NULL, TRUE},
2608 {"Newsgroups:", NULL, TRUE},
2609 {"Followup-To:", NULL, TRUE},
2610 {"List-Post:", NULL, FALSE},
2611 {"X-Priority:", NULL, FALSE},
2612 {NULL, NULL, FALSE}};
2628 g_return_val_if_fail(msginfo != NULL, -1);
2630 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2631 procheader_get_header_fields(fp, hentry);
2634 if (hentry[H_REPLY_TO].body != NULL) {
2635 if (hentry[H_REPLY_TO].body[0] != '\0') {
2637 conv_unmime_header(hentry[H_REPLY_TO].body,
2640 g_free(hentry[H_REPLY_TO].body);
2641 hentry[H_REPLY_TO].body = NULL;
2643 if (hentry[H_CC].body != NULL) {
2644 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2645 g_free(hentry[H_CC].body);
2646 hentry[H_CC].body = NULL;
2648 if (hentry[H_REFERENCES].body != NULL) {
2649 if (compose->mode == COMPOSE_REEDIT)
2650 compose->references = hentry[H_REFERENCES].body;
2652 compose->references = compose_parse_references
2653 (hentry[H_REFERENCES].body, msginfo->msgid);
2654 g_free(hentry[H_REFERENCES].body);
2656 hentry[H_REFERENCES].body = NULL;
2658 if (hentry[H_BCC].body != NULL) {
2659 if (compose->mode == COMPOSE_REEDIT)
2661 conv_unmime_header(hentry[H_BCC].body, NULL);
2662 g_free(hentry[H_BCC].body);
2663 hentry[H_BCC].body = NULL;
2665 if (hentry[H_NEWSGROUPS].body != NULL) {
2666 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2667 hentry[H_NEWSGROUPS].body = NULL;
2669 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2670 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2671 compose->followup_to =
2672 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2675 g_free(hentry[H_FOLLOWUP_TO].body);
2676 hentry[H_FOLLOWUP_TO].body = NULL;
2678 if (hentry[H_LIST_POST].body != NULL) {
2681 extract_address(hentry[H_LIST_POST].body);
2682 if (hentry[H_LIST_POST].body[0] != '\0') {
2683 scan_mailto_url(hentry[H_LIST_POST].body,
2684 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2686 g_free(compose->ml_post);
2687 compose->ml_post = to;
2690 g_free(hentry[H_LIST_POST].body);
2691 hentry[H_LIST_POST].body = NULL;
2694 /* CLAWS - X-Priority */
2695 if (compose->mode == COMPOSE_REEDIT)
2696 if (hentry[H_X_PRIORITY].body != NULL) {
2699 priority = atoi(hentry[H_X_PRIORITY].body);
2700 g_free(hentry[H_X_PRIORITY].body);
2702 hentry[H_X_PRIORITY].body = NULL;
2704 if (priority < PRIORITY_HIGHEST ||
2705 priority > PRIORITY_LOWEST)
2706 priority = PRIORITY_NORMAL;
2708 compose->priority = priority;
2711 if (compose->mode == COMPOSE_REEDIT) {
2712 if (msginfo->inreplyto && *msginfo->inreplyto)
2713 compose->inreplyto = g_strdup(msginfo->inreplyto);
2717 if (msginfo->msgid && *msginfo->msgid)
2718 compose->inreplyto = g_strdup(msginfo->msgid);
2720 if (!compose->references) {
2721 if (msginfo->msgid && *msginfo->msgid) {
2722 if (msginfo->inreplyto && *msginfo->inreplyto)
2723 compose->references =
2724 g_strdup_printf("<%s>\n\t<%s>",
2728 compose->references =
2729 g_strconcat("<", msginfo->msgid, ">",
2731 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2732 compose->references =
2733 g_strconcat("<", msginfo->inreplyto, ">",
2741 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2743 GSList *ref_id_list, *cur;
2747 ref_id_list = references_list_append(NULL, ref);
2748 if (!ref_id_list) return NULL;
2749 if (msgid && *msgid)
2750 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2755 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2756 /* "<" + Message-ID + ">" + CR+LF+TAB */
2757 len += strlen((gchar *)cur->data) + 5;
2759 if (len > MAX_REFERENCES_LEN) {
2760 /* remove second message-ID */
2761 if (ref_id_list && ref_id_list->next &&
2762 ref_id_list->next->next) {
2763 g_free(ref_id_list->next->data);
2764 ref_id_list = g_slist_remove
2765 (ref_id_list, ref_id_list->next->data);
2767 slist_free_strings(ref_id_list);
2768 g_slist_free(ref_id_list);
2775 new_ref = g_string_new("");
2776 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2777 if (new_ref->len > 0)
2778 g_string_append(new_ref, "\n\t");
2779 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2782 slist_free_strings(ref_id_list);
2783 g_slist_free(ref_id_list);
2785 new_ref_str = new_ref->str;
2786 g_string_free(new_ref, FALSE);
2791 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2792 const gchar *fmt, const gchar *qmark,
2793 const gchar *body, gboolean rewrap,
2794 gboolean need_unescape,
2795 const gchar *err_msg)
2797 MsgInfo* dummyinfo = NULL;
2798 gchar *quote_str = NULL;
2800 gboolean prev_autowrap;
2801 const gchar *trimmed_body = body;
2802 gint cursor_pos = -1;
2803 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2804 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2809 SIGNAL_BLOCK(buffer);
2812 dummyinfo = compose_msginfo_new_from_compose(compose);
2813 msginfo = dummyinfo;
2816 if (qmark != NULL) {
2818 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2819 compose->gtkaspell);
2821 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2823 quote_fmt_scan_string(qmark);
2826 buf = quote_fmt_get_buffer();
2828 alertpanel_error(_("Quote mark format error."));
2830 Xstrdup_a(quote_str, buf, goto error)
2833 if (fmt && *fmt != '\0') {
2836 while (*trimmed_body == '\n')
2840 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2841 compose->gtkaspell);
2843 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2845 if (need_unescape) {
2848 /* decode \-escape sequences in the internal representation of the quote format */
2849 tmp = malloc(strlen(fmt)+1);
2850 pref_get_unescaped_pref(tmp, fmt);
2851 quote_fmt_scan_string(tmp);
2855 quote_fmt_scan_string(fmt);
2859 buf = quote_fmt_get_buffer();
2861 gint line = quote_fmt_get_line();
2862 alertpanel_error(err_msg, line);
2868 prev_autowrap = compose->autowrap;
2869 compose->autowrap = FALSE;
2871 mark = gtk_text_buffer_get_insert(buffer);
2872 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2873 if (g_utf8_validate(buf, -1, NULL)) {
2874 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2876 gchar *tmpout = NULL;
2877 tmpout = conv_codeset_strdup
2878 (buf, conv_get_locale_charset_str_no_utf8(),
2880 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2882 tmpout = g_malloc(strlen(buf)*2+1);
2883 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2885 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2889 cursor_pos = quote_fmt_get_cursor_pos();
2890 if (cursor_pos == -1)
2891 cursor_pos = gtk_text_iter_get_offset(&iter);
2892 compose->set_cursor_pos = cursor_pos;
2894 gtk_text_buffer_get_start_iter(buffer, &iter);
2895 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2896 gtk_text_buffer_place_cursor(buffer, &iter);
2898 compose->autowrap = prev_autowrap;
2899 if (compose->autowrap && rewrap)
2900 compose_wrap_all(compose);
2907 SIGNAL_UNBLOCK(buffer);
2909 procmsg_msginfo_free( dummyinfo );
2914 /* if ml_post is of type addr@host and from is of type
2915 * addr-anything@host, return TRUE
2917 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2919 gchar *left_ml = NULL;
2920 gchar *right_ml = NULL;
2921 gchar *left_from = NULL;
2922 gchar *right_from = NULL;
2923 gboolean result = FALSE;
2925 if (!ml_post || !from)
2928 left_ml = g_strdup(ml_post);
2929 if (strstr(left_ml, "@")) {
2930 right_ml = strstr(left_ml, "@")+1;
2931 *(strstr(left_ml, "@")) = '\0';
2934 left_from = g_strdup(from);
2935 if (strstr(left_from, "@")) {
2936 right_from = strstr(left_from, "@")+1;
2937 *(strstr(left_from, "@")) = '\0';
2940 if (left_ml && left_from && right_ml && right_from
2941 && !strncmp(left_from, left_ml, strlen(left_ml))
2942 && !strcmp(right_from, right_ml)) {
2951 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2953 gchar *my_addr1, *my_addr2;
2955 if (!addr1 || !addr2)
2958 Xstrdup_a(my_addr1, addr1, return FALSE);
2959 Xstrdup_a(my_addr2, addr2, return FALSE);
2961 extract_address(my_addr1);
2962 extract_address(my_addr2);
2964 return !strcasecmp(my_addr1, my_addr2);
2967 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2968 gboolean to_all, gboolean to_ml,
2970 gboolean followup_and_reply_to)
2972 GSList *cc_list = NULL;
2975 gchar *replyto = NULL;
2976 GHashTable *to_table;
2978 gboolean reply_to_ml = FALSE;
2979 gboolean default_reply_to = FALSE;
2981 g_return_if_fail(compose->account != NULL);
2982 g_return_if_fail(msginfo != NULL);
2984 reply_to_ml = to_ml && compose->ml_post;
2986 default_reply_to = msginfo->folder &&
2987 msginfo->folder->prefs->enable_default_reply_to;
2989 if (compose->account->protocol != A_NNTP) {
2990 if (reply_to_ml && !default_reply_to) {
2992 gboolean is_subscr = is_subscription(compose->ml_post,
2995 /* normal answer to ml post with a reply-to */
2996 compose_entry_append(compose,
2999 if (compose->replyto
3000 && !same_address(compose->ml_post, compose->replyto))
3001 compose_entry_append(compose,
3005 /* answer to subscription confirmation */
3006 if (compose->replyto)
3007 compose_entry_append(compose,
3010 else if (msginfo->from)
3011 compose_entry_append(compose,
3016 else if (!(to_all || to_sender) && default_reply_to) {
3017 compose_entry_append(compose,
3018 msginfo->folder->prefs->default_reply_to,
3020 compose_entry_mark_default_to(compose,
3021 msginfo->folder->prefs->default_reply_to);
3026 Xstrdup_a(tmp1, msginfo->from, return);
3027 extract_address(tmp1);
3028 if (to_all || to_sender ||
3029 !account_find_from_address(tmp1, FALSE))
3030 compose_entry_append(compose,
3031 (compose->replyto && !to_sender)
3032 ? compose->replyto :
3033 msginfo->from ? msginfo->from : "",
3035 else if (!to_all && !to_sender) {
3036 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3037 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3038 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3039 if (compose->replyto) {
3040 compose_entry_append(compose,
3044 compose_entry_append(compose,
3045 msginfo->from ? msginfo->from : "",
3049 /* replying to own mail, use original recp */
3050 compose_entry_append(compose,
3051 msginfo->to ? msginfo->to : "",
3053 compose_entry_append(compose,
3054 msginfo->cc ? msginfo->cc : "",
3060 if (to_sender || (compose->followup_to &&
3061 !strncmp(compose->followup_to, "poster", 6)))
3062 compose_entry_append
3064 (compose->replyto ? compose->replyto :
3065 msginfo->from ? msginfo->from : ""),
3068 else if (followup_and_reply_to || to_all) {
3069 compose_entry_append
3071 (compose->replyto ? compose->replyto :
3072 msginfo->from ? msginfo->from : ""),
3075 compose_entry_append
3077 compose->followup_to ? compose->followup_to :
3078 compose->newsgroups ? compose->newsgroups : "",
3079 COMPOSE_NEWSGROUPS);
3082 compose_entry_append
3084 compose->followup_to ? compose->followup_to :
3085 compose->newsgroups ? compose->newsgroups : "",
3086 COMPOSE_NEWSGROUPS);
3089 if (msginfo->subject && *msginfo->subject) {
3093 buf = p = g_strdup(msginfo->subject);
3094 p += subject_get_prefix_length(p);
3095 memmove(buf, p, strlen(p) + 1);
3097 buf2 = g_strdup_printf("Re: %s", buf);
3098 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3103 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3105 if (to_ml && compose->ml_post) return;
3106 if (!to_all || compose->account->protocol == A_NNTP) return;
3108 if (compose->replyto) {
3109 Xstrdup_a(replyto, compose->replyto, return);
3110 extract_address(replyto);
3112 if (msginfo->from) {
3113 Xstrdup_a(from, msginfo->from, return);
3114 extract_address(from);
3117 if (replyto && from)
3118 cc_list = address_list_append_with_comments(cc_list, from);
3119 if (to_all && msginfo->folder &&
3120 msginfo->folder->prefs->enable_default_reply_to)
3121 cc_list = address_list_append_with_comments(cc_list,
3122 msginfo->folder->prefs->default_reply_to);
3123 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3124 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3126 to_table = g_hash_table_new(g_str_hash, g_str_equal);
3128 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
3129 if (compose->account) {
3130 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
3131 GINT_TO_POINTER(1));
3133 /* remove address on To: and that of current account */
3134 for (cur = cc_list; cur != NULL; ) {
3135 GSList *next = cur->next;
3138 addr = g_utf8_strdown(cur->data, -1);
3139 extract_address(addr);
3141 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3142 cc_list = g_slist_remove(cc_list, cur->data);
3144 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3148 hash_free_strings(to_table);
3149 g_hash_table_destroy(to_table);
3152 for (cur = cc_list; cur != NULL; cur = cur->next)
3153 compose_entry_append(compose, (gchar *)cur->data,
3155 slist_free_strings(cc_list);
3156 g_slist_free(cc_list);
3161 #define SET_ENTRY(entry, str) \
3164 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \<