2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2013 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "claws-features.h"
27 #ifndef PANGO_ENABLE_ENGINE
28 # define PANGO_ENABLE_ENGINE
32 #include <glib/gi18n.h>
33 #include <gdk/gdkkeysyms.h>
36 #include <pango/pango-break.h>
41 #include <sys/types.h>
47 # include <sys/wait.h>
51 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
55 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
62 #include "mainwindow.h"
64 #ifndef USE_NEW_ADDRBOOK
65 #include "addressbook.h"
67 #include "addressbook-dbus.h"
68 #include "addressadd.h"
70 #include "folderview.h"
73 #include "stock_pixmap.h"
74 #include "send_message.h"
77 #include "customheader.h"
78 #include "prefs_common.h"
79 #include "prefs_account.h"
83 #include "procheader.h"
85 #include "statusbar.h"
88 #include "quoted-printable.h"
92 #include "gtkshruler.h"
94 #include "alertpanel.h"
95 #include "manage_window.h"
97 #include "folder_item_prefs.h"
98 #include "addr_compl.h"
99 #include "quote_fmt.h"
101 #include "foldersel.h"
104 #include "message_search.h"
105 #include "combobox.h"
109 #include "autofaces.h"
110 #include "spell_entry.h"
123 #define N_ATTACH_COLS (N_COL_COLUMNS)
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
128 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
130 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
132 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
134 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
135 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
136 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
137 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
138 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
139 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
140 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
141 } ComposeCallAdvancedAction;
145 PRIORITY_HIGHEST = 1,
154 COMPOSE_INSERT_SUCCESS,
155 COMPOSE_INSERT_READ_ERROR,
156 COMPOSE_INSERT_INVALID_CHARACTER,
157 COMPOSE_INSERT_NO_FILE
158 } ComposeInsertResult;
162 COMPOSE_WRITE_FOR_SEND,
163 COMPOSE_WRITE_FOR_STORE
168 COMPOSE_QUOTE_FORCED,
175 SUBJECT_FIELD_PRESENT,
180 #define B64_LINE_SIZE 57
181 #define B64_BUFFSIZE 77
183 #define MAX_REFERENCES_LEN 999
185 static GList *compose_list = NULL;
186 static GSList *extra_headers = NULL;
188 static Compose *compose_generic_new (PrefsAccount *account,
192 GList *listAddress );
194 static Compose *compose_create (PrefsAccount *account,
199 static void compose_entry_mark_default_to (Compose *compose,
200 const gchar *address);
201 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
202 ComposeQuoteMode quote_mode,
206 static Compose *compose_forward_multiple (PrefsAccount *account,
207 GSList *msginfo_list);
208 static Compose *compose_reply (MsgInfo *msginfo,
209 ComposeQuoteMode quote_mode,
214 static Compose *compose_reply_mode (ComposeMode mode,
215 GSList *msginfo_list,
217 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
218 static void compose_update_privacy_systems_menu(Compose *compose);
220 static GtkWidget *compose_account_option_menu_create
222 static void compose_set_out_encoding (Compose *compose);
223 static void compose_set_template_menu (Compose *compose);
224 static void compose_destroy (Compose *compose);
226 static MailField compose_entries_set (Compose *compose,
228 ComposeEntryType to_type);
229 static gint compose_parse_header (Compose *compose,
231 static gint compose_parse_manual_headers (Compose *compose,
233 HeaderEntry *entries);
234 static gchar *compose_parse_references (const gchar *ref,
237 static gchar *compose_quote_fmt (Compose *compose,
243 gboolean need_unescape,
244 const gchar *err_msg);
246 static void compose_reply_set_entry (Compose *compose,
252 followup_and_reply_to);
253 static void compose_reedit_set_entry (Compose *compose,
256 static void compose_insert_sig (Compose *compose,
258 static ComposeInsertResult compose_insert_file (Compose *compose,
261 static gboolean compose_attach_append (Compose *compose,
264 const gchar *content_type,
265 const gchar *charset);
266 static void compose_attach_parts (Compose *compose,
269 static gboolean compose_beautify_paragraph (Compose *compose,
270 GtkTextIter *par_iter,
272 static void compose_wrap_all (Compose *compose);
273 static void compose_wrap_all_full (Compose *compose,
276 static void compose_set_title (Compose *compose);
277 static void compose_select_account (Compose *compose,
278 PrefsAccount *account,
281 static PrefsAccount *compose_current_mail_account(void);
282 /* static gint compose_send (Compose *compose); */
283 static gboolean compose_check_for_valid_recipient
285 static gboolean compose_check_entries (Compose *compose,
286 gboolean check_everything);
287 static gint compose_write_to_file (Compose *compose,
290 gboolean attach_parts);
291 static gint compose_write_body_to_file (Compose *compose,
293 static gint compose_remove_reedit_target (Compose *compose,
295 static void compose_remove_draft (Compose *compose);
296 static gint compose_queue_sub (Compose *compose,
300 gboolean check_subject,
301 gboolean remove_reedit_target);
302 static int compose_add_attachments (Compose *compose,
304 static gchar *compose_get_header (Compose *compose);
305 static gchar *compose_get_manual_headers_info (Compose *compose);
307 static void compose_convert_header (Compose *compose,
312 gboolean addr_field);
314 static void compose_attach_info_free (AttachInfo *ainfo);
315 static void compose_attach_remove_selected (GtkAction *action,
318 static void compose_template_apply (Compose *compose,
321 static void compose_attach_property (GtkAction *action,
323 static void compose_attach_property_create (gboolean *cancelled);
324 static void attach_property_ok (GtkWidget *widget,
325 gboolean *cancelled);
326 static void attach_property_cancel (GtkWidget *widget,
327 gboolean *cancelled);
328 static gint attach_property_delete_event (GtkWidget *widget,
330 gboolean *cancelled);
331 static gboolean attach_property_key_pressed (GtkWidget *widget,
333 gboolean *cancelled);
335 static void compose_exec_ext_editor (Compose *compose);
337 static gint compose_exec_ext_editor_real (const gchar *file);
338 static gboolean compose_ext_editor_kill (Compose *compose);
339 static gboolean compose_input_cb (GIOChannel *source,
340 GIOCondition condition,
342 static void compose_set_ext_editor_sensitive (Compose *compose,
344 #endif /* G_OS_UNIX */
346 static void compose_undo_state_changed (UndoMain *undostruct,
351 static void compose_create_header_entry (Compose *compose);
352 static void compose_add_header_entry (Compose *compose, const gchar *header,
353 gchar *text, ComposePrefType pref_type);
354 static void compose_remove_header_entries(Compose *compose);
356 static void compose_update_priority_menu_item(Compose * compose);
358 static void compose_spell_menu_changed (void *data);
359 static void compose_dict_changed (void *data);
361 static void compose_add_field_list ( Compose *compose,
362 GList *listAddress );
364 /* callback functions */
366 static void compose_notebook_size_alloc (GtkNotebook *notebook,
367 GtkAllocation *allocation,
369 static gboolean compose_edit_size_alloc (GtkEditable *widget,
370 GtkAllocation *allocation,
371 GtkSHRuler *shruler);
372 static void account_activated (GtkComboBox *optmenu,
374 static void attach_selected (GtkTreeView *tree_view,
375 GtkTreePath *tree_path,
376 GtkTreeViewColumn *column,
378 static gboolean attach_button_pressed (GtkWidget *widget,
379 GdkEventButton *event,
381 static gboolean attach_key_pressed (GtkWidget *widget,
384 static void compose_send_cb (GtkAction *action, gpointer data);
385 static void compose_send_later_cb (GtkAction *action, gpointer data);
387 static void compose_save_cb (GtkAction *action,
390 static void compose_attach_cb (GtkAction *action,
392 static void compose_insert_file_cb (GtkAction *action,
394 static void compose_insert_sig_cb (GtkAction *action,
396 static void compose_replace_sig_cb (GtkAction *action,
399 static void compose_close_cb (GtkAction *action,
401 static void compose_print_cb (GtkAction *action,
404 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
406 static void compose_address_cb (GtkAction *action,
408 static void about_show_cb (GtkAction *action,
410 static void compose_template_activate_cb(GtkWidget *widget,
413 static void compose_ext_editor_cb (GtkAction *action,
416 static gint compose_delete_cb (GtkWidget *widget,
420 static void compose_undo_cb (GtkAction *action,
422 static void compose_redo_cb (GtkAction *action,
424 static void compose_cut_cb (GtkAction *action,
426 static void compose_copy_cb (GtkAction *action,
428 static void compose_paste_cb (GtkAction *action,
430 static void compose_paste_as_quote_cb (GtkAction *action,
432 static void compose_paste_no_wrap_cb (GtkAction *action,
434 static void compose_paste_wrap_cb (GtkAction *action,
436 static void compose_allsel_cb (GtkAction *action,
439 static void compose_advanced_action_cb (GtkAction *action,
442 static void compose_grab_focus_cb (GtkWidget *widget,
445 static void compose_changed_cb (GtkTextBuffer *textbuf,
448 static void compose_wrap_cb (GtkAction *action,
450 static void compose_wrap_all_cb (GtkAction *action,
452 static void compose_find_cb (GtkAction *action,
454 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
456 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
459 static void compose_toggle_ruler_cb (GtkToggleAction *action,
461 static void compose_toggle_sign_cb (GtkToggleAction *action,
463 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
465 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
466 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
467 static void activate_privacy_system (Compose *compose,
468 PrefsAccount *account,
470 static void compose_use_signing(Compose *compose, gboolean use_signing);
471 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
472 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
474 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
476 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
477 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
478 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
480 static void compose_attach_drag_received_cb (GtkWidget *widget,
481 GdkDragContext *drag_context,
484 GtkSelectionData *data,
488 static void compose_insert_drag_received_cb (GtkWidget *widget,
489 GdkDragContext *drag_context,
492 GtkSelectionData *data,
496 static void compose_header_drag_received_cb (GtkWidget *widget,
497 GdkDragContext *drag_context,
500 GtkSelectionData *data,
505 static gboolean compose_drag_drop (GtkWidget *widget,
506 GdkDragContext *drag_context,
508 guint time, gpointer user_data);
509 static gboolean completion_set_focus_to_subject
514 static void text_inserted (GtkTextBuffer *buffer,
519 static Compose *compose_generic_reply(MsgInfo *msginfo,
520 ComposeQuoteMode quote_mode,
524 gboolean followup_and_reply_to,
527 static void compose_headerentry_changed_cb (GtkWidget *entry,
528 ComposeHeaderEntry *headerentry);
529 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
531 ComposeHeaderEntry *headerentry);
532 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
533 ComposeHeaderEntry *headerentry);
535 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
537 static void compose_allow_user_actions (Compose *compose, gboolean allow);
539 static void compose_nothing_cb (GtkAction *action, gpointer data)
545 static void compose_check_all (GtkAction *action, gpointer data);
546 static void compose_highlight_all (GtkAction *action, gpointer data);
547 static void compose_check_backwards (GtkAction *action, gpointer data);
548 static void compose_check_forwards_go (GtkAction *action, gpointer data);
551 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
553 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
556 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
557 FolderItem *folder_item);
559 static void compose_attach_update_label(Compose *compose);
560 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
561 gboolean respect_default_to);
562 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
564 static GtkActionEntry compose_popup_entries[] =
566 {"Compose", NULL, "Compose" },
567 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
568 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
569 {"Compose/---", NULL, "---", NULL, NULL, NULL },
570 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
573 static GtkActionEntry compose_entries[] =
575 {"Menu", NULL, "Menu" },
577 {"Message", NULL, N_("_Message") },
578 {"Edit", NULL, N_("_Edit") },
580 {"Spelling", NULL, N_("_Spelling") },
582 {"Options", NULL, N_("_Options") },
583 {"Tools", NULL, N_("_Tools") },
584 {"Help", NULL, N_("_Help") },
586 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
587 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
588 {"Message/---", NULL, "---" },
590 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
591 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
592 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
593 {"Message/ReplaceSig", NULL, N_("_Replace signature"), NULL, NULL, G_CALLBACK(compose_replace_sig_cb) },
594 /* {"Message/---", NULL, "---" }, */
595 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
596 /* {"Message/---", NULL, "---" }, */
597 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
598 /* {"Message/---", NULL, "---" }, */
599 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
602 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
603 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
604 {"Edit/---", NULL, "---" },
606 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
607 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
608 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
610 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
611 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
612 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
613 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
615 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
617 {"Edit/Advanced", NULL, N_("A_dvanced") },
618 {"Edit/Advanced/BackChar", NULL, N_("Move a character backward"), "<shift><control>B", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER*/
619 {"Edit/Advanced/ForwChar", NULL, N_("Move a character forward"), "<shift><control>F", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER*/
620 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
621 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
622 {"Edit/Advanced/BegLine", NULL, N_("Move to beginning of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE*/
623 {"Edit/Advanced/EndLine", NULL, N_("Move to end of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE*/
624 {"Edit/Advanced/PrevLine", NULL, N_("Move to previous line"), "<control>P", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE*/
625 {"Edit/Advanced/NextLine", NULL, N_("Move to next line"), "<control>N", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE*/
626 {"Edit/Advanced/DelBackChar", NULL, N_("Delete a character backward"), "<control>H", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER*/
627 {"Edit/Advanced/DelForwChar", NULL, N_("Delete a character forward"), "<control>D", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER*/
628 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
629 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
630 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
631 {"Edit/Advanced/DelEndLine", NULL, N_("Delete to end of line"), "<control>K", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END*/
633 /* {"Edit/---", NULL, "---" }, */
634 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
636 /* {"Edit/---", NULL, "---" }, */
637 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
638 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
639 /* {"Edit/---", NULL, "---" }, */
640 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
643 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
644 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
645 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
646 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
648 {"Spelling/---", NULL, "---" },
649 {"Spelling/Options", NULL, N_("_Options") },
654 {"Options/ReplyMode", NULL, N_("Reply _mode") },
655 {"Options/---", NULL, "---" },
656 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
657 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
659 /* {"Options/---", NULL, "---" }, */
661 {"Options/Priority", NULL, N_("_Priority") },
663 {"Options/Encoding", NULL, N_("Character _encoding") },
664 {"Options/Encoding/---", NULL, "---" },
665 #define ENC_ACTION(cs_char,c_char,string) \
666 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
668 {"Options/Encoding/Western", NULL, N_("Western European") },
669 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
670 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
671 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
672 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
673 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
674 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
675 {"Options/Encoding/Korean", NULL, N_("Korean") },
676 {"Options/Encoding/Thai", NULL, N_("Thai") },
679 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
681 {"Tools/Template", NULL, N_("_Template") },
682 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
683 {"Tools/Actions", NULL, N_("Actio_ns") },
684 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
687 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
690 static GtkToggleActionEntry compose_toggle_entries[] =
692 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
693 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
694 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
695 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
696 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
697 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
698 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
701 static GtkRadioActionEntry compose_radio_rm_entries[] =
703 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
704 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
705 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
706 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
709 static GtkRadioActionEntry compose_radio_prio_entries[] =
711 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
712 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
713 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
714 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
715 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
718 static GtkRadioActionEntry compose_radio_enc_entries[] =
720 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
729 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
730 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
731 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
732 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
733 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
734 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
735 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
736 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
737 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
738 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
739 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
740 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
741 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
742 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
743 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
744 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
745 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
746 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
747 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
748 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
749 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
750 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
751 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
754 static GtkTargetEntry compose_mime_types[] =
756 {"text/uri-list", 0, 0},
757 {"UTF8_STRING", 0, 0},
761 static gboolean compose_put_existing_to_front(MsgInfo *info)
763 GList *compose_list = compose_get_compose_list();
767 for (elem = compose_list; elem != NULL && elem->data != NULL;
769 Compose *c = (Compose*)elem->data;
771 if (!c->targetinfo || !c->targetinfo->msgid ||
775 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
776 gtkut_window_popup(c->window);
784 static GdkColor quote_color1 =
785 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
786 static GdkColor quote_color2 =
787 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
788 static GdkColor quote_color3 =
789 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
791 static GdkColor quote_bgcolor1 =
792 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
793 static GdkColor quote_bgcolor2 =
794 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
795 static GdkColor quote_bgcolor3 =
796 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
798 static GdkColor signature_color = {
805 static GdkColor uri_color = {
812 static void compose_create_tags(GtkTextView *text, Compose *compose)
814 GtkTextBuffer *buffer;
815 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
816 #if !GTK_CHECK_VERSION(2, 24, 0)
823 buffer = gtk_text_view_get_buffer(text);
825 if (prefs_common.enable_color) {
826 /* grab the quote colors, converting from an int to a GdkColor */
827 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
829 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
831 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
833 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
835 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
837 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
839 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
841 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
844 signature_color = quote_color1 = quote_color2 = quote_color3 =
845 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
848 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
849 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
850 "foreground-gdk", "e_color1,
851 "paragraph-background-gdk", "e_bgcolor1,
853 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
854 "foreground-gdk", "e_color2,
855 "paragraph-background-gdk", "e_bgcolor2,
857 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
858 "foreground-gdk", "e_color3,
859 "paragraph-background-gdk", "e_bgcolor3,
862 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
863 "foreground-gdk", "e_color1,
865 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
866 "foreground-gdk", "e_color2,
868 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
869 "foreground-gdk", "e_color3,
873 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
874 "foreground-gdk", &signature_color,
877 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
878 "foreground-gdk", &uri_color,
880 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
881 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
883 #if !GTK_CHECK_VERSION(2, 24, 0)
884 color[0] = quote_color1;
885 color[1] = quote_color2;
886 color[2] = quote_color3;
887 color[3] = quote_bgcolor1;
888 color[4] = quote_bgcolor2;
889 color[5] = quote_bgcolor3;
890 color[6] = signature_color;
891 color[7] = uri_color;
893 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
894 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
896 for (i = 0; i < 8; i++) {
897 if (success[i] == FALSE) {
898 g_warning("Compose: color allocation failed.\n");
899 quote_color1 = quote_color2 = quote_color3 =
900 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
901 signature_color = uri_color = black;
907 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
910 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
913 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
915 return compose_generic_new(account, mailto, item, NULL, NULL);
918 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
920 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
923 #define SCROLL_TO_CURSOR(compose) { \
924 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
925 gtk_text_view_get_buffer( \
926 GTK_TEXT_VIEW(compose->text))); \
927 gtk_text_view_scroll_mark_onscreen( \
928 GTK_TEXT_VIEW(compose->text), \
932 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
935 if (folderidentifier) {
936 #if !GTK_CHECK_VERSION(2, 24, 0)
937 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
939 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
941 prefs_common.compose_save_to_history = add_history(
942 prefs_common.compose_save_to_history, folderidentifier);
943 #if !GTK_CHECK_VERSION(2, 24, 0)
944 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
945 prefs_common.compose_save_to_history);
947 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
948 prefs_common.compose_save_to_history);
952 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
953 if (folderidentifier)
954 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
956 gtk_entry_set_text(GTK_ENTRY(entry), "");
959 static gchar *compose_get_save_to(Compose *compose)
962 gchar *result = NULL;
963 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
964 result = gtk_editable_get_chars(entry, 0, -1);
967 #if !GTK_CHECK_VERSION(2, 24, 0)
968 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
970 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
972 prefs_common.compose_save_to_history = add_history(
973 prefs_common.compose_save_to_history, result);
974 #if !GTK_CHECK_VERSION(2, 24, 0)
975 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
976 prefs_common.compose_save_to_history);
978 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
979 prefs_common.compose_save_to_history);
985 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
986 GList *attach_files, GList *listAddress )
989 GtkTextView *textview;
990 GtkTextBuffer *textbuf;
992 const gchar *subject_format = NULL;
993 const gchar *body_format = NULL;
994 gchar *mailto_from = NULL;
995 PrefsAccount *mailto_account = NULL;
996 MsgInfo* dummyinfo = NULL;
997 gint cursor_pos = -1;
998 MailField mfield = NO_FIELD_PRESENT;
1002 /* check if mailto defines a from */
1003 if (mailto && *mailto != '\0') {
1004 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1005 /* mailto defines a from, check if we can get account prefs from it,
1006 if not, the account prefs will be guessed using other ways, but we'll keep
1009 mailto_account = account_find_from_address(mailto_from, TRUE);
1010 if (mailto_account == NULL) {
1012 Xstrdup_a(tmp_from, mailto_from, return NULL);
1013 extract_address(tmp_from);
1014 mailto_account = account_find_from_address(tmp_from, TRUE);
1018 account = mailto_account;
1021 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1022 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
1023 account = account_find_from_id(item->prefs->default_account);
1025 /* if no account prefs set, fallback to the current one */
1026 if (!account) account = cur_account;
1027 cm_return_val_if_fail(account != NULL, NULL);
1029 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1031 /* override from name if mailto asked for it */
1033 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1034 g_free(mailto_from);
1036 /* override from name according to folder properties */
1037 if (item && item->prefs &&
1038 item->prefs->compose_with_format &&
1039 item->prefs->compose_override_from_format &&
1040 *item->prefs->compose_override_from_format != '\0') {
1045 dummyinfo = compose_msginfo_new_from_compose(compose);
1047 /* decode \-escape sequences in the internal representation of the quote format */
1048 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1049 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1052 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1053 compose->gtkaspell);
1055 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1057 quote_fmt_scan_string(tmp);
1060 buf = quote_fmt_get_buffer();
1062 alertpanel_error(_("New message From format error."));
1064 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1065 quote_fmt_reset_vartable();
1070 compose->replyinfo = NULL;
1071 compose->fwdinfo = NULL;
1073 textview = GTK_TEXT_VIEW(compose->text);
1074 textbuf = gtk_text_view_get_buffer(textview);
1075 compose_create_tags(textview, compose);
1077 undo_block(compose->undostruct);
1079 compose_set_dictionaries_from_folder_prefs(compose, item);
1082 if (account->auto_sig)
1083 compose_insert_sig(compose, FALSE);
1084 gtk_text_buffer_get_start_iter(textbuf, &iter);
1085 gtk_text_buffer_place_cursor(textbuf, &iter);
1087 if (account->protocol != A_NNTP) {
1088 if (mailto && *mailto != '\0') {
1089 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1092 compose_set_folder_prefs(compose, item, TRUE);
1094 if (item && item->ret_rcpt) {
1095 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1098 if (mailto && *mailto != '\0') {
1099 if (!strchr(mailto, '@'))
1100 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1102 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1103 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1104 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1105 mfield = TO_FIELD_PRESENT;
1108 * CLAWS: just don't allow return receipt request, even if the user
1109 * may want to send an email. simple but foolproof.
1111 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1113 compose_add_field_list( compose, listAddress );
1115 if (item && item->prefs && item->prefs->compose_with_format) {
1116 subject_format = item->prefs->compose_subject_format;
1117 body_format = item->prefs->compose_body_format;
1118 } else if (account->compose_with_format) {
1119 subject_format = account->compose_subject_format;
1120 body_format = account->compose_body_format;
1121 } else if (prefs_common.compose_with_format) {
1122 subject_format = prefs_common.compose_subject_format;
1123 body_format = prefs_common.compose_body_format;
1126 if (subject_format || body_format) {
1129 && *subject_format != '\0' )
1131 gchar *subject = NULL;
1136 dummyinfo = compose_msginfo_new_from_compose(compose);
1138 /* decode \-escape sequences in the internal representation of the quote format */
1139 tmp = g_malloc(strlen(subject_format)+1);
1140 pref_get_unescaped_pref(tmp, subject_format);
1142 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1144 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1145 compose->gtkaspell);
1147 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1149 quote_fmt_scan_string(tmp);
1152 buf = quote_fmt_get_buffer();
1154 alertpanel_error(_("New message subject format error."));
1156 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1157 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1158 quote_fmt_reset_vartable();
1162 mfield = SUBJECT_FIELD_PRESENT;
1166 && *body_format != '\0' )
1169 GtkTextBuffer *buffer;
1170 GtkTextIter start, end;
1174 dummyinfo = compose_msginfo_new_from_compose(compose);
1176 text = GTK_TEXT_VIEW(compose->text);
1177 buffer = gtk_text_view_get_buffer(text);
1178 gtk_text_buffer_get_start_iter(buffer, &start);
1179 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1180 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1182 compose_quote_fmt(compose, dummyinfo,
1184 NULL, tmp, FALSE, TRUE,
1185 _("The body of the \"New message\" template has an error at line %d."));
1186 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1187 quote_fmt_reset_vartable();
1191 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1192 gtkaspell_highlight_all(compose->gtkaspell);
1194 mfield = BODY_FIELD_PRESENT;
1198 procmsg_msginfo_free( dummyinfo );
1204 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1205 ainfo = (AttachInfo *) curr->data;
1206 compose_attach_append(compose, ainfo->file, ainfo->name,
1207 ainfo->content_type, ainfo->charset);
1211 compose_show_first_last_header(compose, TRUE);
1213 /* Set save folder */
1214 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1215 gchar *folderidentifier;
1217 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1218 folderidentifier = folder_item_get_identifier(item);
1219 compose_set_save_to(compose, folderidentifier);
1220 g_free(folderidentifier);
1223 /* Place cursor according to provided input (mfield) */
1225 case NO_FIELD_PRESENT:
1226 if (compose->header_last)
1227 gtk_widget_grab_focus(compose->header_last->entry);
1229 case TO_FIELD_PRESENT:
1230 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1232 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1235 gtk_widget_grab_focus(compose->subject_entry);
1237 case SUBJECT_FIELD_PRESENT:
1238 textview = GTK_TEXT_VIEW(compose->text);
1241 textbuf = gtk_text_view_get_buffer(textview);
1244 mark = gtk_text_buffer_get_insert(textbuf);
1245 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1246 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1248 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1249 * only defers where it comes to the variable body
1250 * is not null. If no body is present compose->text
1251 * will be null in which case you cannot place the
1252 * cursor inside the component so. An empty component
1253 * is therefore created before placing the cursor
1255 case BODY_FIELD_PRESENT:
1256 cursor_pos = quote_fmt_get_cursor_pos();
1257 if (cursor_pos == -1)
1258 gtk_widget_grab_focus(compose->header_last->entry);
1260 gtk_widget_grab_focus(compose->text);
1264 undo_unblock(compose->undostruct);
1266 if (prefs_common.auto_exteditor)
1267 compose_exec_ext_editor(compose);
1269 compose->draft_timeout_tag = -1;
1270 SCROLL_TO_CURSOR(compose);
1272 compose->modified = FALSE;
1273 compose_set_title(compose);
1275 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1280 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1281 gboolean override_pref, const gchar *system)
1283 const gchar *privacy = NULL;
1285 cm_return_if_fail(compose != NULL);
1286 cm_return_if_fail(account != NULL);
1288 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1291 if (account->default_privacy_system && strlen(account->default_privacy_system))
1292 privacy = account->default_privacy_system;
1296 GSList *privacy_avail = privacy_get_system_ids();
1297 if (privacy_avail && g_slist_length(privacy_avail)) {
1298 privacy = (gchar *)(privacy_avail->data);
1301 if (privacy != NULL) {
1303 g_free(compose->privacy_system);
1304 compose->privacy_system = NULL;
1306 if (compose->privacy_system == NULL)
1307 compose->privacy_system = g_strdup(privacy);
1308 else if (*(compose->privacy_system) == '\0') {
1309 g_free(compose->privacy_system);
1310 compose->privacy_system = g_strdup(privacy);
1312 compose_update_privacy_system_menu_item(compose, FALSE);
1313 compose_use_encryption(compose, TRUE);
1317 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1319 const gchar *privacy = NULL;
1321 if (account->default_privacy_system && strlen(account->default_privacy_system))
1322 privacy = account->default_privacy_system;
1326 GSList *privacy_avail = privacy_get_system_ids();
1327 if (privacy_avail && g_slist_length(privacy_avail)) {
1328 privacy = (gchar *)(privacy_avail->data);
1332 if (privacy != NULL) {
1334 g_free(compose->privacy_system);
1335 compose->privacy_system = NULL;
1337 if (compose->privacy_system == NULL)
1338 compose->privacy_system = g_strdup(privacy);
1339 compose_update_privacy_system_menu_item(compose, FALSE);
1340 compose_use_signing(compose, TRUE);
1344 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1348 Compose *compose = NULL;
1350 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1352 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1353 cm_return_val_if_fail(msginfo != NULL, NULL);
1355 list_len = g_slist_length(msginfo_list);
1359 case COMPOSE_REPLY_TO_ADDRESS:
1360 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1361 FALSE, prefs_common.default_reply_list, FALSE, body);
1363 case COMPOSE_REPLY_WITH_QUOTE:
1364 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1365 FALSE, prefs_common.default_reply_list, FALSE, body);
1367 case COMPOSE_REPLY_WITHOUT_QUOTE:
1368 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1369 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1371 case COMPOSE_REPLY_TO_SENDER:
1372 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1373 FALSE, FALSE, TRUE, body);
1375 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1376 compose = compose_followup_and_reply_to(msginfo,
1377 COMPOSE_QUOTE_CHECK,
1378 FALSE, FALSE, body);
1380 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1381 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1382 FALSE, FALSE, TRUE, body);
1384 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1385 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1386 FALSE, FALSE, TRUE, NULL);
1388 case COMPOSE_REPLY_TO_ALL:
1389 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1390 TRUE, FALSE, FALSE, body);
1392 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1393 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1394 TRUE, FALSE, FALSE, body);
1396 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1397 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1398 TRUE, FALSE, FALSE, NULL);
1400 case COMPOSE_REPLY_TO_LIST:
1401 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1402 FALSE, TRUE, FALSE, body);
1404 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1405 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1406 FALSE, TRUE, FALSE, body);
1408 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1409 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1410 FALSE, TRUE, FALSE, NULL);
1412 case COMPOSE_FORWARD:
1413 if (prefs_common.forward_as_attachment) {
1414 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1417 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1421 case COMPOSE_FORWARD_INLINE:
1422 /* check if we reply to more than one Message */
1423 if (list_len == 1) {
1424 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1427 /* more messages FALL THROUGH */
1428 case COMPOSE_FORWARD_AS_ATTACH:
1429 compose = compose_forward_multiple(NULL, msginfo_list);
1431 case COMPOSE_REDIRECT:
1432 compose = compose_redirect(NULL, msginfo, FALSE);
1435 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1438 if (compose == NULL) {
1439 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1443 compose->rmode = mode;
1444 switch (compose->rmode) {
1446 case COMPOSE_REPLY_WITH_QUOTE:
1447 case COMPOSE_REPLY_WITHOUT_QUOTE:
1448 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1449 debug_print("reply mode Normal\n");
1450 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1451 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1453 case COMPOSE_REPLY_TO_SENDER:
1454 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1455 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1456 debug_print("reply mode Sender\n");
1457 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1459 case COMPOSE_REPLY_TO_ALL:
1460 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1461 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1462 debug_print("reply mode All\n");
1463 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1465 case COMPOSE_REPLY_TO_LIST:
1466 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1467 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1468 debug_print("reply mode List\n");
1469 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1471 case COMPOSE_REPLY_TO_ADDRESS:
1472 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1480 static Compose *compose_reply(MsgInfo *msginfo,
1481 ComposeQuoteMode quote_mode,
1487 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1488 to_sender, FALSE, body);
1491 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1492 ComposeQuoteMode quote_mode,
1497 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1498 to_sender, TRUE, body);
1501 static void compose_extract_original_charset(Compose *compose)
1503 MsgInfo *info = NULL;
1504 if (compose->replyinfo) {
1505 info = compose->replyinfo;
1506 } else if (compose->fwdinfo) {
1507 info = compose->fwdinfo;
1508 } else if (compose->targetinfo) {
1509 info = compose->targetinfo;
1512 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1513 MimeInfo *partinfo = mimeinfo;
1514 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1515 partinfo = procmime_mimeinfo_next(partinfo);
1517 compose->orig_charset =
1518 g_strdup(procmime_mimeinfo_get_parameter(
1519 partinfo, "charset"));
1521 procmime_mimeinfo_free_all(mimeinfo);
1525 #define SIGNAL_BLOCK(buffer) { \
1526 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1527 G_CALLBACK(compose_changed_cb), \
1529 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1530 G_CALLBACK(text_inserted), \
1534 #define SIGNAL_UNBLOCK(buffer) { \
1535 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1536 G_CALLBACK(compose_changed_cb), \
1538 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1539 G_CALLBACK(text_inserted), \
1543 static Compose *compose_generic_reply(MsgInfo *msginfo,
1544 ComposeQuoteMode quote_mode,
1545 gboolean to_all, gboolean to_ml,
1547 gboolean followup_and_reply_to,
1551 PrefsAccount *account = NULL;
1552 GtkTextView *textview;
1553 GtkTextBuffer *textbuf;
1554 gboolean quote = FALSE;
1555 const gchar *qmark = NULL;
1556 const gchar *body_fmt = NULL;
1557 gchar *s_system = NULL;
1559 cm_return_val_if_fail(msginfo != NULL, NULL);
1560 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1562 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1564 cm_return_val_if_fail(account != NULL, NULL);
1566 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1568 compose->updating = TRUE;
1570 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1571 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1573 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1574 if (!compose->replyinfo)
1575 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1577 compose_extract_original_charset(compose);
1579 if (msginfo->folder && msginfo->folder->ret_rcpt)
1580 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1582 /* Set save folder */
1583 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1584 gchar *folderidentifier;
1586 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1587 folderidentifier = folder_item_get_identifier(msginfo->folder);
1588 compose_set_save_to(compose, folderidentifier);
1589 g_free(folderidentifier);
1592 if (compose_parse_header(compose, msginfo) < 0) {
1593 compose->updating = FALSE;
1594 compose_destroy(compose);
1598 /* override from name according to folder properties */
1599 if (msginfo->folder && msginfo->folder->prefs &&
1600 msginfo->folder->prefs->reply_with_format &&
1601 msginfo->folder->prefs->reply_override_from_format &&
1602 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1607 /* decode \-escape sequences in the internal representation of the quote format */
1608 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1609 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1612 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1613 compose->gtkaspell);
1615 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1617 quote_fmt_scan_string(tmp);
1620 buf = quote_fmt_get_buffer();
1622 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1624 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1625 quote_fmt_reset_vartable();
1630 textview = (GTK_TEXT_VIEW(compose->text));
1631 textbuf = gtk_text_view_get_buffer(textview);
1632 compose_create_tags(textview, compose);
1634 undo_block(compose->undostruct);
1636 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1637 gtkaspell_block_check(compose->gtkaspell);
1640 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1641 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1642 /* use the reply format of folder (if enabled), or the account's one
1643 (if enabled) or fallback to the global reply format, which is always
1644 enabled (even if empty), and use the relevant quotemark */
1646 if (msginfo->folder && msginfo->folder->prefs &&
1647 msginfo->folder->prefs->reply_with_format) {
1648 qmark = msginfo->folder->prefs->reply_quotemark;
1649 body_fmt = msginfo->folder->prefs->reply_body_format;
1651 } else if (account->reply_with_format) {
1652 qmark = account->reply_quotemark;
1653 body_fmt = account->reply_body_format;
1656 qmark = prefs_common.quotemark;
1657 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1658 body_fmt = gettext(prefs_common.quotefmt);
1665 /* empty quotemark is not allowed */
1666 if (qmark == NULL || *qmark == '\0')
1668 compose_quote_fmt(compose, compose->replyinfo,
1669 body_fmt, qmark, body, FALSE, TRUE,
1670 _("The body of the \"Reply\" template has an error at line %d."));
1671 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1672 quote_fmt_reset_vartable();
1675 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1676 compose_force_encryption(compose, account, FALSE, s_system);
1679 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1680 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1681 compose_force_signing(compose, account, s_system);
1685 SIGNAL_BLOCK(textbuf);
1687 if (account->auto_sig)
1688 compose_insert_sig(compose, FALSE);
1690 compose_wrap_all(compose);
1693 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1694 gtkaspell_highlight_all(compose->gtkaspell);
1695 gtkaspell_unblock_check(compose->gtkaspell);
1697 SIGNAL_UNBLOCK(textbuf);
1699 gtk_widget_grab_focus(compose->text);
1701 undo_unblock(compose->undostruct);
1703 if (prefs_common.auto_exteditor)
1704 compose_exec_ext_editor(compose);
1706 compose->modified = FALSE;
1707 compose_set_title(compose);
1709 compose->updating = FALSE;
1710 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1711 SCROLL_TO_CURSOR(compose);
1713 if (compose->deferred_destroy) {
1714 compose_destroy(compose);
1722 #define INSERT_FW_HEADER(var, hdr) \
1723 if (msginfo->var && *msginfo->var) { \
1724 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1725 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1726 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1729 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1730 gboolean as_attach, const gchar *body,
1731 gboolean no_extedit,
1735 GtkTextView *textview;
1736 GtkTextBuffer *textbuf;
1737 gint cursor_pos = -1;
1740 cm_return_val_if_fail(msginfo != NULL, NULL);
1741 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1744 !(account = compose_guess_forward_account_from_msginfo
1746 account = cur_account;
1748 if (!prefs_common.forward_as_attachment)
1749 mode = COMPOSE_FORWARD_INLINE;
1751 mode = COMPOSE_FORWARD;
1752 compose = compose_create(account, msginfo->folder, mode, batch);
1754 compose->updating = TRUE;
1755 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1756 if (!compose->fwdinfo)
1757 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1759 compose_extract_original_charset(compose);
1761 if (msginfo->subject && *msginfo->subject) {
1762 gchar *buf, *buf2, *p;
1764 buf = p = g_strdup(msginfo->subject);
1765 p += subject_get_prefix_length(p);
1766 memmove(buf, p, strlen(p) + 1);
1768 buf2 = g_strdup_printf("Fw: %s", buf);
1769 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1775 /* override from name according to folder properties */
1776 if (msginfo->folder && msginfo->folder->prefs &&
1777 msginfo->folder->prefs->forward_with_format &&
1778 msginfo->folder->prefs->forward_override_from_format &&
1779 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1783 MsgInfo *full_msginfo = NULL;
1786 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1788 full_msginfo = procmsg_msginfo_copy(msginfo);
1790 /* decode \-escape sequences in the internal representation of the quote format */
1791 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1792 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1795 gtkaspell_block_check(compose->gtkaspell);
1796 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1797 compose->gtkaspell);
1799 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1801 quote_fmt_scan_string(tmp);
1804 buf = quote_fmt_get_buffer();
1806 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1808 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1809 quote_fmt_reset_vartable();
1812 procmsg_msginfo_free(full_msginfo);
1815 textview = GTK_TEXT_VIEW(compose->text);
1816 textbuf = gtk_text_view_get_buffer(textview);
1817 compose_create_tags(textview, compose);
1819 undo_block(compose->undostruct);
1823 msgfile = procmsg_get_message_file(msginfo);
1824 if (!is_file_exist(msgfile))
1825 g_warning("%s: file not exist\n", msgfile);
1827 compose_attach_append(compose, msgfile, msgfile,
1828 "message/rfc822", NULL);
1832 const gchar *qmark = NULL;
1833 const gchar *body_fmt = NULL;
1834 MsgInfo *full_msginfo;
1836 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1838 full_msginfo = procmsg_msginfo_copy(msginfo);
1840 /* use the forward format of folder (if enabled), or the account's one
1841 (if enabled) or fallback to the global forward format, which is always
1842 enabled (even if empty), and use the relevant quotemark */
1843 if (msginfo->folder && msginfo->folder->prefs &&
1844 msginfo->folder->prefs->forward_with_format) {
1845 qmark = msginfo->folder->prefs->forward_quotemark;
1846 body_fmt = msginfo->folder->prefs->forward_body_format;
1848 } else if (account->forward_with_format) {
1849 qmark = account->forward_quotemark;
1850 body_fmt = account->forward_body_format;
1853 qmark = prefs_common.fw_quotemark;
1854 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1855 body_fmt = gettext(prefs_common.fw_quotefmt);
1860 /* empty quotemark is not allowed */
1861 if (qmark == NULL || *qmark == '\0')
1864 compose_quote_fmt(compose, full_msginfo,
1865 body_fmt, qmark, body, FALSE, TRUE,
1866 _("The body of the \"Forward\" template has an error at line %d."));
1867 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1868 quote_fmt_reset_vartable();
1869 compose_attach_parts(compose, msginfo);
1871 procmsg_msginfo_free(full_msginfo);
1874 SIGNAL_BLOCK(textbuf);
1876 if (account->auto_sig)
1877 compose_insert_sig(compose, FALSE);
1879 compose_wrap_all(compose);
1882 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1883 gtkaspell_highlight_all(compose->gtkaspell);
1884 gtkaspell_unblock_check(compose->gtkaspell);
1886 SIGNAL_UNBLOCK(textbuf);
1888 cursor_pos = quote_fmt_get_cursor_pos();
1889 if (cursor_pos == -1)
1890 gtk_widget_grab_focus(compose->header_last->entry);
1892 gtk_widget_grab_focus(compose->text);
1894 if (!no_extedit && prefs_common.auto_exteditor)
1895 compose_exec_ext_editor(compose);
1898 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1899 gchar *folderidentifier;
1901 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1902 folderidentifier = folder_item_get_identifier(msginfo->folder);
1903 compose_set_save_to(compose, folderidentifier);
1904 g_free(folderidentifier);
1907 undo_unblock(compose->undostruct);
1909 compose->modified = FALSE;
1910 compose_set_title(compose);
1912 compose->updating = FALSE;
1913 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1914 SCROLL_TO_CURSOR(compose);
1916 if (compose->deferred_destroy) {
1917 compose_destroy(compose);
1921 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1926 #undef INSERT_FW_HEADER
1928 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1931 GtkTextView *textview;
1932 GtkTextBuffer *textbuf;
1936 gboolean single_mail = TRUE;
1938 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1940 if (g_slist_length(msginfo_list) > 1)
1941 single_mail = FALSE;
1943 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1944 if (((MsgInfo *)msginfo->data)->folder == NULL)
1947 /* guess account from first selected message */
1949 !(account = compose_guess_forward_account_from_msginfo
1950 (msginfo_list->data)))
1951 account = cur_account;
1953 cm_return_val_if_fail(account != NULL, NULL);
1955 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1956 if (msginfo->data) {
1957 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1958 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1962 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1963 g_warning("no msginfo_list");
1967 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1969 compose->updating = TRUE;
1971 /* override from name according to folder properties */
1972 if (msginfo_list->data) {
1973 MsgInfo *msginfo = msginfo_list->data;
1975 if (msginfo->folder && msginfo->folder->prefs &&
1976 msginfo->folder->prefs->forward_with_format &&
1977 msginfo->folder->prefs->forward_override_from_format &&
1978 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1983 /* decode \-escape sequences in the internal representation of the quote format */
1984 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1985 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1988 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1989 compose->gtkaspell);
1991 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1993 quote_fmt_scan_string(tmp);
1996 buf = quote_fmt_get_buffer();
1998 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2000 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
2001 quote_fmt_reset_vartable();
2007 textview = GTK_TEXT_VIEW(compose->text);
2008 textbuf = gtk_text_view_get_buffer(textview);
2009 compose_create_tags(textview, compose);
2011 undo_block(compose->undostruct);
2012 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
2013 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
2015 if (!is_file_exist(msgfile))
2016 g_warning("%s: file not exist\n", msgfile);
2018 compose_attach_append(compose, msgfile, msgfile,
2019 "message/rfc822", NULL);
2024 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2025 if (info->subject && *info->subject) {
2026 gchar *buf, *buf2, *p;
2028 buf = p = g_strdup(info->subject);
2029 p += subject_get_prefix_length(p);
2030 memmove(buf, p, strlen(p) + 1);
2032 buf2 = g_strdup_printf("Fw: %s", buf);
2033 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2039 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2040 _("Fw: multiple emails"));
2043 SIGNAL_BLOCK(textbuf);
2045 if (account->auto_sig)
2046 compose_insert_sig(compose, FALSE);
2048 compose_wrap_all(compose);
2050 SIGNAL_UNBLOCK(textbuf);
2052 gtk_text_buffer_get_start_iter(textbuf, &iter);
2053 gtk_text_buffer_place_cursor(textbuf, &iter);
2055 gtk_widget_grab_focus(compose->header_last->entry);
2056 undo_unblock(compose->undostruct);
2057 compose->modified = FALSE;
2058 compose_set_title(compose);
2060 compose->updating = FALSE;
2061 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2062 SCROLL_TO_CURSOR(compose);
2064 if (compose->deferred_destroy) {
2065 compose_destroy(compose);
2069 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2074 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2076 GtkTextIter start = *iter;
2077 GtkTextIter end_iter;
2078 int start_pos = gtk_text_iter_get_offset(&start);
2080 if (!compose->account->sig_sep)
2083 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2084 start_pos+strlen(compose->account->sig_sep));
2086 /* check sig separator */
2087 str = gtk_text_iter_get_text(&start, &end_iter);
2088 if (!strcmp(str, compose->account->sig_sep)) {
2090 /* check end of line (\n) */
2091 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2092 start_pos+strlen(compose->account->sig_sep));
2093 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2094 start_pos+strlen(compose->account->sig_sep)+1);
2095 tmp = gtk_text_iter_get_text(&start, &end_iter);
2096 if (!strcmp(tmp,"\n")) {
2108 static void compose_colorize_signature(Compose *compose)
2110 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2112 GtkTextIter end_iter;
2113 gtk_text_buffer_get_start_iter(buffer, &iter);
2114 while (gtk_text_iter_forward_line(&iter))
2115 if (compose_is_sig_separator(compose, buffer, &iter)) {
2116 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2117 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2121 #define BLOCK_WRAP() { \
2122 prev_autowrap = compose->autowrap; \
2123 buffer = gtk_text_view_get_buffer( \
2124 GTK_TEXT_VIEW(compose->text)); \
2125 compose->autowrap = FALSE; \
2127 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2128 G_CALLBACK(compose_changed_cb), \
2130 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2131 G_CALLBACK(text_inserted), \
2134 #define UNBLOCK_WRAP() { \
2135 compose->autowrap = prev_autowrap; \
2136 if (compose->autowrap) { \
2137 gint old = compose->draft_timeout_tag; \
2138 compose->draft_timeout_tag = -2; \
2139 compose_wrap_all(compose); \
2140 compose->draft_timeout_tag = old; \
2143 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2144 G_CALLBACK(compose_changed_cb), \
2146 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2147 G_CALLBACK(text_inserted), \
2151 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2153 Compose *compose = NULL;
2154 PrefsAccount *account = NULL;
2155 GtkTextView *textview;
2156 GtkTextBuffer *textbuf;
2160 gchar buf[BUFFSIZE];
2161 gboolean use_signing = FALSE;
2162 gboolean use_encryption = FALSE;
2163 gchar *privacy_system = NULL;
2164 int priority = PRIORITY_NORMAL;
2165 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2166 gboolean autowrap = prefs_common.autowrap;
2167 gboolean autoindent = prefs_common.auto_indent;
2168 HeaderEntry *manual_headers = NULL;
2170 cm_return_val_if_fail(msginfo != NULL, NULL);
2171 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2173 if (compose_put_existing_to_front(msginfo)) {
2177 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2178 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2179 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2180 gchar queueheader_buf[BUFFSIZE];
2183 /* Select Account from queue headers */
2184 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2185 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2186 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2187 account = account_find_from_id(id);
2189 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2190 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2191 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2192 account = account_find_from_id(id);
2194 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2195 sizeof(queueheader_buf), "NAID:")) {
2196 id = atoi(&queueheader_buf[strlen("NAID:")]);
2197 account = account_find_from_id(id);
2199 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2200 sizeof(queueheader_buf), "MAID:")) {
2201 id = atoi(&queueheader_buf[strlen("MAID:")]);
2202 account = account_find_from_id(id);
2204 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2205 sizeof(queueheader_buf), "S:")) {
2206 account = account_find_from_address(queueheader_buf, FALSE);
2208 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2209 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2210 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2211 use_signing = param;
2214 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2215 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2216 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2217 use_signing = param;
2220 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2221 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2222 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2223 use_encryption = param;
2225 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2226 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2227 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2228 use_encryption = param;
2230 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2231 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2232 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2235 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2236 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2237 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2240 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2241 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2242 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2244 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2245 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2246 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2248 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2249 sizeof(queueheader_buf), "X-Priority: ")) {
2250 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2253 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2254 sizeof(queueheader_buf), "RMID:")) {
2255 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2256 if (tokens[0] && tokens[1] && tokens[2]) {
2257 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2258 if (orig_item != NULL) {
2259 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2264 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2265 sizeof(queueheader_buf), "FMID:")) {
2266 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2267 if (tokens[0] && tokens[1] && tokens[2]) {
2268 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2269 if (orig_item != NULL) {
2270 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2275 /* Get manual headers */
2276 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2277 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2278 if (*listmh != '\0') {
2279 debug_print("Got manual headers: %s\n", listmh);
2280 manual_headers = procheader_entries_from_str(listmh);
2285 account = msginfo->folder->folder->account;
2288 if (!account && prefs_common.reedit_account_autosel) {
2289 gchar from[BUFFSIZE];
2290 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2291 extract_address(from);
2292 account = account_find_from_address(from, FALSE);
2296 account = cur_account;
2298 cm_return_val_if_fail(account != NULL, NULL);
2300 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2302 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2303 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2304 compose->autowrap = autowrap;
2305 compose->replyinfo = replyinfo;
2306 compose->fwdinfo = fwdinfo;
2308 compose->updating = TRUE;
2309 compose->priority = priority;
2311 if (privacy_system != NULL) {
2312 compose->privacy_system = privacy_system;
2313 compose_use_signing(compose, use_signing);
2314 compose_use_encryption(compose, use_encryption);
2315 compose_update_privacy_system_menu_item(compose, FALSE);
2317 activate_privacy_system(compose, account, FALSE);
2320 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2322 compose_extract_original_charset(compose);
2324 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2325 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2326 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2327 gchar queueheader_buf[BUFFSIZE];
2329 /* Set message save folder */
2330 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2331 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2332 compose_set_save_to(compose, &queueheader_buf[4]);
2334 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2335 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2337 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2342 if (compose_parse_header(compose, msginfo) < 0) {
2343 compose->updating = FALSE;
2344 compose_destroy(compose);
2347 compose_reedit_set_entry(compose, msginfo);
2349 textview = GTK_TEXT_VIEW(compose->text);
2350 textbuf = gtk_text_view_get_buffer(textview);
2351 compose_create_tags(textview, compose);
2353 mark = gtk_text_buffer_get_insert(textbuf);
2354 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2356 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2357 G_CALLBACK(compose_changed_cb),
2360 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2361 fp = procmime_get_first_encrypted_text_content(msginfo);
2363 compose_force_encryption(compose, account, TRUE, NULL);
2366 fp = procmime_get_first_text_content(msginfo);
2369 g_warning("Can't get text part\n");
2373 gboolean prev_autowrap;
2374 GtkTextBuffer *buffer;
2376 while (fgets(buf, sizeof(buf), fp) != NULL) {
2378 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2384 compose_attach_parts(compose, msginfo);
2386 compose_colorize_signature(compose);
2388 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2389 G_CALLBACK(compose_changed_cb),
2392 if (manual_headers != NULL) {
2393 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2394 procheader_entries_free(manual_headers);
2395 compose->updating = FALSE;
2396 compose_destroy(compose);
2399 procheader_entries_free(manual_headers);
2402 gtk_widget_grab_focus(compose->text);
2404 if (prefs_common.auto_exteditor) {
2405 compose_exec_ext_editor(compose);
2407 compose->modified = FALSE;
2408 compose_set_title(compose);
2410 compose->updating = FALSE;
2411 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2412 SCROLL_TO_CURSOR(compose);
2414 if (compose->deferred_destroy) {
2415 compose_destroy(compose);
2419 compose->sig_str = account_get_signature_str(compose->account);
2421 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2426 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2433 cm_return_val_if_fail(msginfo != NULL, NULL);
2436 account = account_get_reply_account(msginfo,
2437 prefs_common.reply_account_autosel);
2438 cm_return_val_if_fail(account != NULL, NULL);
2440 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2442 compose->updating = TRUE;
2444 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2445 compose->replyinfo = NULL;
2446 compose->fwdinfo = NULL;
2448 compose_show_first_last_header(compose, TRUE);
2450 gtk_widget_grab_focus(compose->header_last->entry);
2452 filename = procmsg_get_message_file(msginfo);
2454 if (filename == NULL) {
2455 compose->updating = FALSE;
2456 compose_destroy(compose);
2461 compose->redirect_filename = filename;
2463 /* Set save folder */
2464 item = msginfo->folder;
2465 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2466 gchar *folderidentifier;
2468 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2469 folderidentifier = folder_item_get_identifier(item);
2470 compose_set_save_to(compose, folderidentifier);
2471 g_free(folderidentifier);
2474 compose_attach_parts(compose, msginfo);
2476 if (msginfo->subject)
2477 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2479 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2481 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2482 _("The body of the \"Redirect\" template has an error at line %d."));
2483 quote_fmt_reset_vartable();
2484 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2486 compose_colorize_signature(compose);
2489 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2490 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2491 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2493 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2494 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2495 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2496 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2497 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", FALSE);
2498 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2499 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2500 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2501 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2503 if (compose->toolbar->draft_btn)
2504 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2505 if (compose->toolbar->insert_btn)
2506 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2507 if (compose->toolbar->attach_btn)
2508 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2509 if (compose->toolbar->sig_btn)
2510 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2511 if (compose->toolbar->exteditor_btn)
2512 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2513 if (compose->toolbar->linewrap_current_btn)
2514 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2515 if (compose->toolbar->linewrap_all_btn)
2516 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2518 compose->modified = FALSE;
2519 compose_set_title(compose);
2520 compose->updating = FALSE;
2521 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2522 SCROLL_TO_CURSOR(compose);
2524 if (compose->deferred_destroy) {
2525 compose_destroy(compose);
2529 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2534 GList *compose_get_compose_list(void)
2536 return compose_list;
2539 void compose_entry_append(Compose *compose, const gchar *address,
2540 ComposeEntryType type, ComposePrefType pref_type)
2542 const gchar *header;
2544 gboolean in_quote = FALSE;
2545 if (!address || *address == '\0') return;
2552 header = N_("Bcc:");
2554 case COMPOSE_REPLYTO:
2555 header = N_("Reply-To:");
2557 case COMPOSE_NEWSGROUPS:
2558 header = N_("Newsgroups:");
2560 case COMPOSE_FOLLOWUPTO:
2561 header = N_( "Followup-To:");
2563 case COMPOSE_INREPLYTO:
2564 header = N_( "In-Reply-To:");
2571 header = prefs_common_translated_header_name(header);
2573 cur = begin = (gchar *)address;
2575 /* we separate the line by commas, but not if we're inside a quoted
2577 while (*cur != '\0') {
2579 in_quote = !in_quote;
2580 if (*cur == ',' && !in_quote) {
2581 gchar *tmp = g_strdup(begin);
2583 tmp[cur-begin]='\0';
2586 while (*tmp == ' ' || *tmp == '\t')
2588 compose_add_header_entry(compose, header, tmp, pref_type);
2595 gchar *tmp = g_strdup(begin);
2597 tmp[cur-begin]='\0';
2598 while (*tmp == ' ' || *tmp == '\t')
2600 compose_add_header_entry(compose, header, tmp, pref_type);
2605 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2607 #if !GTK_CHECK_VERSION(3, 0, 0)
2608 static GdkColor yellow;
2609 static GdkColor black;
2610 static gboolean yellow_initialised = FALSE;
2612 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2613 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2618 #if !GTK_CHECK_VERSION(3, 0, 0)
2619 if (!yellow_initialised) {
2620 gdk_color_parse("#f5f6be", &yellow);
2621 gdk_color_parse("#000000", &black);
2622 yellow_initialised = gdk_colormap_alloc_color(
2623 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2624 yellow_initialised &= gdk_colormap_alloc_color(
2625 gdk_colormap_get_system(), &black, FALSE, TRUE);
2629 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2630 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2631 if (gtk_entry_get_text(entry) &&
2632 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2633 #if !GTK_CHECK_VERSION(3, 0, 0)
2634 if (yellow_initialised) {
2636 gtk_widget_modify_base(
2637 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2638 GTK_STATE_NORMAL, &yellow);
2639 gtk_widget_modify_text(
2640 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2641 GTK_STATE_NORMAL, &black);
2642 #if !GTK_CHECK_VERSION(3, 0, 0)
2649 void compose_toolbar_cb(gint action, gpointer data)
2651 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2652 Compose *compose = (Compose*)toolbar_item->parent;
2654 cm_return_if_fail(compose != NULL);
2658 compose_send_cb(NULL, compose);
2661 compose_send_later_cb(NULL, compose);
2664 compose_draft(compose, COMPOSE_QUIT_EDITING);
2667 compose_insert_file_cb(NULL, compose);
2670 compose_attach_cb(NULL, compose);
2673 compose_insert_sig(compose, FALSE);
2676 compose_insert_sig(compose, TRUE);
2679 compose_ext_editor_cb(NULL, compose);
2681 case A_LINEWRAP_CURRENT:
2682 compose_beautify_paragraph(compose, NULL, TRUE);
2684 case A_LINEWRAP_ALL:
2685 compose_wrap_all_full(compose, TRUE);
2688 compose_address_cb(NULL, compose);
2691 case A_CHECK_SPELLING:
2692 compose_check_all(NULL, compose);
2700 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2705 gchar *subject = NULL;
2709 gchar **attach = NULL;
2710 gchar *inreplyto = NULL;
2711 MailField mfield = NO_FIELD_PRESENT;
2713 /* get mailto parts but skip from */
2714 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2717 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2718 mfield = TO_FIELD_PRESENT;
2721 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2723 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2725 if (!g_utf8_validate (subject, -1, NULL)) {
2726 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2727 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2730 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2732 mfield = SUBJECT_FIELD_PRESENT;
2735 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2736 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2739 gboolean prev_autowrap = compose->autowrap;
2741 compose->autowrap = FALSE;
2743 mark = gtk_text_buffer_get_insert(buffer);
2744 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2746 if (!g_utf8_validate (body, -1, NULL)) {
2747 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2748 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2751 gtk_text_buffer_insert(buffer, &iter, body, -1);
2753 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2755 compose->autowrap = prev_autowrap;
2756 if (compose->autowrap)
2757 compose_wrap_all(compose);
2758 mfield = BODY_FIELD_PRESENT;
2762 gint i = 0, att = 0;
2763 gchar *warn_files = NULL;
2764 while (attach[i] != NULL) {
2765 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2766 if (utf8_filename) {
2767 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2768 gchar *tmp = g_strdup_printf("%s%s\n",
2769 warn_files?warn_files:"",
2775 g_free(utf8_filename);
2777 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2782 alertpanel_notice(ngettext(
2783 "The following file has been attached: \n%s",
2784 "The following files have been attached: \n%s", att), warn_files);
2789 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2802 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2804 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2805 {"Cc:", NULL, TRUE},
2806 {"References:", NULL, FALSE},
2807 {"Bcc:", NULL, TRUE},
2808 {"Newsgroups:", NULL, TRUE},
2809 {"Followup-To:", NULL, TRUE},
2810 {"List-Post:", NULL, FALSE},
2811 {"X-Priority:", NULL, FALSE},
2812 {NULL, NULL, FALSE}};
2828 cm_return_val_if_fail(msginfo != NULL, -1);
2830 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2831 procheader_get_header_fields(fp, hentry);
2834 if (hentry[H_REPLY_TO].body != NULL) {
2835 if (hentry[H_REPLY_TO].body[0] != '\0') {
2837 conv_unmime_header(hentry[H_REPLY_TO].body,
2840 g_free(hentry[H_REPLY_TO].body);
2841 hentry[H_REPLY_TO].body = NULL;
2843 if (hentry[H_CC].body != NULL) {
2844 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2845 g_free(hentry[H_CC].body);
2846 hentry[H_CC].body = NULL;
2848 if (hentry[H_REFERENCES].body != NULL) {
2849 if (compose->mode == COMPOSE_REEDIT)
2850 compose->references = hentry[H_REFERENCES].body;
2852 compose->references = compose_parse_references
2853 (hentry[H_REFERENCES].body, msginfo->msgid);
2854 g_free(hentry[H_REFERENCES].body);
2856 hentry[H_REFERENCES].body = NULL;
2858 if (hentry[H_BCC].body != NULL) {
2859 if (compose->mode == COMPOSE_REEDIT)
2861 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2862 g_free(hentry[H_BCC].body);
2863 hentry[H_BCC].body = NULL;
2865 if (hentry[H_NEWSGROUPS].body != NULL) {
2866 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2867 hentry[H_NEWSGROUPS].body = NULL;
2869 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2870 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2871 compose->followup_to =
2872 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2875 g_free(hentry[H_FOLLOWUP_TO].body);
2876 hentry[H_FOLLOWUP_TO].body = NULL;
2878 if (hentry[H_LIST_POST].body != NULL) {
2879 gchar *to = NULL, *start = NULL;
2881 extract_address(hentry[H_LIST_POST].body);
2882 if (hentry[H_LIST_POST].body[0] != '\0') {
2883 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2885 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2886 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2889 g_free(compose->ml_post);
2890 compose->ml_post = to;
2893 g_free(hentry[H_LIST_POST].body);
2894 hentry[H_LIST_POST].body = NULL;
2897 /* CLAWS - X-Priority */
2898 if (compose->mode == COMPOSE_REEDIT)
2899 if (hentry[H_X_PRIORITY].body != NULL) {
2902 priority = atoi(hentry[H_X_PRIORITY].body);
2903 g_free(hentry[H_X_PRIORITY].body);
2905 hentry[H_X_PRIORITY].body = NULL;
2907 if (priority < PRIORITY_HIGHEST ||
2908 priority > PRIORITY_LOWEST)
2909 priority = PRIORITY_NORMAL;
2911 compose->priority = priority;
2914 if (compose->mode == COMPOSE_REEDIT) {
2915 if (msginfo->inreplyto && *msginfo->inreplyto)
2916 compose->inreplyto = g_strdup(msginfo->inreplyto);
2920 if (msginfo->msgid && *msginfo->msgid)
2921 compose->inreplyto = g_strdup(msginfo->msgid);
2923 if (!compose->references) {
2924 if (msginfo->msgid && *msginfo->msgid) {
2925 if (msginfo->inreplyto && *msginfo->inreplyto)
2926 compose->references =
2927 g_strdup_printf("<%s>\n\t<%s>",
2931 compose->references =
2932 g_strconcat("<", msginfo->msgid, ">",
2934 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2935 compose->references =
2936 g_strconcat("<", msginfo->inreplyto, ">",
2944 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2949 cm_return_val_if_fail(msginfo != NULL, -1);
2951 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2952 procheader_get_header_fields(fp, entries);
2956 while (he != NULL && he->name != NULL) {
2958 GtkListStore *model = NULL;
2960 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
2961 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
2962 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
2963 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
2964 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
2971 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2973 GSList *ref_id_list, *cur;
2977 ref_id_list = references_list_append(NULL, ref);
2978 if (!ref_id_list) return NULL;
2979 if (msgid && *msgid)
2980 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2985 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2986 /* "<" + Message-ID + ">" + CR+LF+TAB */
2987 len += strlen((gchar *)cur->data) + 5;
2989 if (len > MAX_REFERENCES_LEN) {
2990 /* remove second message-ID */
2991 if (ref_id_list && ref_id_list->next &&
2992 ref_id_list->next->next) {
2993 g_free(ref_id_list->next->data);
2994 ref_id_list = g_slist_remove
2995 (ref_id_list, ref_id_list->next->data);
2997 slist_free_strings_full(ref_id_list);
3004 new_ref = g_string_new("");
3005 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3006 if (new_ref->len > 0)
3007 g_string_append(new_ref, "\n\t");
3008 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3011 slist_free_strings_full(ref_id_list);
3013 new_ref_str = new_ref->str;
3014 g_string_free(new_ref, FALSE);
3019 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3020 const gchar *fmt, const gchar *qmark,
3021 const gchar *body, gboolean rewrap,
3022 gboolean need_unescape,
3023 const gchar *err_msg)
3025 MsgInfo* dummyinfo = NULL;
3026 gchar *quote_str = NULL;
3028 gboolean prev_autowrap;
3029 const gchar *trimmed_body = body;
3030 gint cursor_pos = -1;
3031 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3032 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3037 SIGNAL_BLOCK(buffer);
3040 dummyinfo = compose_msginfo_new_from_compose(compose);
3041 msginfo = dummyinfo;
3044 if (qmark != NULL) {
3046 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3047 compose->gtkaspell);
3049 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3051 quote_fmt_scan_string(qmark);
3054 buf = quote_fmt_get_buffer();
3056 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3058 Xstrdup_a(quote_str, buf, goto error)
3061 if (fmt && *fmt != '\0') {
3064 while (*trimmed_body == '\n')
3068 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3069 compose->gtkaspell);
3071 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3073 if (need_unescape) {
3076 /* decode \-escape sequences in the internal representation of the quote format */
3077 tmp = g_malloc(strlen(fmt)+1);
3078 pref_get_unescaped_pref(tmp, fmt);
3079 quote_fmt_scan_string(tmp);
3083 quote_fmt_scan_string(fmt);
3087 buf = quote_fmt_get_buffer();
3089 gint line = quote_fmt_get_line();
3090 alertpanel_error(err_msg, line);
3096 prev_autowrap = compose->autowrap;
3097 compose->autowrap = FALSE;
3099 mark = gtk_text_buffer_get_insert(buffer);
3100 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3101 if (g_utf8_validate(buf, -1, NULL)) {
3102 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3104 gchar *tmpout = NULL;
3105 tmpout = conv_codeset_strdup
3106 (buf, conv_get_locale_charset_str_no_utf8(),
3108 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3110 tmpout = g_malloc(strlen(buf)*2+1);
3111 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3113 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3117 cursor_pos = quote_fmt_get_cursor_pos();
3118 if (cursor_pos == -1)
3119 cursor_pos = gtk_text_iter_get_offset(&iter);
3120 compose->set_cursor_pos = cursor_pos;
3122 gtk_text_buffer_get_start_iter(buffer, &iter);
3123 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3124 gtk_text_buffer_place_cursor(buffer, &iter);
3126 compose->autowrap = prev_autowrap;
3127 if (compose->autowrap && rewrap)
3128 compose_wrap_all(compose);
3135 SIGNAL_UNBLOCK(buffer);
3137 procmsg_msginfo_free( dummyinfo );
3142 /* if ml_post is of type addr@host and from is of type
3143 * addr-anything@host, return TRUE
3145 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3147 gchar *left_ml = NULL;
3148 gchar *right_ml = NULL;
3149 gchar *left_from = NULL;
3150 gchar *right_from = NULL;
3151 gboolean result = FALSE;
3153 if (!ml_post || !from)
3156 left_ml = g_strdup(ml_post);
3157 if (strstr(left_ml, "@")) {
3158 right_ml = strstr(left_ml, "@")+1;
3159 *(strstr(left_ml, "@")) = '\0';
3162 left_from = g_strdup(from);
3163 if (strstr(left_from, "@")) {
3164 right_from = strstr(left_from, "@")+1;
3165 *(strstr(left_from, "@")) = '\0';
3168 if (left_ml && left_from && right_ml && right_from
3169 && !strncmp(left_from, left_ml, strlen(left_ml))
3170 && !strcmp(right_from, right_ml)) {
3179 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3180 gboolean respect_default_to)
3184 if (!folder || !folder->prefs)