2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2016 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/>.
21 #include "claws-features.h"
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #ifndef USE_ALT_ADDRBOOK
64 #include "addressbook.h"
66 #include "addressbook-dbus.h"
67 #include "addressadd.h"
69 #include "folderview.h"
72 #include "stock_pixmap.h"
73 #include "send_message.h"
76 #include "customheader.h"
77 #include "prefs_common.h"
78 #include "prefs_account.h"
82 #include "procheader.h"
84 #include "statusbar.h"
86 #include "quoted-printable.h"
90 #include "gtkshruler.h"
92 #include "alertpanel.h"
93 #include "manage_window.h"
95 #include "folder_item_prefs.h"
96 #include "addr_compl.h"
97 #include "quote_fmt.h"
99 #include "foldersel.h"
102 #include "message_search.h"
103 #include "combobox.h"
107 #include "autofaces.h"
108 #include "spell_entry.h"
122 #define N_ATTACH_COLS (N_COL_COLUMNS)
126 COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED = -1,
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE = 0,
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 #define COMPOSE_DRAFT_TIMEOUT_UNSET -1
186 #define COMPOSE_DRAFT_TIMEOUT_FORBIDDEN -2
188 static GdkColor default_header_bgcolor = {
195 static GdkColor default_header_color = {
202 static GList *compose_list = NULL;
203 static GSList *extra_headers = NULL;
205 static Compose *compose_generic_new (PrefsAccount *account,
209 GList *listAddress );
211 static Compose *compose_create (PrefsAccount *account,
216 static void compose_entry_indicate (Compose *compose,
217 const gchar *address);
218 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
219 ComposeQuoteMode quote_mode,
223 static Compose *compose_forward_multiple (PrefsAccount *account,
224 GSList *msginfo_list);
225 static Compose *compose_reply (MsgInfo *msginfo,
226 ComposeQuoteMode quote_mode,
231 static Compose *compose_reply_mode (ComposeMode mode,
232 GSList *msginfo_list,
234 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
235 static void compose_update_privacy_systems_menu(Compose *compose);
237 static GtkWidget *compose_account_option_menu_create
239 static void compose_set_out_encoding (Compose *compose);
240 static void compose_set_template_menu (Compose *compose);
241 static void compose_destroy (Compose *compose);
243 static MailField compose_entries_set (Compose *compose,
245 ComposeEntryType to_type);
246 static gint compose_parse_header (Compose *compose,
248 static gint compose_parse_manual_headers (Compose *compose,
250 HeaderEntry *entries);
251 static gchar *compose_parse_references (const gchar *ref,
254 static gchar *compose_quote_fmt (Compose *compose,
260 gboolean need_unescape,
261 const gchar *err_msg);
263 static void compose_reply_set_entry (Compose *compose,
269 followup_and_reply_to);
270 static void compose_reedit_set_entry (Compose *compose,
273 static void compose_insert_sig (Compose *compose,
275 static ComposeInsertResult compose_insert_file (Compose *compose,
278 static gboolean compose_attach_append (Compose *compose,
281 const gchar *content_type,
282 const gchar *charset);
283 static void compose_attach_parts (Compose *compose,
286 static gboolean compose_beautify_paragraph (Compose *compose,
287 GtkTextIter *par_iter,
289 static void compose_wrap_all (Compose *compose);
290 static void compose_wrap_all_full (Compose *compose,
293 static void compose_set_title (Compose *compose);
294 static void compose_select_account (Compose *compose,
295 PrefsAccount *account,
298 static PrefsAccount *compose_current_mail_account(void);
299 /* static gint compose_send (Compose *compose); */
300 static gboolean compose_check_for_valid_recipient
302 static gboolean compose_check_entries (Compose *compose,
303 gboolean check_everything);
304 static gint compose_write_to_file (Compose *compose,
307 gboolean attach_parts);
308 static gint compose_write_body_to_file (Compose *compose,
310 static gint compose_remove_reedit_target (Compose *compose,
312 static void compose_remove_draft (Compose *compose);
313 static gint compose_queue_sub (Compose *compose,
317 gboolean perform_checks,
318 gboolean remove_reedit_target);
319 static int compose_add_attachments (Compose *compose,
321 static gchar *compose_get_header (Compose *compose);
322 static gchar *compose_get_manual_headers_info (Compose *compose);
324 static void compose_convert_header (Compose *compose,
329 gboolean addr_field);
331 static void compose_attach_info_free (AttachInfo *ainfo);
332 static void compose_attach_remove_selected (GtkAction *action,
335 static void compose_template_apply (Compose *compose,
338 static void compose_attach_property (GtkAction *action,
340 static void compose_attach_property_create (gboolean *cancelled);
341 static void attach_property_ok (GtkWidget *widget,
342 gboolean *cancelled);
343 static void attach_property_cancel (GtkWidget *widget,
344 gboolean *cancelled);
345 static gint attach_property_delete_event (GtkWidget *widget,
347 gboolean *cancelled);
348 static gboolean attach_property_key_pressed (GtkWidget *widget,
350 gboolean *cancelled);
352 static void compose_exec_ext_editor (Compose *compose);
354 static gint compose_exec_ext_editor_real (const gchar *file,
355 GdkNativeWindow socket_wid);
356 static gboolean compose_ext_editor_kill (Compose *compose);
357 static gboolean compose_input_cb (GIOChannel *source,
358 GIOCondition condition,
360 static void compose_set_ext_editor_sensitive (Compose *compose,
362 static gboolean compose_get_ext_editor_cmd_valid();
363 static gboolean compose_get_ext_editor_uses_socket();
364 static gboolean compose_ext_editor_plug_removed_cb
367 #endif /* G_OS_UNIX */
369 static void compose_undo_state_changed (UndoMain *undostruct,
374 static void compose_create_header_entry (Compose *compose);
375 static void compose_add_header_entry (Compose *compose, const gchar *header,
376 gchar *text, ComposePrefType pref_type);
377 static void compose_remove_header_entries(Compose *compose);
379 static void compose_update_priority_menu_item(Compose * compose);
381 static void compose_spell_menu_changed (void *data);
382 static void compose_dict_changed (void *data);
384 static void compose_add_field_list ( Compose *compose,
385 GList *listAddress );
387 /* callback functions */
389 static void compose_notebook_size_alloc (GtkNotebook *notebook,
390 GtkAllocation *allocation,
392 static gboolean compose_edit_size_alloc (GtkEditable *widget,
393 GtkAllocation *allocation,
394 GtkSHRuler *shruler);
395 static void account_activated (GtkComboBox *optmenu,
397 static void attach_selected (GtkTreeView *tree_view,
398 GtkTreePath *tree_path,
399 GtkTreeViewColumn *column,
401 static gboolean attach_button_pressed (GtkWidget *widget,
402 GdkEventButton *event,
404 static gboolean attach_key_pressed (GtkWidget *widget,
407 static void compose_send_cb (GtkAction *action, gpointer data);
408 static void compose_send_later_cb (GtkAction *action, gpointer data);
410 static void compose_save_cb (GtkAction *action,
413 static void compose_attach_cb (GtkAction *action,
415 static void compose_insert_file_cb (GtkAction *action,
417 static void compose_insert_sig_cb (GtkAction *action,
419 static void compose_replace_sig_cb (GtkAction *action,
422 static void compose_close_cb (GtkAction *action,
424 static void compose_print_cb (GtkAction *action,
427 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
429 static void compose_address_cb (GtkAction *action,
431 static void about_show_cb (GtkAction *action,
433 static void compose_template_activate_cb(GtkWidget *widget,
436 static void compose_ext_editor_cb (GtkAction *action,
439 static gint compose_delete_cb (GtkWidget *widget,
443 static void compose_undo_cb (GtkAction *action,
445 static void compose_redo_cb (GtkAction *action,
447 static void compose_cut_cb (GtkAction *action,
449 static void compose_copy_cb (GtkAction *action,
451 static void compose_paste_cb (GtkAction *action,
453 static void compose_paste_as_quote_cb (GtkAction *action,
455 static void compose_paste_no_wrap_cb (GtkAction *action,
457 static void compose_paste_wrap_cb (GtkAction *action,
459 static void compose_allsel_cb (GtkAction *action,
462 static void compose_advanced_action_cb (GtkAction *action,
465 static void compose_grab_focus_cb (GtkWidget *widget,
468 static void compose_changed_cb (GtkTextBuffer *textbuf,
471 static void compose_wrap_cb (GtkAction *action,
473 static void compose_wrap_all_cb (GtkAction *action,
475 static void compose_find_cb (GtkAction *action,
477 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
479 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
482 static void compose_toggle_ruler_cb (GtkToggleAction *action,
484 static void compose_toggle_sign_cb (GtkToggleAction *action,
486 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
488 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
489 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
490 static void activate_privacy_system (Compose *compose,
491 PrefsAccount *account,
493 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
495 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
497 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
498 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
499 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
501 static void compose_attach_drag_received_cb (GtkWidget *widget,
502 GdkDragContext *drag_context,
505 GtkSelectionData *data,
509 static void compose_insert_drag_received_cb (GtkWidget *widget,
510 GdkDragContext *drag_context,
513 GtkSelectionData *data,
517 static void compose_header_drag_received_cb (GtkWidget *widget,
518 GdkDragContext *drag_context,
521 GtkSelectionData *data,
526 static gboolean compose_drag_drop (GtkWidget *widget,
527 GdkDragContext *drag_context,
529 guint time, gpointer user_data);
530 static gboolean completion_set_focus_to_subject
535 static void text_inserted (GtkTextBuffer *buffer,
540 static Compose *compose_generic_reply(MsgInfo *msginfo,
541 ComposeQuoteMode quote_mode,
545 gboolean followup_and_reply_to,
548 static void compose_headerentry_changed_cb (GtkWidget *entry,
549 ComposeHeaderEntry *headerentry);
550 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
552 ComposeHeaderEntry *headerentry);
553 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
554 ComposeHeaderEntry *headerentry);
556 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
558 static void compose_allow_user_actions (Compose *compose, gboolean allow);
560 static void compose_nothing_cb (GtkAction *action, gpointer data)
566 static void compose_check_all (GtkAction *action, gpointer data);
567 static void compose_highlight_all (GtkAction *action, gpointer data);
568 static void compose_check_backwards (GtkAction *action, gpointer data);
569 static void compose_check_forwards_go (GtkAction *action, gpointer data);
572 static PrefsAccount *compose_find_account (MsgInfo *msginfo);
574 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
577 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
578 FolderItem *folder_item);
580 static void compose_attach_update_label(Compose *compose);
581 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
582 gboolean respect_default_to);
583 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
584 static void from_name_activate_cb(GtkWidget *widget, gpointer data);
586 static GtkActionEntry compose_popup_entries[] =
588 {"Compose", NULL, "Compose", NULL, NULL, NULL },
589 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
590 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
591 {"Compose/---", NULL, "---", NULL, NULL, NULL },
592 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
595 static GtkActionEntry compose_entries[] =
597 {"Menu", NULL, "Menu", NULL, NULL, NULL },
599 {"Message", NULL, N_("_Message"), NULL, NULL, NULL },
600 {"Edit", NULL, N_("_Edit"), NULL, NULL, NULL },
602 {"Spelling", NULL, N_("_Spelling"), NULL, NULL, NULL },
604 {"Options", NULL, N_("_Options"), NULL, NULL, NULL },
605 {"Tools", NULL, N_("_Tools"), NULL, NULL, NULL },
606 {"Help", NULL, N_("_Help"), NULL, NULL, NULL },
608 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
609 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
610 {"Message/---", NULL, "---", NULL, NULL, NULL },
612 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
613 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
614 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
615 {"Message/ReplaceSig", NULL, N_("_Replace signature"), NULL, NULL, G_CALLBACK(compose_replace_sig_cb) },
616 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
617 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
618 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
619 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
620 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
621 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
624 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
625 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
626 {"Edit/---", NULL, "---", NULL, NULL, NULL },
628 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
629 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
630 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
632 {"Edit/SpecialPaste", NULL, N_("_Special paste"), NULL, NULL, NULL },
633 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
634 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
635 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
637 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
639 {"Edit/Advanced", NULL, N_("A_dvanced"), NULL, NULL, NULL },
640 {"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*/
641 {"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*/
642 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
643 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
644 {"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*/
645 {"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*/
646 {"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*/
647 {"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*/
648 {"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*/
649 {"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*/
650 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
651 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
652 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
653 {"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*/
655 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
656 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
658 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
659 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
660 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
661 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
662 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
665 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
666 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
667 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
668 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
670 {"Spelling/---", NULL, "---", NULL, NULL, NULL },
671 {"Spelling/Options", NULL, N_("_Options"), NULL, NULL, NULL },
675 {"Options/ReplyMode", NULL, N_("Reply _mode"), NULL, NULL, NULL },
676 {"Options/---", NULL, "---", NULL, NULL, NULL },
677 {"Options/PrivacySystem", NULL, N_("Privacy _System"), NULL, NULL, NULL },
678 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
680 /* {"Options/---", NULL, "---", NULL, NULL, NULL }, */
681 {"Options/Priority", NULL, N_("_Priority"), NULL, NULL, NULL },
683 {"Options/Encoding", NULL, N_("Character _encoding"), NULL, NULL, NULL },
684 {"Options/Encoding/---", NULL, "---", NULL, NULL, NULL },
685 #define ENC_ACTION(cs_char,c_char,string) \
686 {"Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
688 {"Options/Encoding/Western", NULL, N_("Western European"), NULL, NULL, NULL },
689 {"Options/Encoding/Baltic", NULL, N_("Baltic"), NULL, NULL, NULL },
690 {"Options/Encoding/Hebrew", NULL, N_("Hebrew"), NULL, NULL, NULL },
691 {"Options/Encoding/Arabic", NULL, N_("Arabic"), NULL, NULL, NULL },
692 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic"), NULL, NULL, NULL },
693 {"Options/Encoding/Japanese", NULL, N_("Japanese"), NULL, NULL, NULL },
694 {"Options/Encoding/Chinese", NULL, N_("Chinese"), NULL, NULL, NULL },
695 {"Options/Encoding/Korean", NULL, N_("Korean"), NULL, NULL, NULL },
696 {"Options/Encoding/Thai", NULL, N_("Thai"), NULL, NULL, NULL },
699 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
701 {"Tools/Template", NULL, N_("_Template"), NULL, NULL, NULL },
702 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
703 {"Tools/Actions", NULL, N_("Actio_ns"), NULL, NULL, NULL },
704 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
707 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
710 static GtkToggleActionEntry compose_toggle_entries[] =
712 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb), FALSE }, /* Toggle */
713 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb), FALSE }, /* Toggle */
714 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb), FALSE }, /* Toggle */
715 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb), FALSE }, /* Toggle */
716 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb), FALSE }, /* Toggle */
717 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb), FALSE }, /* Toggle */
718 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb), FALSE }, /* Toggle */
721 static GtkRadioActionEntry compose_radio_rm_entries[] =
723 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
724 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
725 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
726 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
729 static GtkRadioActionEntry compose_radio_prio_entries[] =
731 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
732 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
733 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
734 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
735 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
738 static GtkRadioActionEntry compose_radio_enc_entries[] =
740 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
741 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
742 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
743 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
744 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
745 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
746 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
747 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
748 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
749 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
750 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
751 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
752 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
753 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
754 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
755 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
756 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
757 ENC_ACTION("Cyrillic/"CS_MACCYR, C_MACCYR, "_Mac-Cyrillic"), /* RADIO compose_set_encoding_cb */
758 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
759 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
760 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
761 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
762 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
763 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
764 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
765 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
766 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
767 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
768 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
769 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
770 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
771 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
772 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
775 static GtkTargetEntry compose_mime_types[] =
777 {"text/uri-list", 0, 0},
778 {"UTF8_STRING", 0, 0},
782 static gboolean compose_put_existing_to_front(MsgInfo *info)
784 const GList *compose_list = compose_get_compose_list();
785 const GList *elem = NULL;
788 for (elem = compose_list; elem != NULL && elem->data != NULL;
790 Compose *c = (Compose*)elem->data;
792 if (!c->targetinfo || !c->targetinfo->msgid ||
796 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
797 gtkut_window_popup(c->window);
805 static GdkColor quote_color1 =
806 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
807 static GdkColor quote_color2 =
808 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
809 static GdkColor quote_color3 =
810 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
812 static GdkColor quote_bgcolor1 =
813 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
814 static GdkColor quote_bgcolor2 =
815 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
816 static GdkColor quote_bgcolor3 =
817 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
819 static GdkColor signature_color = {
826 static GdkColor uri_color = {
833 static void compose_create_tags(GtkTextView *text, Compose *compose)
835 GtkTextBuffer *buffer;
836 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
837 #if !GTK_CHECK_VERSION(2, 24, 0)
844 buffer = gtk_text_view_get_buffer(text);
846 if (prefs_common.enable_color) {
847 /* grab the quote colors, converting from an int to a GdkColor */
848 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
850 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
852 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
854 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
856 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
858 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
860 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
862 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
865 signature_color = quote_color1 = quote_color2 = quote_color3 =
866 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
869 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
870 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
871 "foreground-gdk", "e_color1,
872 "paragraph-background-gdk", "e_bgcolor1,
874 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
875 "foreground-gdk", "e_color2,
876 "paragraph-background-gdk", "e_bgcolor2,
878 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
879 "foreground-gdk", "e_color3,
880 "paragraph-background-gdk", "e_bgcolor3,
883 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
884 "foreground-gdk", "e_color1,
886 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
887 "foreground-gdk", "e_color2,
889 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
890 "foreground-gdk", "e_color3,
894 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
895 "foreground-gdk", &signature_color,
898 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
899 "foreground-gdk", &uri_color,
901 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
902 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
904 #if !GTK_CHECK_VERSION(2, 24, 0)
905 color[0] = quote_color1;
906 color[1] = quote_color2;
907 color[2] = quote_color3;
908 color[3] = quote_bgcolor1;
909 color[4] = quote_bgcolor2;
910 color[5] = quote_bgcolor3;
911 color[6] = signature_color;
912 color[7] = uri_color;
914 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
915 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
917 for (i = 0; i < 8; i++) {
918 if (success[i] == FALSE) {
919 g_warning("Compose: color allocation failed.");
920 quote_color1 = quote_color2 = quote_color3 =
921 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
922 signature_color = uri_color = black;
928 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
931 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
934 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
936 return compose_generic_new(account, mailto, item, NULL, NULL);
939 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
941 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
944 #define SCROLL_TO_CURSOR(compose) { \
945 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
946 gtk_text_view_get_buffer( \
947 GTK_TEXT_VIEW(compose->text))); \
948 gtk_text_view_scroll_mark_onscreen( \
949 GTK_TEXT_VIEW(compose->text), \
953 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
956 if (folderidentifier) {
957 #if !GTK_CHECK_VERSION(2, 24, 0)
958 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
960 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
962 prefs_common.compose_save_to_history = add_history(
963 prefs_common.compose_save_to_history, folderidentifier);
964 #if !GTK_CHECK_VERSION(2, 24, 0)
965 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
966 prefs_common.compose_save_to_history);
968 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
969 prefs_common.compose_save_to_history);
973 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
974 if (folderidentifier)
975 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
977 gtk_entry_set_text(GTK_ENTRY(entry), "");
980 static gchar *compose_get_save_to(Compose *compose)
983 gchar *result = NULL;
984 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
985 result = gtk_editable_get_chars(entry, 0, -1);
988 #if !GTK_CHECK_VERSION(2, 24, 0)
989 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
991 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
993 prefs_common.compose_save_to_history = add_history(
994 prefs_common.compose_save_to_history, result);
995 #if !GTK_CHECK_VERSION(2, 24, 0)
996 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
997 prefs_common.compose_save_to_history);
999 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
1000 prefs_common.compose_save_to_history);
1006 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
1007 GList *attach_files, GList *listAddress )
1010 GtkTextView *textview;
1011 GtkTextBuffer *textbuf;
1013 const gchar *subject_format = NULL;
1014 const gchar *body_format = NULL;
1015 gchar *mailto_from = NULL;
1016 PrefsAccount *mailto_account = NULL;
1017 MsgInfo* dummyinfo = NULL;
1018 gint cursor_pos = -1;
1019 MailField mfield = NO_FIELD_PRESENT;
1023 /* check if mailto defines a from */
1024 if (mailto && *mailto != '\0') {
1025 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1026 /* mailto defines a from, check if we can get account prefs from it,
1027 if not, the account prefs will be guessed using other ways, but we'll keep
1030 mailto_account = account_find_from_address(mailto_from, TRUE);
1031 if (mailto_account == NULL) {
1033 Xstrdup_a(tmp_from, mailto_from, return NULL);
1034 extract_address(tmp_from);
1035 mailto_account = account_find_from_address(tmp_from, TRUE);
1039 account = mailto_account;
1042 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1043 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
1044 account = account_find_from_id(item->prefs->default_account);
1046 /* if no account prefs set, fallback to the current one */
1047 if (!account) account = cur_account;
1048 cm_return_val_if_fail(account != NULL, NULL);
1050 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1052 /* override from name if mailto asked for it */
1054 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1055 g_free(mailto_from);
1057 /* override from name according to folder properties */
1058 if (item && item->prefs &&
1059 item->prefs->compose_with_format &&
1060 item->prefs->compose_override_from_format &&
1061 *item->prefs->compose_override_from_format != '\0') {
1066 dummyinfo = compose_msginfo_new_from_compose(compose);
1068 /* decode \-escape sequences in the internal representation of the quote format */
1069 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1070 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1073 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1074 compose->gtkaspell);
1076 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1078 quote_fmt_scan_string(tmp);
1081 buf = quote_fmt_get_buffer();
1083 alertpanel_error(_("New message From format error."));
1085 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1086 quote_fmt_reset_vartable();
1087 quote_fmtlex_destroy();
1092 compose->replyinfo = NULL;
1093 compose->fwdinfo = NULL;
1095 textview = GTK_TEXT_VIEW(compose->text);
1096 textbuf = gtk_text_view_get_buffer(textview);
1097 compose_create_tags(textview, compose);
1099 undo_block(compose->undostruct);
1101 compose_set_dictionaries_from_folder_prefs(compose, item);
1104 if (account->auto_sig)
1105 compose_insert_sig(compose, FALSE);
1106 gtk_text_buffer_get_start_iter(textbuf, &iter);
1107 gtk_text_buffer_place_cursor(textbuf, &iter);
1109 if (account->protocol != A_NNTP) {
1110 if (mailto && *mailto != '\0') {
1111 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1114 compose_set_folder_prefs(compose, item, TRUE);
1116 if (item && item->ret_rcpt) {
1117 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1120 if (mailto && *mailto != '\0') {
1121 if (!strchr(mailto, '@'))
1122 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1124 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1125 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1126 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1127 mfield = TO_FIELD_PRESENT;
1130 * CLAWS: just don't allow return receipt request, even if the user
1131 * may want to send an email. simple but foolproof.
1133 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1135 compose_add_field_list( compose, listAddress );
1137 if (item && item->prefs && item->prefs->compose_with_format) {
1138 subject_format = item->prefs->compose_subject_format;
1139 body_format = item->prefs->compose_body_format;
1140 } else if (account->compose_with_format) {
1141 subject_format = account->compose_subject_format;
1142 body_format = account->compose_body_format;
1143 } else if (prefs_common.compose_with_format) {
1144 subject_format = prefs_common.compose_subject_format;
1145 body_format = prefs_common.compose_body_format;
1148 if (subject_format || body_format) {
1151 && *subject_format != '\0' )
1153 gchar *subject = NULL;
1158 dummyinfo = compose_msginfo_new_from_compose(compose);
1160 /* decode \-escape sequences in the internal representation of the quote format */
1161 tmp = g_malloc(strlen(subject_format)+1);
1162 pref_get_unescaped_pref(tmp, subject_format);
1164 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1166 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1167 compose->gtkaspell);
1169 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1171 quote_fmt_scan_string(tmp);
1174 buf = quote_fmt_get_buffer();
1176 alertpanel_error(_("New message subject format error."));
1178 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1179 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1180 quote_fmt_reset_vartable();
1181 quote_fmtlex_destroy();
1185 mfield = SUBJECT_FIELD_PRESENT;
1189 && *body_format != '\0' )
1192 GtkTextBuffer *buffer;
1193 GtkTextIter start, end;
1197 dummyinfo = compose_msginfo_new_from_compose(compose);
1199 text = GTK_TEXT_VIEW(compose->text);
1200 buffer = gtk_text_view_get_buffer(text);
1201 gtk_text_buffer_get_start_iter(buffer, &start);
1202 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1203 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1205 compose_quote_fmt(compose, dummyinfo,
1207 NULL, tmp, FALSE, TRUE,
1208 _("The body of the \"New message\" template has an error at line %d."));
1209 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1210 quote_fmt_reset_vartable();
1214 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1215 gtkaspell_highlight_all(compose->gtkaspell);
1217 mfield = BODY_FIELD_PRESENT;
1221 procmsg_msginfo_free( &dummyinfo );
1227 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1228 ainfo = (AttachInfo *) curr->data;
1229 compose_attach_append(compose, ainfo->file, ainfo->file,
1230 ainfo->content_type, ainfo->charset);
1234 compose_show_first_last_header(compose, TRUE);
1236 /* Set save folder */
1237 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1238 gchar *folderidentifier;
1240 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1241 folderidentifier = folder_item_get_identifier(item);
1242 compose_set_save_to(compose, folderidentifier);
1243 g_free(folderidentifier);
1246 /* Place cursor according to provided input (mfield) */
1248 case NO_FIELD_PRESENT:
1249 if (compose->header_last)
1250 gtk_widget_grab_focus(compose->header_last->entry);
1252 case TO_FIELD_PRESENT:
1253 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1255 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1258 gtk_widget_grab_focus(compose->subject_entry);
1260 case SUBJECT_FIELD_PRESENT:
1261 textview = GTK_TEXT_VIEW(compose->text);
1264 textbuf = gtk_text_view_get_buffer(textview);
1267 mark = gtk_text_buffer_get_insert(textbuf);
1268 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1269 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1271 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1272 * only defers where it comes to the variable body
1273 * is not null. If no body is present compose->text
1274 * will be null in which case you cannot place the
1275 * cursor inside the component so. An empty component
1276 * is therefore created before placing the cursor
1278 case BODY_FIELD_PRESENT:
1279 cursor_pos = quote_fmt_get_cursor_pos();
1280 if (cursor_pos == -1)
1281 gtk_widget_grab_focus(compose->header_last->entry);
1283 gtk_widget_grab_focus(compose->text);
1287 undo_unblock(compose->undostruct);
1289 if (prefs_common.auto_exteditor)
1290 compose_exec_ext_editor(compose);
1292 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
1294 SCROLL_TO_CURSOR(compose);
1296 compose->modified = FALSE;
1297 compose_set_title(compose);
1299 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1304 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1305 gboolean override_pref, const gchar *system)
1307 const gchar *privacy = NULL;
1309 cm_return_if_fail(compose != NULL);
1310 cm_return_if_fail(account != NULL);
1312 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1315 if (account->default_privacy_system && strlen(account->default_privacy_system))
1316 privacy = account->default_privacy_system;
1320 GSList *privacy_avail = privacy_get_system_ids();
1321 if (privacy_avail && g_slist_length(privacy_avail)) {
1322 privacy = (gchar *)(privacy_avail->data);
1324 g_slist_free_full(privacy_avail, g_free);
1326 if (privacy != NULL) {
1328 g_free(compose->privacy_system);
1329 compose->privacy_system = NULL;
1330 g_free(compose->encdata);
1331 compose->encdata = NULL;
1333 if (compose->privacy_system == NULL)
1334 compose->privacy_system = g_strdup(privacy);
1335 else if (*(compose->privacy_system) == '\0') {
1336 g_free(compose->privacy_system);
1337 g_free(compose->encdata);
1338 compose->encdata = NULL;
1339 compose->privacy_system = g_strdup(privacy);
1341 compose_update_privacy_system_menu_item(compose, FALSE);
1342 compose_use_encryption(compose, TRUE);
1346 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1348 const gchar *privacy = NULL;
1350 if (account->default_privacy_system && strlen(account->default_privacy_system))
1351 privacy = account->default_privacy_system;
1355 GSList *privacy_avail = privacy_get_system_ids();
1356 if (privacy_avail && g_slist_length(privacy_avail)) {
1357 privacy = (gchar *)(privacy_avail->data);
1361 if (privacy != NULL) {
1363 g_free(compose->privacy_system);
1364 compose->privacy_system = NULL;
1365 g_free(compose->encdata);
1366 compose->encdata = NULL;
1368 if (compose->privacy_system == NULL)
1369 compose->privacy_system = g_strdup(privacy);
1370 compose_update_privacy_system_menu_item(compose, FALSE);
1371 compose_use_signing(compose, TRUE);
1375 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1379 Compose *compose = NULL;
1381 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1383 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1384 cm_return_val_if_fail(msginfo != NULL, NULL);
1386 list_len = g_slist_length(msginfo_list);
1390 case COMPOSE_REPLY_TO_ADDRESS:
1391 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1392 FALSE, prefs_common.default_reply_list, FALSE, body);
1394 case COMPOSE_REPLY_WITH_QUOTE:
1395 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1396 FALSE, prefs_common.default_reply_list, FALSE, body);
1398 case COMPOSE_REPLY_WITHOUT_QUOTE:
1399 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1400 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1402 case COMPOSE_REPLY_TO_SENDER:
1403 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1404 FALSE, FALSE, TRUE, body);
1406 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1407 compose = compose_followup_and_reply_to(msginfo,
1408 COMPOSE_QUOTE_CHECK,
1409 FALSE, FALSE, body);
1411 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1412 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1413 FALSE, FALSE, TRUE, body);
1415 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1416 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1417 FALSE, FALSE, TRUE, NULL);
1419 case COMPOSE_REPLY_TO_ALL:
1420 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1421 TRUE, FALSE, FALSE, body);
1423 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1424 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1425 TRUE, FALSE, FALSE, body);
1427 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1428 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1429 TRUE, FALSE, FALSE, NULL);
1431 case COMPOSE_REPLY_TO_LIST:
1432 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1433 FALSE, TRUE, FALSE, body);
1435 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1436 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1437 FALSE, TRUE, FALSE, body);
1439 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1440 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1441 FALSE, TRUE, FALSE, NULL);
1443 case COMPOSE_FORWARD:
1444 if (prefs_common.forward_as_attachment) {
1445 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1448 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1452 case COMPOSE_FORWARD_INLINE:
1453 /* check if we reply to more than one Message */
1454 if (list_len == 1) {
1455 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1458 /* more messages FALL THROUGH */
1459 case COMPOSE_FORWARD_AS_ATTACH:
1460 compose = compose_forward_multiple(NULL, msginfo_list);
1462 case COMPOSE_REDIRECT:
1463 compose = compose_redirect(NULL, msginfo, FALSE);
1466 g_warning("compose_reply_mode(): invalid Compose Mode: %d", mode);
1469 if (compose == NULL) {
1470 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1474 compose->rmode = mode;
1475 switch (compose->rmode) {
1477 case COMPOSE_REPLY_WITH_QUOTE:
1478 case COMPOSE_REPLY_WITHOUT_QUOTE:
1479 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1480 debug_print("reply mode Normal\n");
1481 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1482 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1484 case COMPOSE_REPLY_TO_SENDER:
1485 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1486 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1487 debug_print("reply mode Sender\n");
1488 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1490 case COMPOSE_REPLY_TO_ALL:
1491 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1492 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1493 debug_print("reply mode All\n");
1494 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1496 case COMPOSE_REPLY_TO_LIST:
1497 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1498 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1499 debug_print("reply mode List\n");
1500 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1502 case COMPOSE_REPLY_TO_ADDRESS:
1503 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1511 static Compose *compose_reply(MsgInfo *msginfo,
1512 ComposeQuoteMode quote_mode,
1518 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1519 to_sender, FALSE, body);
1522 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1523 ComposeQuoteMode quote_mode,
1528 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1529 to_sender, TRUE, body);
1532 static void compose_extract_original_charset(Compose *compose)
1534 MsgInfo *info = NULL;
1535 if (compose->replyinfo) {
1536 info = compose->replyinfo;
1537 } else if (compose->fwdinfo) {
1538 info = compose->fwdinfo;
1539 } else if (compose->targetinfo) {
1540 info = compose->targetinfo;
1543 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1544 MimeInfo *partinfo = mimeinfo;
1545 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1546 partinfo = procmime_mimeinfo_next(partinfo);
1548 compose->orig_charset =
1549 g_strdup(procmime_mimeinfo_get_parameter(
1550 partinfo, "charset"));
1552 procmime_mimeinfo_free_all(&mimeinfo);
1556 #define SIGNAL_BLOCK(buffer) { \
1557 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1558 G_CALLBACK(compose_changed_cb), \
1560 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1561 G_CALLBACK(text_inserted), \
1565 #define SIGNAL_UNBLOCK(buffer) { \
1566 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1567 G_CALLBACK(compose_changed_cb), \
1569 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1570 G_CALLBACK(text_inserted), \
1574 static Compose *compose_generic_reply(MsgInfo *msginfo,
1575 ComposeQuoteMode quote_mode,
1576 gboolean to_all, gboolean to_ml,
1578 gboolean followup_and_reply_to,
1582 PrefsAccount *account = NULL;
1583 GtkTextView *textview;
1584 GtkTextBuffer *textbuf;
1585 gboolean quote = FALSE;
1586 const gchar *qmark = NULL;
1587 const gchar *body_fmt = NULL;
1588 gchar *s_system = NULL;
1590 cm_return_val_if_fail(msginfo != NULL, NULL);
1591 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1593 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1595 cm_return_val_if_fail(account != NULL, NULL);
1597 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1599 compose->updating = TRUE;
1601 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1602 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1604 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1605 if (!compose->replyinfo)
1606 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1608 compose_extract_original_charset(compose);
1610 if (msginfo->folder && msginfo->folder->ret_rcpt)
1611 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1613 /* Set save folder */
1614 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1615 gchar *folderidentifier;
1617 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1618 folderidentifier = folder_item_get_identifier(msginfo->folder);
1619 compose_set_save_to(compose, folderidentifier);
1620 g_free(folderidentifier);
1623 if (compose_parse_header(compose, msginfo) < 0) {
1624 compose->updating = FALSE;
1625 compose_destroy(compose);
1629 /* override from name according to folder properties */
1630 if (msginfo->folder && msginfo->folder->prefs &&
1631 msginfo->folder->prefs->reply_with_format &&
1632 msginfo->folder->prefs->reply_override_from_format &&
1633 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1638 /* decode \-escape sequences in the internal representation of the quote format */
1639 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1640 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1643 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1644 compose->gtkaspell);
1646 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1648 quote_fmt_scan_string(tmp);
1651 buf = quote_fmt_get_buffer();
1653 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1655 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1656 quote_fmt_reset_vartable();
1657 quote_fmtlex_destroy();
1662 textview = (GTK_TEXT_VIEW(compose->text));
1663 textbuf = gtk_text_view_get_buffer(textview);
1664 compose_create_tags(textview, compose);
1666 undo_block(compose->undostruct);
1668 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1669 gtkaspell_block_check(compose->gtkaspell);
1672 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1673 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1674 /* use the reply format of folder (if enabled), or the account's one
1675 (if enabled) or fallback to the global reply format, which is always
1676 enabled (even if empty), and use the relevant quotemark */
1678 if (msginfo->folder && msginfo->folder->prefs &&
1679 msginfo->folder->prefs->reply_with_format) {
1680 qmark = msginfo->folder->prefs->reply_quotemark;
1681 body_fmt = msginfo->folder->prefs->reply_body_format;
1683 } else if (account->reply_with_format) {
1684 qmark = account->reply_quotemark;
1685 body_fmt = account->reply_body_format;
1688 qmark = prefs_common.quotemark;
1689 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1690 body_fmt = gettext(prefs_common.quotefmt);
1697 /* empty quotemark is not allowed */
1698 if (qmark == NULL || *qmark == '\0')
1700 compose_quote_fmt(compose, compose->replyinfo,
1701 body_fmt, qmark, body, FALSE, TRUE,
1702 _("The body of the \"Reply\" template has an error at line %d."));
1703 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1704 quote_fmt_reset_vartable();
1707 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1708 compose_force_encryption(compose, account, FALSE, s_system);
1711 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1712 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1713 compose_force_signing(compose, account, s_system);
1717 SIGNAL_BLOCK(textbuf);
1719 if (account->auto_sig)
1720 compose_insert_sig(compose, FALSE);
1722 compose_wrap_all(compose);
1725 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1726 gtkaspell_highlight_all(compose->gtkaspell);
1727 gtkaspell_unblock_check(compose->gtkaspell);
1729 SIGNAL_UNBLOCK(textbuf);
1731 gtk_widget_grab_focus(compose->text);
1733 undo_unblock(compose->undostruct);
1735 if (prefs_common.auto_exteditor)
1736 compose_exec_ext_editor(compose);
1738 compose->modified = FALSE;
1739 compose_set_title(compose);
1741 compose->updating = FALSE;
1742 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1743 SCROLL_TO_CURSOR(compose);
1745 if (compose->deferred_destroy) {
1746 compose_destroy(compose);
1754 #define INSERT_FW_HEADER(var, hdr) \
1755 if (msginfo->var && *msginfo->var) { \
1756 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1757 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1758 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1761 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1762 gboolean as_attach, const gchar *body,
1763 gboolean no_extedit,
1767 GtkTextView *textview;
1768 GtkTextBuffer *textbuf;
1769 gint cursor_pos = -1;
1772 cm_return_val_if_fail(msginfo != NULL, NULL);
1773 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1775 if (!account && !(account = compose_find_account(msginfo)))
1776 account = cur_account;
1778 if (!prefs_common.forward_as_attachment)
1779 mode = COMPOSE_FORWARD_INLINE;
1781 mode = COMPOSE_FORWARD;
1782 compose = compose_create(account, msginfo->folder, mode, batch);
1784 compose->updating = TRUE;
1785 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1786 if (!compose->fwdinfo)
1787 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1789 compose_extract_original_charset(compose);
1791 if (msginfo->subject && *msginfo->subject) {
1792 gchar *buf, *buf2, *p;
1794 buf = p = g_strdup(msginfo->subject);
1795 p += subject_get_prefix_length(p);
1796 memmove(buf, p, strlen(p) + 1);
1798 buf2 = g_strdup_printf("Fw: %s", buf);
1799 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1805 /* override from name according to folder properties */
1806 if (msginfo->folder && msginfo->folder->prefs &&
1807 msginfo->folder->prefs->forward_with_format &&
1808 msginfo->folder->prefs->forward_override_from_format &&
1809 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1813 MsgInfo *full_msginfo = NULL;
1816 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1818 full_msginfo = procmsg_msginfo_copy(msginfo);
1820 /* decode \-escape sequences in the internal representation of the quote format */
1821 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1822 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1825 gtkaspell_block_check(compose->gtkaspell);
1826 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1827 compose->gtkaspell);
1829 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1831 quote_fmt_scan_string(tmp);
1834 buf = quote_fmt_get_buffer();
1836 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1838 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1839 quote_fmt_reset_vartable();
1840 quote_fmtlex_destroy();
1843 procmsg_msginfo_free(&full_msginfo);
1846 textview = GTK_TEXT_VIEW(compose->text);
1847 textbuf = gtk_text_view_get_buffer(textview);
1848 compose_create_tags(textview, compose);
1850 undo_block(compose->undostruct);
1854 msgfile = procmsg_get_message_file(msginfo);
1855 if (!is_file_exist(msgfile))
1856 g_warning("%s: file does not exist", msgfile);
1858 compose_attach_append(compose, msgfile, msgfile,
1859 "message/rfc822", NULL);
1863 const gchar *qmark = NULL;
1864 const gchar *body_fmt = NULL;
1865 MsgInfo *full_msginfo;
1867 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1869 full_msginfo = procmsg_msginfo_copy(msginfo);
1871 /* use the forward format of folder (if enabled), or the account's one
1872 (if enabled) or fallback to the global forward format, which is always
1873 enabled (even if empty), and use the relevant quotemark */
1874 if (msginfo->folder && msginfo->folder->prefs &&
1875 msginfo->folder->prefs->forward_with_format) {
1876 qmark = msginfo->folder->prefs->forward_quotemark;
1877 body_fmt = msginfo->folder->prefs->forward_body_format;
1879 } else if (account->forward_with_format) {
1880 qmark = account->forward_quotemark;
1881 body_fmt = account->forward_body_format;
1884 qmark = prefs_common.fw_quotemark;
1885 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1886 body_fmt = gettext(prefs_common.fw_quotefmt);
1891 /* empty quotemark is not allowed */
1892 if (qmark == NULL || *qmark == '\0')
1895 compose_quote_fmt(compose, full_msginfo,
1896 body_fmt, qmark, body, FALSE, TRUE,
1897 _("The body of the \"Forward\" template has an error at line %d."));
1898 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1899 quote_fmt_reset_vartable();
1900 compose_attach_parts(compose, msginfo);
1902 procmsg_msginfo_free(&full_msginfo);
1905 SIGNAL_BLOCK(textbuf);
1907 if (account->auto_sig)
1908 compose_insert_sig(compose, FALSE);
1910 compose_wrap_all(compose);
1913 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1914 gtkaspell_highlight_all(compose->gtkaspell);
1915 gtkaspell_unblock_check(compose->gtkaspell);
1917 SIGNAL_UNBLOCK(textbuf);
1919 cursor_pos = quote_fmt_get_cursor_pos();
1920 if (cursor_pos == -1)
1921 gtk_widget_grab_focus(compose->header_last->entry);
1923 gtk_widget_grab_focus(compose->text);
1925 if (!no_extedit && prefs_common.auto_exteditor)
1926 compose_exec_ext_editor(compose);
1929 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1930 gchar *folderidentifier;
1932 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1933 folderidentifier = folder_item_get_identifier(msginfo->folder);
1934 compose_set_save_to(compose, folderidentifier);
1935 g_free(folderidentifier);
1938 undo_unblock(compose->undostruct);
1940 compose->modified = FALSE;
1941 compose_set_title(compose);
1943 compose->updating = FALSE;
1944 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1945 SCROLL_TO_CURSOR(compose);
1947 if (compose->deferred_destroy) {
1948 compose_destroy(compose);
1952 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1957 #undef INSERT_FW_HEADER
1959 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1962 GtkTextView *textview;
1963 GtkTextBuffer *textbuf;
1967 gboolean single_mail = TRUE;
1969 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1971 if (g_slist_length(msginfo_list) > 1)
1972 single_mail = FALSE;
1974 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1975 if (((MsgInfo *)msginfo->data)->folder == NULL)
1978 /* guess account from first selected message */
1980 !(account = compose_find_account(msginfo_list->data)))
1981 account = cur_account;
1983 cm_return_val_if_fail(account != NULL, NULL);
1985 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1986 if (msginfo->data) {
1987 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1988 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1992 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1993 g_warning("no msginfo_list");
1997 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1999 compose->updating = TRUE;
2001 /* override from name according to folder properties */
2002 if (msginfo_list->data) {
2003 MsgInfo *msginfo = msginfo_list->data;
2005 if (msginfo->folder && msginfo->folder->prefs &&
2006 msginfo->folder->prefs->forward_with_format &&
2007 msginfo->folder->prefs->forward_override_from_format &&
2008 *msginfo->folder->prefs->forward_override_from_format != '\0') {
2013 /* decode \-escape sequences in the internal representation of the quote format */
2014 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
2015 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
2018 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2019 compose->gtkaspell);
2021 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2023 quote_fmt_scan_string(tmp);
2026 buf = quote_fmt_get_buffer();
2028 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2030 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
2031 quote_fmt_reset_vartable();
2032 quote_fmtlex_destroy();
2038 textview = GTK_TEXT_VIEW(compose->text);
2039 textbuf = gtk_text_view_get_buffer(textview);
2040 compose_create_tags(textview, compose);
2042 undo_block(compose->undostruct);
2043 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
2044 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
2046 if (!is_file_exist(msgfile))
2047 g_warning("%s: file does not exist", msgfile);
2049 compose_attach_append(compose, msgfile, msgfile,
2050 "message/rfc822", NULL);
2055 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2056 if (info->subject && *info->subject) {
2057 gchar *buf, *buf2, *p;
2059 buf = p = g_strdup(info->subject);
2060 p += subject_get_prefix_length(p);
2061 memmove(buf, p, strlen(p) + 1);
2063 buf2 = g_strdup_printf("Fw: %s", buf);
2064 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2070 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2071 _("Fw: multiple emails"));
2074 SIGNAL_BLOCK(textbuf);
2076 if (account->auto_sig)
2077 compose_insert_sig(compose, FALSE);
2079 compose_wrap_all(compose);
2081 SIGNAL_UNBLOCK(textbuf);
2083 gtk_text_buffer_get_start_iter(textbuf, &iter);
2084 gtk_text_buffer_place_cursor(textbuf, &iter);
2086 if (prefs_common.auto_exteditor)
2087 compose_exec_ext_editor(compose);
2089 gtk_widget_grab_focus(compose->header_last->entry);
2090 undo_unblock(compose->undostruct);
2091 compose->modified = FALSE;
2092 compose_set_title(compose);
2094 compose->updating = FALSE;
2095 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2096 SCROLL_TO_CURSOR(compose);
2098 if (compose->deferred_destroy) {
2099 compose_destroy(compose);
2103 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2108 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2110 GtkTextIter start = *iter;
2111 GtkTextIter end_iter;
2112 int start_pos = gtk_text_iter_get_offset(&start);
2114 if (!compose->account->sig_sep)
2117 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2118 start_pos+strlen(compose->account->sig_sep));
2120 /* check sig separator */
2121 str = gtk_text_iter_get_text(&start, &end_iter);
2122 if (!strcmp(str, compose->account->sig_sep)) {
2124 /* check end of line (\n) */
2125 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2126 start_pos+strlen(compose->account->sig_sep));
2127 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2128 start_pos+strlen(compose->account->sig_sep)+1);
2129 tmp = gtk_text_iter_get_text(&start, &end_iter);
2130 if (!strcmp(tmp,"\n")) {
2142 static gboolean compose_update_folder_hook(gpointer source, gpointer data)
2144 FolderUpdateData *hookdata = (FolderUpdateData *)source;
2145 Compose *compose = (Compose *)data;
2146 FolderItem *old_item = NULL;
2147 FolderItem *new_item = NULL;
2148 gchar *old_id, *new_id;
2150 if (!(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
2151 && !(hookdata->update_flags & FOLDER_MOVE_FOLDERITEM))
2154 old_item = hookdata->item;
2155 new_item = hookdata->item2;
2157 old_id = folder_item_get_identifier(old_item);
2158 new_id = new_item ? folder_item_get_identifier(new_item) : g_strdup("NULL");
2160 if (compose->targetinfo && compose->targetinfo->folder == old_item) {
2161 debug_print("updating targetinfo folder: %s -> %s\n", old_id, new_id);
2162 compose->targetinfo->folder = new_item;
2165 if (compose->replyinfo && compose->replyinfo->folder == old_item) {
2166 debug_print("updating replyinfo folder: %s -> %s\n", old_id, new_id);
2167 compose->replyinfo->folder = new_item;
2170 if (compose->fwdinfo && compose->fwdinfo->folder == old_item) {
2171 debug_print("updating fwdinfo folder: %s -> %s\n", old_id, new_id);
2172 compose->fwdinfo->folder = new_item;
2180 static void compose_colorize_signature(Compose *compose)
2182 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2184 GtkTextIter end_iter;
2185 gtk_text_buffer_get_start_iter(buffer, &iter);
2186 while (gtk_text_iter_forward_line(&iter))
2187 if (compose_is_sig_separator(compose, buffer, &iter)) {
2188 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2189 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2193 #define BLOCK_WRAP() { \
2194 prev_autowrap = compose->autowrap; \
2195 buffer = gtk_text_view_get_buffer( \
2196 GTK_TEXT_VIEW(compose->text)); \
2197 compose->autowrap = FALSE; \
2199 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2200 G_CALLBACK(compose_changed_cb), \
2202 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2203 G_CALLBACK(text_inserted), \
2206 #define UNBLOCK_WRAP() { \
2207 compose->autowrap = prev_autowrap; \
2208 if (compose->autowrap) { \
2209 gint old = compose->draft_timeout_tag; \
2210 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; \
2211 compose_wrap_all(compose); \
2212 compose->draft_timeout_tag = old; \
2215 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2216 G_CALLBACK(compose_changed_cb), \
2218 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2219 G_CALLBACK(text_inserted), \
2223 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2225 Compose *compose = NULL;
2226 PrefsAccount *account = NULL;
2227 GtkTextView *textview;
2228 GtkTextBuffer *textbuf;
2232 gboolean use_signing = FALSE;
2233 gboolean use_encryption = FALSE;
2234 gchar *privacy_system = NULL;
2235 int priority = PRIORITY_NORMAL;
2236 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2237 gboolean autowrap = prefs_common.autowrap;
2238 gboolean autoindent = prefs_common.auto_indent;
2239 HeaderEntry *manual_headers = NULL;
2241 cm_return_val_if_fail(msginfo != NULL, NULL);
2242 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2244 if (compose_put_existing_to_front(msginfo)) {
2248 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2249 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2250 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2251 gchar *queueheader_buf = NULL;
2254 /* Select Account from queue headers */
2255 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2256 "X-Claws-Account-Id:")) {
2257 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2258 account = account_find_from_id(id);
2259 g_free(queueheader_buf);
2261 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2262 "X-Sylpheed-Account-Id:")) {
2263 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2264 account = account_find_from_id(id);
2265 g_free(queueheader_buf);
2267 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2269 id = atoi(&queueheader_buf[strlen("NAID:")]);
2270 account = account_find_from_id(id);
2271 g_free(queueheader_buf);
2273 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2275 id = atoi(&queueheader_buf[strlen("MAID:")]);
2276 account = account_find_from_id(id);
2277 g_free(queueheader_buf);
2279 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2281 account = account_find_from_address(queueheader_buf, FALSE);
2282 g_free(queueheader_buf);
2284 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2286 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2287 use_signing = param;
2288 g_free(queueheader_buf);
2290 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2291 "X-Sylpheed-Sign:")) {
2292 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2293 use_signing = param;
2294 g_free(queueheader_buf);
2296 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2297 "X-Claws-Encrypt:")) {
2298 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2299 use_encryption = param;
2300 g_free(queueheader_buf);
2302 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2303 "X-Sylpheed-Encrypt:")) {
2304 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2305 use_encryption = param;
2306 g_free(queueheader_buf);
2308 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2309 "X-Claws-Auto-Wrapping:")) {
2310 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2312 g_free(queueheader_buf);
2314 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2315 "X-Claws-Auto-Indent:")) {
2316 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2318 g_free(queueheader_buf);
2320 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2321 "X-Claws-Privacy-System:")) {
2322 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2323 g_free(queueheader_buf);
2325 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2326 "X-Sylpheed-Privacy-System:")) {
2327 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2328 g_free(queueheader_buf);
2330 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2332 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2334 g_free(queueheader_buf);
2336 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2338 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2339 if (tokens && tokens[0] && tokens[1] && tokens[2]) {
2340 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2341 if (orig_item != NULL) {
2342 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2346 g_free(queueheader_buf);
2348 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2350 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2351 if (tokens && tokens[0] && tokens[1] && tokens[2]) {
2352 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2353 if (orig_item != NULL) {
2354 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2358 g_free(queueheader_buf);
2360 /* Get manual headers */
2361 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2362 "X-Claws-Manual-Headers:")) {
2363 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2364 if (listmh && *listmh != '\0') {
2365 debug_print("Got manual headers: %s\n", listmh);
2366 manual_headers = procheader_entries_from_str(listmh);
2369 g_free(queueheader_buf);
2372 account = msginfo->folder->folder->account;
2375 if (!account && prefs_common.reedit_account_autosel) {
2377 if (!procheader_get_header_from_msginfo(msginfo, &from, "FROM:")) {
2378 extract_address(from);
2379 account = account_find_from_address(from, FALSE);
2384 account = cur_account;
2386 cm_return_val_if_fail(account != NULL, NULL);
2388 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2390 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2391 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2392 compose->autowrap = autowrap;
2393 compose->replyinfo = replyinfo;
2394 compose->fwdinfo = fwdinfo;
2396 compose->updating = TRUE;
2397 compose->priority = priority;
2399 if (privacy_system != NULL) {
2400 compose->privacy_system = privacy_system;
2401 compose_use_signing(compose, use_signing);
2402 compose_use_encryption(compose, use_encryption);
2403 compose_update_privacy_system_menu_item(compose, FALSE);
2405 activate_privacy_system(compose, account, FALSE);
2408 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2410 compose_extract_original_charset(compose);
2412 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2413 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2414 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2415 gchar *queueheader_buf = NULL;
2417 /* Set message save folder */
2418 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, "SCF:")) {
2419 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2420 compose_set_save_to(compose, &queueheader_buf[4]);
2421 g_free(queueheader_buf);
2423 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, "RRCPT:")) {
2424 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2426 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2428 g_free(queueheader_buf);
2432 if (compose_parse_header(compose, msginfo) < 0) {
2433 compose->updating = FALSE;
2434 compose_destroy(compose);
2437 compose_reedit_set_entry(compose, msginfo);
2439 textview = GTK_TEXT_VIEW(compose->text);
2440 textbuf = gtk_text_view_get_buffer(textview);
2441 compose_create_tags(textview, compose);
2443 mark = gtk_text_buffer_get_insert(textbuf);
2444 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2446 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2447 G_CALLBACK(compose_changed_cb),
2450 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2451 fp = procmime_get_first_encrypted_text_content(msginfo);
2453 compose_force_encryption(compose, account, TRUE, NULL);
2456 fp = procmime_get_first_text_content(msginfo);
2459 g_warning("Can't get text part");
2463 gchar buf[BUFFSIZE];
2464 gboolean prev_autowrap;
2465 GtkTextBuffer *buffer;
2467 while (fgets(buf, sizeof(buf), fp) != NULL) {
2469 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2475 compose_attach_parts(compose, msginfo);
2477 compose_colorize_signature(compose);
2479 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2480 G_CALLBACK(compose_changed_cb),
2483 if (manual_headers != NULL) {
2484 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2485 procheader_entries_free(manual_headers);
2486 compose->updating = FALSE;
2487 compose_destroy(compose);
2490 procheader_entries_free(manual_headers);
2493 gtk_widget_grab_focus(compose->text);
2495 if (prefs_common.auto_exteditor) {
2496 compose_exec_ext_editor(compose);
2498 compose->modified = FALSE;
2499 compose_set_title(compose);
2501 compose->updating = FALSE;
2502 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2503 SCROLL_TO_CURSOR(compose);
2505 if (compose->deferred_destroy) {
2506 compose_destroy(compose);
2510 compose->sig_str = account_get_signature_str(compose->account);
2512 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2517 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2524 cm_return_val_if_fail(msginfo != NULL, NULL);
2527 account = account_get_reply_account(msginfo,
2528 prefs_common.reply_account_autosel);
2529 cm_return_val_if_fail(account != NULL, NULL);
2531 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2533 compose->updating = TRUE;
2535 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2536 compose->replyinfo = NULL;
2537 compose->fwdinfo = NULL;
2539 compose_show_first_last_header(compose, TRUE);
2541 gtk_widget_grab_focus(compose->header_last->entry);
2543 filename = procmsg_get_message_file(msginfo);
2545 if (filename == NULL) {
2546 compose->updating = FALSE;
2547 compose_destroy(compose);
2552 compose->redirect_filename = filename;
2554 /* Set save folder */
2555 item = msginfo->folder;
2556 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2557 gchar *folderidentifier;
2559 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2560 folderidentifier = folder_item_get_identifier(item);
2561 compose_set_save_to(compose, folderidentifier);
2562 g_free(folderidentifier);
2565 compose_attach_parts(compose, msginfo);
2567 if (msginfo->subject)
2568 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2570 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2572 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2573 _("The body of the \"Redirect\" template has an error at line %d."));
2574 quote_fmt_reset_vartable();
2575 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2577 compose_colorize_signature(compose);
2580 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2581 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2582 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2584 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2585 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2586 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2587 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2588 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", FALSE);
2589 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2590 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2591 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2592 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2594 if (compose->toolbar->draft_btn)
2595 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2596 if (compose->toolbar->insert_btn)
2597 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2598 if (compose->toolbar->attach_btn)
2599 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2600 if (compose->toolbar->sig_btn)
2601 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2602 if (compose->toolbar->exteditor_btn)
2603 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2604 if (compose->toolbar->linewrap_current_btn)
2605 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2606 if (compose->toolbar->linewrap_all_btn)
2607 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2609 compose->modified = FALSE;
2610 compose_set_title(compose);
2611 compose->updating = FALSE;
2612 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2613 SCROLL_TO_CURSOR(compose);
2615 if (compose->deferred_destroy) {
2616 compose_destroy(compose);
2620 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2625 const GList *compose_get_compose_list(void)
2627 return compose_list;
2630 void compose_entry_append(Compose *compose, const gchar *address,
2631 ComposeEntryType type, ComposePrefType pref_type)
2633 const gchar *header;
2635 gboolean in_quote = FALSE;
2636 if (!address || *address == '\0') return;
2643 header = N_("Bcc:");
2645 case COMPOSE_REPLYTO:
2646 header = N_("Reply-To:");
2648 case COMPOSE_NEWSGROUPS:
2649 header = N_("Newsgroups:");
2651 case COMPOSE_FOLLOWUPTO:
2652 header = N_( "Followup-To:");
2654 case COMPOSE_INREPLYTO:
2655 header = N_( "In-Reply-To:");
2662 header = prefs_common_translated_header_name(header);
2664 cur = begin = (gchar *)address;
2666 /* we separate the line by commas, but not if we're inside a quoted
2668 while (*cur != '\0') {
2670 in_quote = !in_quote;
2671 if (*cur == ',' && !in_quote) {
2672 gchar *tmp = g_strdup(begin);
2674 tmp[cur-begin]='\0';
2677 while (*tmp == ' ' || *tmp == '\t')
2679 compose_add_header_entry(compose, header, tmp, pref_type);
2680 compose_entry_indicate(compose, tmp);
2687 gchar *tmp = g_strdup(begin);
2689 tmp[cur-begin]='\0';
2690 while (*tmp == ' ' || *tmp == '\t')
2692 compose_add_header_entry(compose, header, tmp, pref_type);
2693 compose_entry_indicate(compose, tmp);
2698 static void compose_entry_indicate(Compose *compose, const gchar *mailto)
2703 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2704 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2705 if (gtk_entry_get_text(entry) &&
2706 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2707 gtk_widget_modify_base(
2708 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2709 GTK_STATE_NORMAL, &default_header_bgcolor);
2710 gtk_widget_modify_text(
2711 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2712 GTK_STATE_NORMAL, &default_header_color);
2717 void compose_toolbar_cb(gint action, gpointer data)
2719 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2720 Compose *compose = (Compose*)toolbar_item->parent;
2722 cm_return_if_fail(compose != NULL);
2726 compose_send_cb(NULL, compose);
2729 compose_send_later_cb(NULL, compose);
2732 compose_draft(compose, COMPOSE_QUIT_EDITING);
2735 compose_insert_file_cb(NULL, compose);
2738 compose_attach_cb(NULL, compose);
2741 compose_insert_sig(compose, FALSE);
2744 compose_insert_sig(compose, TRUE);
2747 compose_ext_editor_cb(NULL, compose);
2749 case A_LINEWRAP_CURRENT:
2750 compose_beautify_paragraph(compose, NULL, TRUE);
2752 case A_LINEWRAP_ALL:
2753 compose_wrap_all_full(compose, TRUE);
2756 compose_address_cb(NULL, compose);
2759 case A_CHECK_SPELLING:
2760 compose_check_all(NULL, compose);
2763 case A_PRIVACY_SIGN:
2765 case A_PRIVACY_ENCRYPT:
2772 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2777 gchar *subject = NULL;
2781 gchar **attach = NULL;
2782 gchar *inreplyto = NULL;
2783 MailField mfield = NO_FIELD_PRESENT;
2785 /* get mailto parts but skip from */
2786 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2789 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2790 mfield = TO_FIELD_PRESENT;
2793 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2795 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2797 if (!g_utf8_validate (subject, -1, NULL)) {
2798 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2799 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2802 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2804 mfield = SUBJECT_FIELD_PRESENT;
2807 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2808 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2811 gboolean prev_autowrap = compose->autowrap;
2813 compose->autowrap = FALSE;
2815 mark = gtk_text_buffer_get_insert(buffer);
2816 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2818 if (!g_utf8_validate (body, -1, NULL)) {
2819 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2820 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2823 gtk_text_buffer_insert(buffer, &iter, body, -1);
2825 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2827 compose->autowrap = prev_autowrap;
2828 if (compose->autowrap)
2829 compose_wrap_all(compose);
2830 mfield = BODY_FIELD_PRESENT;
2834 gint i = 0, att = 0;
2835 gchar *warn_files = NULL;
2836 while (attach[i] != NULL) {
2837 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2838 if (utf8_filename) {
2839 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2840 gchar *tmp = g_strdup_printf("%s%s\n",
2841 warn_files?warn_files:"",
2847 g_free(utf8_filename);
2849 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2854 alertpanel_notice(ngettext(
2855 "The following file has been attached: \n%s",
2856 "The following files have been attached: \n%s", att), warn_files);
2861 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2874 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2876 static HeaderEntry hentry[] = {
2877 {"Reply-To:", NULL, TRUE },
2878 {"Cc:", NULL, TRUE },
2879 {"References:", NULL, FALSE },
2880 {"Bcc:", NULL, TRUE },
2881 {"Newsgroups:", NULL, TRUE },
2882 {"Followup-To:", NULL, TRUE },
2883 {"List-Post:", NULL, FALSE },
2884 {"X-Priority:", NULL, FALSE },
2885 {NULL, NULL, FALSE }
2902 cm_return_val_if_fail(msginfo != NULL, -1);
2904 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2905 procheader_get_header_fields(fp, hentry);
2908 if (hentry[H_REPLY_TO].body != NULL) {
2909 if (hentry[H_REPLY_TO].body[0] != '\0') {
2911 conv_unmime_header(hentry[H_REPLY_TO].body,
2914 g_free(hentry[H_REPLY_TO].body);
2915 hentry[H_REPLY_TO].body = NULL;
2917 if (hentry[H_CC].body != NULL) {
2918 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2919 g_free(hentry[H_CC].body);
2920 hentry[H_CC].body = NULL;
2922 if (hentry[H_REFERENCES].body != NULL) {
2923 if (compose->mode == COMPOSE_REEDIT)
2924 compose->references = hentry[H_REFERENCES].body;
2926 compose->references = compose_parse_references
2927 (hentry[H_REFERENCES].body, msginfo->msgid);
2928 g_free(hentry[H_REFERENCES].body);
2930 hentry[H_REFERENCES].body = NULL;
2932 if (hentry[H_BCC].body != NULL) {
2933 if (compose->mode == COMPOSE_REEDIT)
2935 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2936 g_free(hentry[H_BCC].body);
2937 hentry[H_BCC].body = NULL;
2939 if (hentry[H_NEWSGROUPS].body != NULL) {
2940 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2941 hentry[H_NEWSGROUPS].body = NULL;
2943 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2944 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2945 compose->followup_to =
2946 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2949 g_free(hentry[H_FOLLOWUP_TO].body);
2950 hentry[H_FOLLOWUP_TO].body = NULL;
2952 if (hentry[H_LIST_POST].body != NULL) {
2953 gchar *to = NULL, *start = NULL;
2955 extract_address(hentry[H_LIST_POST].body);
2956 if (hentry[H_LIST_POST].body[0] != '\0') {
2957 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2959 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2960 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2963 g_free(compose->ml_post);
2964 compose->ml_post = to;
2967 g_free(hentry[H_LIST_POST].body);
2968 hentry[H_LIST_POST].body = NULL;
2971 /* CLAWS - X-Priority */
2972 if (compose->mode == COMPOSE_REEDIT)
2973 if (hentry[H_X_PRIORITY].body != NULL) {
2976 priority = atoi(hentry[H_X_PRIORITY].body);
2977 g_free(hentry[H_X_PRIORITY].body);
2979 hentry[H_X_PRIORITY].body = NULL;
2981 if (priority < PRIORITY_HIGHEST ||
2982 priority > PRIORITY_LOWEST)
2983 priority = PRIORITY_NORMAL;
2985 compose->priority = priority;
2988 if (compose->mode == COMPOSE_REEDIT) {
2989 if (msginfo->inreplyto && *msginfo->inreplyto)
2990 compose->inreplyto = g_strdup(msginfo->inreplyto);
2992 if (msginfo->msgid && *msginfo->msgid &&
2993 compose->folder != NULL &&
2994 compose->folder->stype == F_DRAFT)
2995 compose->msgid = g_strdup(msginfo->msgid);
2997 if (msginfo->msgid && *msginfo->msgid)
2998 compose->inreplyto = g_strdup(msginfo->msgid);
3000 if (!compose->references) {
3001 if (msginfo->msgid && *msginfo->msgid) {
3002 if (msginfo->inreplyto && *msginfo->inreplyto)
3003 compose->references =
3004 g_strdup_printf("<%s>\n\t<%s>",
3008 compose->references =
3009 g_strconcat("<", msginfo->msgid, ">",
3011 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
3012 compose->references =
3013 g_strconcat("<", msginfo->inreplyto, ">",
3022 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
3027 cm_return_val_if_fail(msginfo != NULL, -1);
3029 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
3030 procheader_get_header_fields(fp, entries);
3034 while (he != NULL && he->name != NULL) {
3036 GtkListStore *model = NULL;
3038 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
3039 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
3040 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
3041 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
3042 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
3049 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
3051 GSList *ref_id_list, *cur;
3055 ref_id_list = references_list_append(NULL, ref);
3056 if (!ref_id_list) return NULL;
3057 if (msgid && *msgid)
3058 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
3063 for (cur = ref_id_list; cur != NULL; cur = cur->next)
3064 /* "<" + Message-ID + ">" + CR+LF+TAB */
3065 len += strlen((gchar *)cur->data) + 5;
3067 if (len > MAX_REFERENCES_LEN) {
3068 /* remove second message-ID */
3069 if (ref_id_list && ref_id_list->next &&
3070 ref_id_list->next->next) {
3071 g_free(ref_id_list->next->data);
3072 ref_id_list = g_slist_remove
3073 (ref_id_list, ref_id_list->next->data);
3075 slist_free_strings_full(ref_id_list);
3082 new_ref = g_string_new("");
3083 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3084 if (new_ref->len > 0)
3085 g_string_append(new_ref, "\n\t");
3086 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3089 slist_free_strings_full(ref_id_list);
3091 new_ref_str = new_ref->str;
3092 g_string_free(new_ref, FALSE);
3097 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3098 const gchar *fmt, const gchar *qmark,
3099 const gchar *body, gboolean rewrap,
3100 gboolean need_unescape,
3101 const gchar *err_msg)
3103 MsgInfo* dummyinfo = NULL;
3104 gchar *quote_str = NULL;
3106 gboolean prev_autowrap;
3107 const gchar *trimmed_body = body;
3108 gint cursor_pos = -1;
3109 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3110 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3115 SIGNAL_BLOCK(buffer);
3118 dummyinfo = compose_msginfo_new_from_compose(compose);
3119 msginfo = dummyinfo;
3122 if (qmark != NULL) {
3124 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3125 compose->gtkaspell);
3127 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3129 quote_fmt_scan_string(qmark);
3132 buf = quote_fmt_get_buffer();
3135 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3137 Xstrdup_a(quote_str, buf, goto error)
3140 if (fmt && *fmt != '\0') {
3143 while (*trimmed_body == '\n')
3147 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3148 compose->gtkaspell);
3150 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3152 if (need_unescape) {
3155 /* decode \-escape sequences