2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2013 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "claws-features.h"
27 #ifndef PANGO_ENABLE_ENGINE
28 # define PANGO_ENABLE_ENGINE
32 #include <glib/gi18n.h>
33 #include <gdk/gdkkeysyms.h>
36 #include <pango/pango-break.h>
41 #include <sys/types.h>
47 # include <sys/wait.h>
51 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
55 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
62 #include "mainwindow.h"
64 #ifndef USE_NEW_ADDRBOOK
65 #include "addressbook.h"
67 #include "addressbook-dbus.h"
68 #include "addressadd.h"
70 #include "folderview.h"
73 #include "stock_pixmap.h"
74 #include "send_message.h"
77 #include "customheader.h"
78 #include "prefs_common.h"
79 #include "prefs_account.h"
83 #include "procheader.h"
85 #include "statusbar.h"
87 #include "quoted-printable.h"
91 #include "gtkshruler.h"
93 #include "alertpanel.h"
94 #include "manage_window.h"
96 #include "folder_item_prefs.h"
97 #include "addr_compl.h"
98 #include "quote_fmt.h"
100 #include "foldersel.h"
103 #include "message_search.h"
104 #include "combobox.h"
108 #include "autofaces.h"
109 #include "spell_entry.h"
122 #define N_ATTACH_COLS (N_COL_COLUMNS)
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
132 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
134 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
135 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
136 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
137 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
138 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
139 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
140 } ComposeCallAdvancedAction;
144 PRIORITY_HIGHEST = 1,
153 COMPOSE_INSERT_SUCCESS,
154 COMPOSE_INSERT_READ_ERROR,
155 COMPOSE_INSERT_INVALID_CHARACTER,
156 COMPOSE_INSERT_NO_FILE
157 } ComposeInsertResult;
161 COMPOSE_WRITE_FOR_SEND,
162 COMPOSE_WRITE_FOR_STORE
167 COMPOSE_QUOTE_FORCED,
174 SUBJECT_FIELD_PRESENT,
179 #define B64_LINE_SIZE 57
180 #define B64_BUFFSIZE 77
182 #define MAX_REFERENCES_LEN 999
184 #define COMPOSE_DRAFT_TIMEOUT_UNSET -1
185 #define COMPOSE_DRAFT_TIMEOUT_FORBIDDEN -2
187 static GList *compose_list = NULL;
188 static GSList *extra_headers = NULL;
190 static Compose *compose_generic_new (PrefsAccount *account,
194 GList *listAddress );
196 static Compose *compose_create (PrefsAccount *account,
201 static void compose_entry_mark_default_to (Compose *compose,
202 const gchar *address);
203 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
204 ComposeQuoteMode quote_mode,
208 static Compose *compose_forward_multiple (PrefsAccount *account,
209 GSList *msginfo_list);
210 static Compose *compose_reply (MsgInfo *msginfo,
211 ComposeQuoteMode quote_mode,
216 static Compose *compose_reply_mode (ComposeMode mode,
217 GSList *msginfo_list,
219 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
220 static void compose_update_privacy_systems_menu(Compose *compose);
222 static GtkWidget *compose_account_option_menu_create
224 static void compose_set_out_encoding (Compose *compose);
225 static void compose_set_template_menu (Compose *compose);
226 static void compose_destroy (Compose *compose);
228 static MailField compose_entries_set (Compose *compose,
230 ComposeEntryType to_type);
231 static gint compose_parse_header (Compose *compose,
233 static gint compose_parse_manual_headers (Compose *compose,
235 HeaderEntry *entries);
236 static gchar *compose_parse_references (const gchar *ref,
239 static gchar *compose_quote_fmt (Compose *compose,
245 gboolean need_unescape,
246 const gchar *err_msg);
248 static void compose_reply_set_entry (Compose *compose,
254 followup_and_reply_to);
255 static void compose_reedit_set_entry (Compose *compose,
258 static void compose_insert_sig (Compose *compose,
260 static ComposeInsertResult compose_insert_file (Compose *compose,
263 static gboolean compose_attach_append (Compose *compose,
266 const gchar *content_type,
267 const gchar *charset);
268 static void compose_attach_parts (Compose *compose,
271 static gboolean compose_beautify_paragraph (Compose *compose,
272 GtkTextIter *par_iter,
274 static void compose_wrap_all (Compose *compose);
275 static void compose_wrap_all_full (Compose *compose,
278 static void compose_set_title (Compose *compose);
279 static void compose_select_account (Compose *compose,
280 PrefsAccount *account,
283 static PrefsAccount *compose_current_mail_account(void);
284 /* static gint compose_send (Compose *compose); */
285 static gboolean compose_check_for_valid_recipient
287 static gboolean compose_check_entries (Compose *compose,
288 gboolean check_everything);
289 static gint compose_write_to_file (Compose *compose,
292 gboolean attach_parts);
293 static gint compose_write_body_to_file (Compose *compose,
295 static gint compose_remove_reedit_target (Compose *compose,
297 static void compose_remove_draft (Compose *compose);
298 static gint compose_queue_sub (Compose *compose,
302 gboolean check_subject,
303 gboolean remove_reedit_target);
304 static int compose_add_attachments (Compose *compose,
306 static gchar *compose_get_header (Compose *compose);
307 static gchar *compose_get_manual_headers_info (Compose *compose);
309 static void compose_convert_header (Compose *compose,
314 gboolean addr_field);
316 static void compose_attach_info_free (AttachInfo *ainfo);
317 static void compose_attach_remove_selected (GtkAction *action,
320 static void compose_template_apply (Compose *compose,
323 static void compose_attach_property (GtkAction *action,
325 static void compose_attach_property_create (gboolean *cancelled);
326 static void attach_property_ok (GtkWidget *widget,
327 gboolean *cancelled);
328 static void attach_property_cancel (GtkWidget *widget,
329 gboolean *cancelled);
330 static gint attach_property_delete_event (GtkWidget *widget,
332 gboolean *cancelled);
333 static gboolean attach_property_key_pressed (GtkWidget *widget,
335 gboolean *cancelled);
337 static void compose_exec_ext_editor (Compose *compose);
339 static gint compose_exec_ext_editor_real (const gchar *file);
340 static gboolean compose_ext_editor_kill (Compose *compose);
341 static gboolean compose_input_cb (GIOChannel *source,
342 GIOCondition condition,
344 static void compose_set_ext_editor_sensitive (Compose *compose,
346 #endif /* G_OS_UNIX */
348 static void compose_undo_state_changed (UndoMain *undostruct,
353 static void compose_create_header_entry (Compose *compose);
354 static void compose_add_header_entry (Compose *compose, const gchar *header,
355 gchar *text, ComposePrefType pref_type);
356 static void compose_remove_header_entries(Compose *compose);
358 static void compose_update_priority_menu_item(Compose * compose);
360 static void compose_spell_menu_changed (void *data);
361 static void compose_dict_changed (void *data);
363 static void compose_add_field_list ( Compose *compose,
364 GList *listAddress );
366 /* callback functions */
368 static void compose_notebook_size_alloc (GtkNotebook *notebook,
369 GtkAllocation *allocation,
371 static gboolean compose_edit_size_alloc (GtkEditable *widget,
372 GtkAllocation *allocation,
373 GtkSHRuler *shruler);
374 static void account_activated (GtkComboBox *optmenu,
376 static void attach_selected (GtkTreeView *tree_view,
377 GtkTreePath *tree_path,
378 GtkTreeViewColumn *column,
380 static gboolean attach_button_pressed (GtkWidget *widget,
381 GdkEventButton *event,
383 static gboolean attach_key_pressed (GtkWidget *widget,
386 static void compose_send_cb (GtkAction *action, gpointer data);
387 static void compose_send_later_cb (GtkAction *action, gpointer data);
389 static void compose_save_cb (GtkAction *action,
392 static void compose_attach_cb (GtkAction *action,
394 static void compose_insert_file_cb (GtkAction *action,
396 static void compose_insert_sig_cb (GtkAction *action,
398 static void compose_replace_sig_cb (GtkAction *action,
401 static void compose_close_cb (GtkAction *action,
403 static void compose_print_cb (GtkAction *action,
406 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
408 static void compose_address_cb (GtkAction *action,
410 static void about_show_cb (GtkAction *action,
412 static void compose_template_activate_cb(GtkWidget *widget,
415 static void compose_ext_editor_cb (GtkAction *action,
418 static gint compose_delete_cb (GtkWidget *widget,
422 static void compose_undo_cb (GtkAction *action,
424 static void compose_redo_cb (GtkAction *action,
426 static void compose_cut_cb (GtkAction *action,
428 static void compose_copy_cb (GtkAction *action,
430 static void compose_paste_cb (GtkAction *action,
432 static void compose_paste_as_quote_cb (GtkAction *action,
434 static void compose_paste_no_wrap_cb (GtkAction *action,
436 static void compose_paste_wrap_cb (GtkAction *action,
438 static void compose_allsel_cb (GtkAction *action,
441 static void compose_advanced_action_cb (GtkAction *action,
444 static void compose_grab_focus_cb (GtkWidget *widget,
447 static void compose_changed_cb (GtkTextBuffer *textbuf,
450 static void compose_wrap_cb (GtkAction *action,
452 static void compose_wrap_all_cb (GtkAction *action,
454 static void compose_find_cb (GtkAction *action,
456 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
458 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
461 static void compose_toggle_ruler_cb (GtkToggleAction *action,
463 static void compose_toggle_sign_cb (GtkToggleAction *action,
465 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
467 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
468 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
469 static void activate_privacy_system (Compose *compose,
470 PrefsAccount *account,
472 static void compose_use_signing(Compose *compose, gboolean use_signing);
473 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
474 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
476 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
478 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
479 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
480 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
482 static void compose_attach_drag_received_cb (GtkWidget *widget,
483 GdkDragContext *drag_context,
486 GtkSelectionData *data,
490 static void compose_insert_drag_received_cb (GtkWidget *widget,
491 GdkDragContext *drag_context,
494 GtkSelectionData *data,
498 static void compose_header_drag_received_cb (GtkWidget *widget,
499 GdkDragContext *drag_context,
502 GtkSelectionData *data,
507 static gboolean compose_drag_drop (GtkWidget *widget,
508 GdkDragContext *drag_context,
510 guint time, gpointer user_data);
511 static gboolean completion_set_focus_to_subject
516 static void text_inserted (GtkTextBuffer *buffer,
521 static Compose *compose_generic_reply(MsgInfo *msginfo,
522 ComposeQuoteMode quote_mode,
526 gboolean followup_and_reply_to,
529 static void compose_headerentry_changed_cb (GtkWidget *entry,
530 ComposeHeaderEntry *headerentry);
531 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
533 ComposeHeaderEntry *headerentry);
534 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
535 ComposeHeaderEntry *headerentry);
537 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
539 static void compose_allow_user_actions (Compose *compose, gboolean allow);
541 static void compose_nothing_cb (GtkAction *action, gpointer data)
547 static void compose_check_all (GtkAction *action, gpointer data);
548 static void compose_highlight_all (GtkAction *action, gpointer data);
549 static void compose_check_backwards (GtkAction *action, gpointer data);
550 static void compose_check_forwards_go (GtkAction *action, gpointer data);
553 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
555 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
558 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
559 FolderItem *folder_item);
561 static void compose_attach_update_label(Compose *compose);
562 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
563 gboolean respect_default_to);
564 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
566 static GtkActionEntry compose_popup_entries[] =
568 {"Compose", NULL, "Compose" },
569 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
570 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
571 {"Compose/---", NULL, "---", NULL, NULL, NULL },
572 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
575 static GtkActionEntry compose_entries[] =
577 {"Menu", NULL, "Menu" },
579 {"Message", NULL, N_("_Message") },
580 {"Edit", NULL, N_("_Edit") },
582 {"Spelling", NULL, N_("_Spelling") },
584 {"Options", NULL, N_("_Options") },
585 {"Tools", NULL, N_("_Tools") },
586 {"Help", NULL, N_("_Help") },
588 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
589 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
590 {"Message/---", NULL, "---" },
592 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
593 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
594 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
595 {"Message/ReplaceSig", NULL, N_("_Replace signature"), NULL, NULL, G_CALLBACK(compose_replace_sig_cb) },
596 /* {"Message/---", NULL, "---" }, */
597 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
598 /* {"Message/---", NULL, "---" }, */
599 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
600 /* {"Message/---", NULL, "---" }, */
601 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
604 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
605 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
606 {"Edit/---", NULL, "---" },
608 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
609 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
610 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
612 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
613 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
614 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
615 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
617 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
619 {"Edit/Advanced", NULL, N_("A_dvanced") },
620 {"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*/
621 {"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*/
622 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
623 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
624 {"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*/
625 {"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*/
626 {"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*/
627 {"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*/
628 {"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*/
629 {"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*/
630 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
631 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
632 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
633 {"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*/
635 /* {"Edit/---", NULL, "---" }, */
636 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
638 /* {"Edit/---", NULL, "---" }, */
639 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
640 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
641 /* {"Edit/---", NULL, "---" }, */
642 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
645 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
646 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
647 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
648 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
650 {"Spelling/---", NULL, "---" },
651 {"Spelling/Options", NULL, N_("_Options") },
656 {"Options/ReplyMode", NULL, N_("Reply _mode") },
657 {"Options/---", NULL, "---" },
658 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
659 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
661 /* {"Options/---", NULL, "---" }, */
663 {"Options/Priority", NULL, N_("_Priority") },
665 {"Options/Encoding", NULL, N_("Character _encoding") },
666 {"Options/Encoding/---", NULL, "---" },
667 #define ENC_ACTION(cs_char,c_char,string) \
668 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
670 {"Options/Encoding/Western", NULL, N_("Western European") },
671 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
672 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
673 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
674 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
675 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
676 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
677 {"Options/Encoding/Korean", NULL, N_("Korean") },
678 {"Options/Encoding/Thai", NULL, N_("Thai") },
681 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
683 {"Tools/Template", NULL, N_("_Template") },
684 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
685 {"Tools/Actions", NULL, N_("Actio_ns") },
686 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
689 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
692 static GtkToggleActionEntry compose_toggle_entries[] =
694 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
695 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
696 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
697 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
698 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
699 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
700 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
703 static GtkRadioActionEntry compose_radio_rm_entries[] =
705 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
706 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
707 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
708 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
711 static GtkRadioActionEntry compose_radio_prio_entries[] =
713 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
714 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
715 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
716 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
717 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
720 static GtkRadioActionEntry compose_radio_enc_entries[] =
722 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
729 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
730 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
731 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
732 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
733 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
734 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
735 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
736 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
737 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
738 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
739 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
740 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
741 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
742 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
743 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
744 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
745 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
746 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
747 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
748 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
749 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
750 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
751 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
752 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
753 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
756 static GtkTargetEntry compose_mime_types[] =
758 {"text/uri-list", 0, 0},
759 {"UTF8_STRING", 0, 0},
763 static gboolean compose_put_existing_to_front(MsgInfo *info)
765 const GList *compose_list = compose_get_compose_list();
766 const GList *elem = NULL;
769 for (elem = compose_list; elem != NULL && elem->data != NULL;
771 Compose *c = (Compose*)elem->data;
773 if (!c->targetinfo || !c->targetinfo->msgid ||
777 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
778 gtkut_window_popup(c->window);
786 static GdkColor quote_color1 =
787 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
788 static GdkColor quote_color2 =
789 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
790 static GdkColor quote_color3 =
791 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
793 static GdkColor quote_bgcolor1 =
794 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
795 static GdkColor quote_bgcolor2 =
796 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
797 static GdkColor quote_bgcolor3 =
798 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
800 static GdkColor signature_color = {
807 static GdkColor uri_color = {
814 static void compose_create_tags(GtkTextView *text, Compose *compose)
816 GtkTextBuffer *buffer;
817 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
818 #if !GTK_CHECK_VERSION(2, 24, 0)
825 buffer = gtk_text_view_get_buffer(text);
827 if (prefs_common.enable_color) {
828 /* grab the quote colors, converting from an int to a GdkColor */
829 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
831 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
833 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
835 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
837 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
839 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
841 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
843 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
846 signature_color = quote_color1 = quote_color2 = quote_color3 =
847 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
850 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
851 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
852 "foreground-gdk", "e_color1,
853 "paragraph-background-gdk", "e_bgcolor1,
855 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
856 "foreground-gdk", "e_color2,
857 "paragraph-background-gdk", "e_bgcolor2,
859 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
860 "foreground-gdk", "e_color3,
861 "paragraph-background-gdk", "e_bgcolor3,
864 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
865 "foreground-gdk", "e_color1,
867 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
868 "foreground-gdk", "e_color2,
870 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
871 "foreground-gdk", "e_color3,
875 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
876 "foreground-gdk", &signature_color,
879 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
880 "foreground-gdk", &uri_color,
882 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
883 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
885 #if !GTK_CHECK_VERSION(2, 24, 0)
886 color[0] = quote_color1;
887 color[1] = quote_color2;
888 color[2] = quote_color3;
889 color[3] = quote_bgcolor1;
890 color[4] = quote_bgcolor2;
891 color[5] = quote_bgcolor3;
892 color[6] = signature_color;
893 color[7] = uri_color;
895 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
896 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
898 for (i = 0; i < 8; i++) {
899 if (success[i] == FALSE) {
900 g_warning("Compose: color allocation failed.\n");
901 quote_color1 = quote_color2 = quote_color3 =
902 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
903 signature_color = uri_color = black;
909 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
912 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
915 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
917 return compose_generic_new(account, mailto, item, NULL, NULL);
920 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
922 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
925 #define SCROLL_TO_CURSOR(compose) { \
926 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
927 gtk_text_view_get_buffer( \
928 GTK_TEXT_VIEW(compose->text))); \
929 gtk_text_view_scroll_mark_onscreen( \
930 GTK_TEXT_VIEW(compose->text), \
934 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
937 if (folderidentifier) {
938 #if !GTK_CHECK_VERSION(2, 24, 0)
939 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
941 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
943 prefs_common.compose_save_to_history = add_history(
944 prefs_common.compose_save_to_history, folderidentifier);
945 #if !GTK_CHECK_VERSION(2, 24, 0)
946 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
947 prefs_common.compose_save_to_history);
949 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
950 prefs_common.compose_save_to_history);
954 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
955 if (folderidentifier)
956 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
958 gtk_entry_set_text(GTK_ENTRY(entry), "");
961 static gchar *compose_get_save_to(Compose *compose)
964 gchar *result = NULL;
965 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
966 result = gtk_editable_get_chars(entry, 0, -1);
969 #if !GTK_CHECK_VERSION(2, 24, 0)
970 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
972 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
974 prefs_common.compose_save_to_history = add_history(
975 prefs_common.compose_save_to_history, result);
976 #if !GTK_CHECK_VERSION(2, 24, 0)
977 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
978 prefs_common.compose_save_to_history);
980 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
981 prefs_common.compose_save_to_history);
987 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
988 GList *attach_files, GList *listAddress )
991 GtkTextView *textview;
992 GtkTextBuffer *textbuf;
994 const gchar *subject_format = NULL;
995 const gchar *body_format = NULL;
996 gchar *mailto_from = NULL;
997 PrefsAccount *mailto_account = NULL;
998 MsgInfo* dummyinfo = NULL;
999 gint cursor_pos = -1;
1000 MailField mfield = NO_FIELD_PRESENT;
1004 /* check if mailto defines a from */
1005 if (mailto && *mailto != '\0') {
1006 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1007 /* mailto defines a from, check if we can get account prefs from it,
1008 if not, the account prefs will be guessed using other ways, but we'll keep
1011 mailto_account = account_find_from_address(mailto_from, TRUE);
1012 if (mailto_account == NULL) {
1014 Xstrdup_a(tmp_from, mailto_from, return NULL);
1015 extract_address(tmp_from);
1016 mailto_account = account_find_from_address(tmp_from, TRUE);
1020 account = mailto_account;
1023 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1024 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
1025 account = account_find_from_id(item->prefs->default_account);
1027 /* if no account prefs set, fallback to the current one */
1028 if (!account) account = cur_account;
1029 cm_return_val_if_fail(account != NULL, NULL);
1031 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1033 /* override from name if mailto asked for it */
1035 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1036 g_free(mailto_from);
1038 /* override from name according to folder properties */
1039 if (item && item->prefs &&
1040 item->prefs->compose_with_format &&
1041 item->prefs->compose_override_from_format &&
1042 *item->prefs->compose_override_from_format != '\0') {
1047 dummyinfo = compose_msginfo_new_from_compose(compose);
1049 /* decode \-escape sequences in the internal representation of the quote format */
1050 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1051 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1054 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1055 compose->gtkaspell);
1057 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1059 quote_fmt_scan_string(tmp);
1062 buf = quote_fmt_get_buffer();
1064 alertpanel_error(_("New message From format error."));
1066 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1067 quote_fmt_reset_vartable();
1072 compose->replyinfo = NULL;
1073 compose->fwdinfo = NULL;
1075 textview = GTK_TEXT_VIEW(compose->text);
1076 textbuf = gtk_text_view_get_buffer(textview);
1077 compose_create_tags(textview, compose);
1079 undo_block(compose->undostruct);
1081 compose_set_dictionaries_from_folder_prefs(compose, item);
1084 if (account->auto_sig)
1085 compose_insert_sig(compose, FALSE);
1086 gtk_text_buffer_get_start_iter(textbuf, &iter);
1087 gtk_text_buffer_place_cursor(textbuf, &iter);
1089 if (account->protocol != A_NNTP) {
1090 if (mailto && *mailto != '\0') {
1091 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1094 compose_set_folder_prefs(compose, item, TRUE);
1096 if (item && item->ret_rcpt) {
1097 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1100 if (mailto && *mailto != '\0') {
1101 if (!strchr(mailto, '@'))
1102 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1104 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1105 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1106 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1107 mfield = TO_FIELD_PRESENT;
1110 * CLAWS: just don't allow return receipt request, even if the user
1111 * may want to send an email. simple but foolproof.
1113 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1115 compose_add_field_list( compose, listAddress );
1117 if (item && item->prefs && item->prefs->compose_with_format) {
1118 subject_format = item->prefs->compose_subject_format;
1119 body_format = item->prefs->compose_body_format;
1120 } else if (account->compose_with_format) {
1121 subject_format = account->compose_subject_format;
1122 body_format = account->compose_body_format;
1123 } else if (prefs_common.compose_with_format) {
1124 subject_format = prefs_common.compose_subject_format;
1125 body_format = prefs_common.compose_body_format;
1128 if (subject_format || body_format) {
1131 && *subject_format != '\0' )
1133 gchar *subject = NULL;
1138 dummyinfo = compose_msginfo_new_from_compose(compose);
1140 /* decode \-escape sequences in the internal representation of the quote format */
1141 tmp = g_malloc(strlen(subject_format)+1);
1142 pref_get_unescaped_pref(tmp, subject_format);
1144 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1146 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1147 compose->gtkaspell);
1149 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1151 quote_fmt_scan_string(tmp);
1154 buf = quote_fmt_get_buffer();
1156 alertpanel_error(_("New message subject format error."));
1158 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1159 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1160 quote_fmt_reset_vartable();
1164 mfield = SUBJECT_FIELD_PRESENT;
1168 && *body_format != '\0' )
1171 GtkTextBuffer *buffer;
1172 GtkTextIter start, end;
1176 dummyinfo = compose_msginfo_new_from_compose(compose);
1178 text = GTK_TEXT_VIEW(compose->text);
1179 buffer = gtk_text_view_get_buffer(text);
1180 gtk_text_buffer_get_start_iter(buffer, &start);
1181 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1182 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1184 compose_quote_fmt(compose, dummyinfo,
1186 NULL, tmp, FALSE, TRUE,
1187 _("The body of the \"New message\" template has an error at line %d."));
1188 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1189 quote_fmt_reset_vartable();
1193 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1194 gtkaspell_highlight_all(compose->gtkaspell);
1196 mfield = BODY_FIELD_PRESENT;
1200 procmsg_msginfo_free( dummyinfo );
1206 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1207 ainfo = (AttachInfo *) curr->data;
1208 compose_attach_append(compose, ainfo->file, ainfo->file,
1209 ainfo->content_type, ainfo->charset);
1213 compose_show_first_last_header(compose, TRUE);
1215 /* Set save folder */
1216 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1217 gchar *folderidentifier;
1219 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1220 folderidentifier = folder_item_get_identifier(item);
1221 compose_set_save_to(compose, folderidentifier);
1222 g_free(folderidentifier);
1225 /* Place cursor according to provided input (mfield) */
1227 case NO_FIELD_PRESENT:
1228 if (compose->header_last)
1229 gtk_widget_grab_focus(compose->header_last->entry);
1231 case TO_FIELD_PRESENT:
1232 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1234 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1237 gtk_widget_grab_focus(compose->subject_entry);
1239 case SUBJECT_FIELD_PRESENT:
1240 textview = GTK_TEXT_VIEW(compose->text);
1243 textbuf = gtk_text_view_get_buffer(textview);
1246 mark = gtk_text_buffer_get_insert(textbuf);
1247 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1248 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1250 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1251 * only defers where it comes to the variable body
1252 * is not null. If no body is present compose->text
1253 * will be null in which case you cannot place the
1254 * cursor inside the component so. An empty component
1255 * is therefore created before placing the cursor
1257 case BODY_FIELD_PRESENT:
1258 cursor_pos = quote_fmt_get_cursor_pos();
1259 if (cursor_pos == -1)
1260 gtk_widget_grab_focus(compose->header_last->entry);
1262 gtk_widget_grab_focus(compose->text);
1266 undo_unblock(compose->undostruct);
1268 if (prefs_common.auto_exteditor)
1269 compose_exec_ext_editor(compose);
1271 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
1273 SCROLL_TO_CURSOR(compose);
1275 compose->modified = FALSE;
1276 compose_set_title(compose);
1278 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1283 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1284 gboolean override_pref, const gchar *system)
1286 const gchar *privacy = NULL;
1288 cm_return_if_fail(compose != NULL);
1289 cm_return_if_fail(account != NULL);
1291 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1294 if (account->default_privacy_system && strlen(account->default_privacy_system))
1295 privacy = account->default_privacy_system;
1299 GSList *privacy_avail = privacy_get_system_ids();
1300 if (privacy_avail && g_slist_length(privacy_avail)) {
1301 privacy = (gchar *)(privacy_avail->data);
1304 if (privacy != NULL) {
1306 g_free(compose->privacy_system);
1307 compose->privacy_system = NULL;
1309 if (compose->privacy_system == NULL)
1310 compose->privacy_system = g_strdup(privacy);
1311 else if (*(compose->privacy_system) == '\0') {
1312 g_free(compose->privacy_system);
1313 compose->privacy_system = g_strdup(privacy);
1315 compose_update_privacy_system_menu_item(compose, FALSE);
1316 compose_use_encryption(compose, TRUE);
1320 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1322 const gchar *privacy = NULL;
1324 if (account->default_privacy_system && strlen(account->default_privacy_system))
1325 privacy = account->default_privacy_system;
1329 GSList *privacy_avail = privacy_get_system_ids();
1330 if (privacy_avail && g_slist_length(privacy_avail)) {
1331 privacy = (gchar *)(privacy_avail->data);
1335 if (privacy != NULL) {
1337 g_free(compose->privacy_system);
1338 compose->privacy_system = NULL;
1340 if (compose->privacy_system == NULL)
1341 compose->privacy_system = g_strdup(privacy);
1342 compose_update_privacy_system_menu_item(compose, FALSE);
1343 compose_use_signing(compose, TRUE);
1347 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1351 Compose *compose = NULL;
1353 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1355 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1356 cm_return_val_if_fail(msginfo != NULL, NULL);
1358 list_len = g_slist_length(msginfo_list);
1362 case COMPOSE_REPLY_TO_ADDRESS:
1363 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1364 FALSE, prefs_common.default_reply_list, FALSE, body);
1366 case COMPOSE_REPLY_WITH_QUOTE:
1367 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1368 FALSE, prefs_common.default_reply_list, FALSE, body);
1370 case COMPOSE_REPLY_WITHOUT_QUOTE:
1371 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1372 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1374 case COMPOSE_REPLY_TO_SENDER:
1375 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1376 FALSE, FALSE, TRUE, body);
1378 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1379 compose = compose_followup_and_reply_to(msginfo,
1380 COMPOSE_QUOTE_CHECK,
1381 FALSE, FALSE, body);
1383 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1384 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1385 FALSE, FALSE, TRUE, body);
1387 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1388 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1389 FALSE, FALSE, TRUE, NULL);
1391 case COMPOSE_REPLY_TO_ALL:
1392 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1393 TRUE, FALSE, FALSE, body);
1395 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1396 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1397 TRUE, FALSE, FALSE, body);
1399 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1400 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1401 TRUE, FALSE, FALSE, NULL);
1403 case COMPOSE_REPLY_TO_LIST:
1404 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1405 FALSE, TRUE, FALSE, body);
1407 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1408 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1409 FALSE, TRUE, FALSE, body);
1411 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1412 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1413 FALSE, TRUE, FALSE, NULL);
1415 case COMPOSE_FORWARD:
1416 if (prefs_common.forward_as_attachment) {
1417 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1420 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1424 case COMPOSE_FORWARD_INLINE:
1425 /* check if we reply to more than one Message */
1426 if (list_len == 1) {
1427 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1430 /* more messages FALL THROUGH */
1431 case COMPOSE_FORWARD_AS_ATTACH:
1432 compose = compose_forward_multiple(NULL, msginfo_list);
1434 case COMPOSE_REDIRECT:
1435 compose = compose_redirect(NULL, msginfo, FALSE);
1438 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1441 if (compose == NULL) {
1442 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1446 compose->rmode = mode;
1447 switch (compose->rmode) {
1449 case COMPOSE_REPLY_WITH_QUOTE:
1450 case COMPOSE_REPLY_WITHOUT_QUOTE:
1451 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1452 debug_print("reply mode Normal\n");
1453 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1454 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1456 case COMPOSE_REPLY_TO_SENDER:
1457 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1458 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1459 debug_print("reply mode Sender\n");
1460 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1462 case COMPOSE_REPLY_TO_ALL:
1463 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1464 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1465 debug_print("reply mode All\n");
1466 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1468 case COMPOSE_REPLY_TO_LIST:
1469 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1470 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1471 debug_print("reply mode List\n");
1472 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1474 case COMPOSE_REPLY_TO_ADDRESS:
1475 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1483 static Compose *compose_reply(MsgInfo *msginfo,
1484 ComposeQuoteMode quote_mode,
1490 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1491 to_sender, FALSE, body);
1494 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1495 ComposeQuoteMode quote_mode,
1500 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1501 to_sender, TRUE, body);
1504 static void compose_extract_original_charset(Compose *compose)
1506 MsgInfo *info = NULL;
1507 if (compose->replyinfo) {
1508 info = compose->replyinfo;
1509 } else if (compose->fwdinfo) {
1510 info = compose->fwdinfo;
1511 } else if (compose->targetinfo) {
1512 info = compose->targetinfo;
1515 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1516 MimeInfo *partinfo = mimeinfo;
1517 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1518 partinfo = procmime_mimeinfo_next(partinfo);
1520 compose->orig_charset =
1521 g_strdup(procmime_mimeinfo_get_parameter(
1522 partinfo, "charset"));
1524 procmime_mimeinfo_free_all(mimeinfo);
1528 #define SIGNAL_BLOCK(buffer) { \
1529 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1530 G_CALLBACK(compose_changed_cb), \
1532 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1533 G_CALLBACK(text_inserted), \
1537 #define SIGNAL_UNBLOCK(buffer) { \
1538 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1539 G_CALLBACK(compose_changed_cb), \
1541 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1542 G_CALLBACK(text_inserted), \
1546 static Compose *compose_generic_reply(MsgInfo *msginfo,
1547 ComposeQuoteMode quote_mode,
1548 gboolean to_all, gboolean to_ml,
1550 gboolean followup_and_reply_to,
1554 PrefsAccount *account = NULL;
1555 GtkTextView *textview;
1556 GtkTextBuffer *textbuf;
1557 gboolean quote = FALSE;
1558 const gchar *qmark = NULL;
1559 const gchar *body_fmt = NULL;
1560 gchar *s_system = NULL;
1562 cm_return_val_if_fail(msginfo != NULL, NULL);
1563 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1565 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1567 cm_return_val_if_fail(account != NULL, NULL);
1569 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1571 compose->updating = TRUE;
1573 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1574 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1576 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1577 if (!compose->replyinfo)
1578 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1580 compose_extract_original_charset(compose);
1582 if (msginfo->folder && msginfo->folder->ret_rcpt)
1583 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1585 /* Set save folder */
1586 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1587 gchar *folderidentifier;
1589 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1590 folderidentifier = folder_item_get_identifier(msginfo->folder);
1591 compose_set_save_to(compose, folderidentifier);
1592 g_free(folderidentifier);
1595 if (compose_parse_header(compose, msginfo) < 0) {
1596 compose->updating = FALSE;
1597 compose_destroy(compose);
1601 /* override from name according to folder properties */
1602 if (msginfo->folder && msginfo->folder->prefs &&
1603 msginfo->folder->prefs->reply_with_format &&
1604 msginfo->folder->prefs->reply_override_from_format &&
1605 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1610 /* decode \-escape sequences in the internal representation of the quote format */
1611 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1612 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1615 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1616 compose->gtkaspell);
1618 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1620 quote_fmt_scan_string(tmp);
1623 buf = quote_fmt_get_buffer();
1625 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1627 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1628 quote_fmt_reset_vartable();
1633 textview = (GTK_TEXT_VIEW(compose->text));
1634 textbuf = gtk_text_view_get_buffer(textview);
1635 compose_create_tags(textview, compose);
1637 undo_block(compose->undostruct);
1639 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1640 gtkaspell_block_check(compose->gtkaspell);
1643 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1644 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1645 /* use the reply format of folder (if enabled), or the account's one
1646 (if enabled) or fallback to the global reply format, which is always
1647 enabled (even if empty), and use the relevant quotemark */
1649 if (msginfo->folder && msginfo->folder->prefs &&
1650 msginfo->folder->prefs->reply_with_format) {
1651 qmark = msginfo->folder->prefs->reply_quotemark;
1652 body_fmt = msginfo->folder->prefs->reply_body_format;
1654 } else if (account->reply_with_format) {
1655 qmark = account->reply_quotemark;
1656 body_fmt = account->reply_body_format;
1659 qmark = prefs_common.quotemark;
1660 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1661 body_fmt = gettext(prefs_common.quotefmt);
1668 /* empty quotemark is not allowed */
1669 if (qmark == NULL || *qmark == '\0')
1671 compose_quote_fmt(compose, compose->replyinfo,
1672 body_fmt, qmark, body, FALSE, TRUE,
1673 _("The body of the \"Reply\" template has an error at line %d."));
1674 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1675 quote_fmt_reset_vartable();
1678 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1679 compose_force_encryption(compose, account, FALSE, s_system);
1682 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1683 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1684 compose_force_signing(compose, account, s_system);
1688 SIGNAL_BLOCK(textbuf);
1690 if (account->auto_sig)
1691 compose_insert_sig(compose, FALSE);
1693 compose_wrap_all(compose);
1696 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1697 gtkaspell_highlight_all(compose->gtkaspell);
1698 gtkaspell_unblock_check(compose->gtkaspell);
1700 SIGNAL_UNBLOCK(textbuf);
1702 gtk_widget_grab_focus(compose->text);
1704 undo_unblock(compose->undostruct);
1706 if (prefs_common.auto_exteditor)
1707 compose_exec_ext_editor(compose);
1709 compose->modified = FALSE;
1710 compose_set_title(compose);
1712 compose->updating = FALSE;
1713 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1714 SCROLL_TO_CURSOR(compose);
1716 if (compose->deferred_destroy) {
1717 compose_destroy(compose);
1725 #define INSERT_FW_HEADER(var, hdr) \
1726 if (msginfo->var && *msginfo->var) { \
1727 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1728 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1729 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1732 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1733 gboolean as_attach, const gchar *body,
1734 gboolean no_extedit,
1738 GtkTextView *textview;
1739 GtkTextBuffer *textbuf;
1740 gint cursor_pos = -1;
1743 cm_return_val_if_fail(msginfo != NULL, NULL);
1744 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1747 !(account = compose_guess_forward_account_from_msginfo
1749 account = cur_account;
1751 if (!prefs_common.forward_as_attachment)
1752 mode = COMPOSE_FORWARD_INLINE;
1754 mode = COMPOSE_FORWARD;
1755 compose = compose_create(account, msginfo->folder, mode, batch);
1757 compose->updating = TRUE;
1758 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1759 if (!compose->fwdinfo)
1760 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1762 compose_extract_original_charset(compose);
1764 if (msginfo->subject && *msginfo->subject) {
1765 gchar *buf, *buf2, *p;
1767 buf = p = g_strdup(msginfo->subject);
1768 p += subject_get_prefix_length(p);
1769 memmove(buf, p, strlen(p) + 1);
1771 buf2 = g_strdup_printf("Fw: %s", buf);
1772 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1778 /* override from name according to folder properties */
1779 if (msginfo->folder && msginfo->folder->prefs &&
1780 msginfo->folder->prefs->forward_with_format &&
1781 msginfo->folder->prefs->forward_override_from_format &&
1782 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1786 MsgInfo *full_msginfo = NULL;
1789 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1791 full_msginfo = procmsg_msginfo_copy(msginfo);
1793 /* decode \-escape sequences in the internal representation of the quote format */
1794 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1795 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1798 gtkaspell_block_check(compose->gtkaspell);
1799 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1800 compose->gtkaspell);
1802 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1804 quote_fmt_scan_string(tmp);
1807 buf = quote_fmt_get_buffer();
1809 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1811 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1812 quote_fmt_reset_vartable();
1815 procmsg_msginfo_free(full_msginfo);
1818 textview = GTK_TEXT_VIEW(compose->text);
1819 textbuf = gtk_text_view_get_buffer(textview);
1820 compose_create_tags(textview, compose);
1822 undo_block(compose->undostruct);
1826 msgfile = procmsg_get_message_file(msginfo);
1827 if (!is_file_exist(msgfile))
1828 g_warning("%s: file not exist\n", msgfile);
1830 compose_attach_append(compose, msgfile, msgfile,
1831 "message/rfc822", NULL);
1835 const gchar *qmark = NULL;
1836 const gchar *body_fmt = NULL;
1837 MsgInfo *full_msginfo;
1839 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1841 full_msginfo = procmsg_msginfo_copy(msginfo);
1843 /* use the forward format of folder (if enabled), or the account's one
1844 (if enabled) or fallback to the global forward format, which is always
1845 enabled (even if empty), and use the relevant quotemark */
1846 if (msginfo->folder && msginfo->folder->prefs &&
1847 msginfo->folder->prefs->forward_with_format) {
1848 qmark = msginfo->folder->prefs->forward_quotemark;
1849 body_fmt = msginfo->folder->prefs->forward_body_format;
1851 } else if (account->forward_with_format) {
1852 qmark = account->forward_quotemark;
1853 body_fmt = account->forward_body_format;
1856 qmark = prefs_common.fw_quotemark;
1857 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1858 body_fmt = gettext(prefs_common.fw_quotefmt);
1863 /* empty quotemark is not allowed */
1864 if (qmark == NULL || *qmark == '\0')
1867 compose_quote_fmt(compose, full_msginfo,
1868 body_fmt, qmark, body, FALSE, TRUE,
1869 _("The body of the \"Forward\" template has an error at line %d."));
1870 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1871 quote_fmt_reset_vartable();
1872 compose_attach_parts(compose, msginfo);
1874 procmsg_msginfo_free(full_msginfo);
1877 SIGNAL_BLOCK(textbuf);
1879 if (account->auto_sig)
1880 compose_insert_sig(compose, FALSE);
1882 compose_wrap_all(compose);
1885 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1886 gtkaspell_highlight_all(compose->gtkaspell);
1887 gtkaspell_unblock_check(compose->gtkaspell);
1889 SIGNAL_UNBLOCK(textbuf);
1891 cursor_pos = quote_fmt_get_cursor_pos();
1892 if (cursor_pos == -1)
1893 gtk_widget_grab_focus(compose->header_last->entry);
1895 gtk_widget_grab_focus(compose->text);
1897 if (!no_extedit && prefs_common.auto_exteditor)
1898 compose_exec_ext_editor(compose);
1901 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1902 gchar *folderidentifier;
1904 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1905 folderidentifier = folder_item_get_identifier(msginfo->folder);
1906 compose_set_save_to(compose, folderidentifier);
1907 g_free(folderidentifier);
1910 undo_unblock(compose->undostruct);
1912 compose->modified = FALSE;
1913 compose_set_title(compose);
1915 compose->updating = FALSE;
1916 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1917 SCROLL_TO_CURSOR(compose);
1919 if (compose->deferred_destroy) {
1920 compose_destroy(compose);
1924 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1929 #undef INSERT_FW_HEADER
1931 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1934 GtkTextView *textview;
1935 GtkTextBuffer *textbuf;
1939 gboolean single_mail = TRUE;
1941 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1943 if (g_slist_length(msginfo_list) > 1)
1944 single_mail = FALSE;
1946 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1947 if (((MsgInfo *)msginfo->data)->folder == NULL)
1950 /* guess account from first selected message */
1952 !(account = compose_guess_forward_account_from_msginfo
1953 (msginfo_list->data)))
1954 account = cur_account;
1956 cm_return_val_if_fail(account != NULL, NULL);
1958 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1959 if (msginfo->data) {
1960 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1961 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1965 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1966 g_warning("no msginfo_list");
1970 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1972 compose->updating = TRUE;
1974 /* override from name according to folder properties */
1975 if (msginfo_list->data) {
1976 MsgInfo *msginfo = msginfo_list->data;
1978 if (msginfo->folder && msginfo->folder->prefs &&
1979 msginfo->folder->prefs->forward_with_format &&
1980 msginfo->folder->prefs->forward_override_from_format &&
1981 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1986 /* decode \-escape sequences in the internal representation of the quote format */
1987 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1988 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1991 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1992 compose->gtkaspell);
1994 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1996 quote_fmt_scan_string(tmp);
1999 buf = quote_fmt_get_buffer();
2001 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2003 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
2004 quote_fmt_reset_vartable();
2010 textview = GTK_TEXT_VIEW(compose->text);
2011 textbuf = gtk_text_view_get_buffer(textview);
2012 compose_create_tags(textview, compose);
2014 undo_block(compose->undostruct);
2015 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
2016 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
2018 if (!is_file_exist(msgfile))
2019 g_warning("%s: file not exist\n", msgfile);
2021 compose_attach_append(compose, msgfile, msgfile,
2022 "message/rfc822", NULL);
2027 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2028 if (info->subject && *info->subject) {
2029 gchar *buf, *buf2, *p;
2031 buf = p = g_strdup(info->subject);
2032 p += subject_get_prefix_length(p);
2033 memmove(buf, p, strlen(p) + 1);
2035 buf2 = g_strdup_printf("Fw: %s", buf);
2036 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2042 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2043 _("Fw: multiple emails"));
2046 SIGNAL_BLOCK(textbuf);
2048 if (account->auto_sig)
2049 compose_insert_sig(compose, FALSE);
2051 compose_wrap_all(compose);
2053 SIGNAL_UNBLOCK(textbuf);
2055 gtk_text_buffer_get_start_iter(textbuf, &iter);
2056 gtk_text_buffer_place_cursor(textbuf, &iter);
2058 gtk_widget_grab_focus(compose->header_last->entry);
2059 undo_unblock(compose->undostruct);
2060 compose->modified = FALSE;
2061 compose_set_title(compose);
2063 compose->updating = FALSE;
2064 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2065 SCROLL_TO_CURSOR(compose);
2067 if (compose->deferred_destroy) {
2068 compose_destroy(compose);
2072 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2077 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2079 GtkTextIter start = *iter;
2080 GtkTextIter end_iter;
2081 int start_pos = gtk_text_iter_get_offset(&start);
2083 if (!compose->account->sig_sep)
2086 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2087 start_pos+strlen(compose->account->sig_sep));
2089 /* check sig separator */
2090 str = gtk_text_iter_get_text(&start, &end_iter);
2091 if (!strcmp(str, compose->account->sig_sep)) {
2093 /* check end of line (\n) */
2094 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2095 start_pos+strlen(compose->account->sig_sep));
2096 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2097 start_pos+strlen(compose->account->sig_sep)+1);
2098 tmp = gtk_text_iter_get_text(&start, &end_iter);
2099 if (!strcmp(tmp,"\n")) {
2111 static gboolean compose_update_folder_hook(gpointer source, gpointer data)
2113 FolderUpdateData *hookdata = (FolderUpdateData *)source;
2114 Compose *compose = (Compose *)data;
2115 FolderItem *old_item = NULL;
2116 FolderItem *new_item = NULL;
2117 gchar *old_id, *new_id;
2119 if (!(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
2120 && !(hookdata->update_flags & FOLDER_MOVE_FOLDERITEM))
2123 old_item = hookdata->item;
2124 new_item = hookdata->item2;
2126 old_id = folder_item_get_identifier(old_item);
2127 new_id = new_item ? folder_item_get_identifier(new_item) : g_strdup("NULL");
2129 if (compose->targetinfo && compose->targetinfo->folder == old_item) {
2130 debug_print("updating targetinfo folder: %s -> %s\n", old_id, new_id);
2131 compose->targetinfo->folder = new_item;
2134 if (compose->replyinfo && compose->replyinfo->folder == old_item) {
2135 debug_print("updating replyinfo folder: %s -> %s\n", old_id, new_id);
2136 compose->replyinfo->folder = new_item;
2139 if (compose->fwdinfo && compose->fwdinfo->folder == old_item) {
2140 debug_print("updating fwdinfo folder: %s -> %s\n", old_id, new_id);
2141 compose->fwdinfo->folder = new_item;
2149 static void compose_colorize_signature(Compose *compose)
2151 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2153 GtkTextIter end_iter;
2154 gtk_text_buffer_get_start_iter(buffer, &iter);
2155 while (gtk_text_iter_forward_line(&iter))
2156 if (compose_is_sig_separator(compose, buffer, &iter)) {
2157 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2158 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2162 #define BLOCK_WRAP() { \
2163 prev_autowrap = compose->autowrap; \
2164 buffer = gtk_text_view_get_buffer( \
2165 GTK_TEXT_VIEW(compose->text)); \
2166 compose->autowrap = FALSE; \
2168 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2169 G_CALLBACK(compose_changed_cb), \
2171 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2172 G_CALLBACK(text_inserted), \
2175 #define UNBLOCK_WRAP() { \
2176 compose->autowrap = prev_autowrap; \
2177 if (compose->autowrap) { \
2178 gint old = compose->draft_timeout_tag; \
2179 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; \
2180 compose_wrap_all(compose); \
2181 compose->draft_timeout_tag = old; \
2184 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2185 G_CALLBACK(compose_changed_cb), \
2187 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2188 G_CALLBACK(text_inserted), \
2192 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2194 Compose *compose = NULL;
2195 PrefsAccount *account = NULL;
2196 GtkTextView *textview;
2197 GtkTextBuffer *textbuf;
2201 gchar buf[BUFFSIZE];
2202 gboolean use_signing = FALSE;
2203 gboolean use_encryption = FALSE;
2204 gchar *privacy_system = NULL;
2205 int priority = PRIORITY_NORMAL;
2206 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2207 gboolean autowrap = prefs_common.autowrap;
2208 gboolean autoindent = prefs_common.auto_indent;
2209 HeaderEntry *manual_headers = NULL;
2211 cm_return_val_if_fail(msginfo != NULL, NULL);
2212 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2214 if (compose_put_existing_to_front(msginfo)) {
2218 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2219 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2220 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2221 gchar queueheader_buf[BUFFSIZE];
2224 /* Select Account from queue headers */
2225 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2226 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2227 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2228 account = account_find_from_id(id);
2230 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2231 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2232 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2233 account = account_find_from_id(id);
2235 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2236 sizeof(queueheader_buf), "NAID:")) {
2237 id = atoi(&queueheader_buf[strlen("NAID:")]);
2238 account = account_find_from_id(id);
2240 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2241 sizeof(queueheader_buf), "MAID:")) {
2242 id = atoi(&queueheader_buf[strlen("MAID:")]);
2243 account = account_find_from_id(id);
2245 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2246 sizeof(queueheader_buf), "S:")) {
2247 account = account_find_from_address(queueheader_buf, FALSE);
2249 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2250 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2251 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2252 use_signing = param;
2255 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2256 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2257 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2258 use_signing = param;
2261 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2262 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2263 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2264 use_encryption = param;
2266 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2267 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2268 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2269 use_encryption = param;
2271 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2272 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2273 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2276 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2277 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2278 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2281 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2282 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2283 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2285 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2286 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2287 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2289 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2290 sizeof(queueheader_buf), "X-Priority: ")) {
2291 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2294 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2295 sizeof(queueheader_buf), "RMID:")) {
2296 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2297 if (tokens[0] && tokens[1] && tokens[2]) {
2298 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2299 if (orig_item != NULL) {
2300 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2305 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2306 sizeof(queueheader_buf), "FMID:")) {
2307 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2308 if (tokens[0] && tokens[1] && tokens[2]) {
2309 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2310 if (orig_item != NULL) {
2311 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2316 /* Get manual headers */
2317 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2318 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2319 if (*listmh != '\0') {
2320 debug_print("Got manual headers: %s\n", listmh);
2321 manual_headers = procheader_entries_from_str(listmh);
2326 account = msginfo->folder->folder->account;
2329 if (!account && prefs_common.reedit_account_autosel) {
2330 gchar from[BUFFSIZE];
2331 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2332 extract_address(from);
2333 account = account_find_from_address(from, FALSE);
2337 account = cur_account;
2339 cm_return_val_if_fail(account != NULL, NULL);
2341 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2343 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2344 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2345 compose->autowrap = autowrap;
2346 compose->replyinfo = replyinfo;
2347 compose->fwdinfo = fwdinfo;
2349 compose->updating = TRUE;
2350 compose->priority = priority;
2352 if (privacy_system != NULL) {
2353 compose->privacy_system = privacy_system;
2354 compose_use_signing(compose, use_signing);
2355 compose_use_encryption(compose, use_encryption);
2356 compose_update_privacy_system_menu_item(compose, FALSE);
2358 activate_privacy_system(compose, account, FALSE);
2361 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2363 compose_extract_original_charset(compose);
2365 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2366 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2367 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2368 gchar queueheader_buf[BUFFSIZE];
2370 /* Set message save folder */
2371 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2372 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2373 compose_set_save_to(compose, &queueheader_buf[4]);
2375 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2376 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2378 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2383 if (compose_parse_header(compose, msginfo) < 0) {
2384 compose->updating = FALSE;
2385 compose_destroy(compose);
2388 compose_reedit_set_entry(compose, msginfo);
2390 textview = GTK_TEXT_VIEW(compose->text);
2391 textbuf = gtk_text_view_get_buffer(textview);
2392 compose_create_tags(textview, compose);
2394 mark = gtk_text_buffer_get_insert(textbuf);
2395 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2397 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2398 G_CALLBACK(compose_changed_cb),
2401 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2402 fp = procmime_get_first_encrypted_text_content(msginfo);
2404 compose_force_encryption(compose, account, TRUE, NULL);
2407 fp = procmime_get_first_text_content(msginfo);
2410 g_warning("Can't get text part\n");
2414 gboolean prev_autowrap;
2415 GtkTextBuffer *buffer;
2417 while (fgets(buf, sizeof(buf), fp) != NULL) {
2419 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2425 compose_attach_parts(compose, msginfo);
2427 compose_colorize_signature(compose);
2429 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2430 G_CALLBACK(compose_changed_cb),
2433 if (manual_headers != NULL) {
2434 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2435 procheader_entries_free(manual_headers);
2436 compose->updating = FALSE;
2437 compose_destroy(compose);
2440 procheader_entries_free(manual_headers);
2443 gtk_widget_grab_focus(compose->text);
2445 if (prefs_common.auto_exteditor) {
2446 compose_exec_ext_editor(compose);
2448 compose->modified = FALSE;
2449 compose_set_title(compose);
2451 compose->updating = FALSE;
2452 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2453 SCROLL_TO_CURSOR(compose);
2455 if (compose->deferred_destroy) {
2456 compose_destroy(compose);
2460 compose->sig_str = account_get_signature_str(compose->account);
2462 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2467 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2474 cm_return_val_if_fail(msginfo != NULL, NULL);
2477 account = account_get_reply_account(msginfo,
2478 prefs_common.reply_account_autosel);
2479 cm_return_val_if_fail(account != NULL, NULL);
2481 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2483 compose->updating = TRUE;
2485 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2486 compose->replyinfo = NULL;
2487 compose->fwdinfo = NULL;
2489 compose_show_first_last_header(compose, TRUE);
2491 gtk_widget_grab_focus(compose->header_last->entry);
2493 filename = procmsg_get_message_file(msginfo);
2495 if (filename == NULL) {
2496 compose->updating = FALSE;
2497 compose_destroy(compose);
2502 compose->redirect_filename = filename;
2504 /* Set save folder */
2505 item = msginfo->folder;
2506 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2507 gchar *folderidentifier;
2509 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2510 folderidentifier = folder_item_get_identifier(item);
2511 compose_set_save_to(compose, folderidentifier);
2512 g_free(folderidentifier);
2515 compose_attach_parts(compose, msginfo);
2517 if (msginfo->subject)
2518 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2520 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2522 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2523 _("The body of the \"Redirect\" template has an error at line %d."));
2524 quote_fmt_reset_vartable();
2525 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2527 compose_colorize_signature(compose);
2530 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2531 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2532 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2534 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2535 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2536 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2537 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2538 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", FALSE);
2539 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2540 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2541 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2542 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2544 if (compose->toolbar->draft_btn)
2545 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2546 if (compose->toolbar->insert_btn)
2547 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2548 if (compose->toolbar->attach_btn)
2549 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2550 if (compose->toolbar->sig_btn)
2551 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2552 if (compose->toolbar->exteditor_btn)
2553 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2554 if (compose->toolbar->linewrap_current_btn)
2555 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2556 if (compose->toolbar->linewrap_all_btn)
2557 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2559 compose->modified = FALSE;
2560 compose_set_title(compose);
2561 compose->updating = FALSE;
2562 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2563 SCROLL_TO_CURSOR(compose);
2565 if (compose->deferred_destroy) {
2566 compose_destroy(compose);
2570 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2575 const GList *compose_get_compose_list(void)
2577 return compose_list;
2580 void compose_entry_append(Compose *compose, const gchar *address,
2581 ComposeEntryType type, ComposePrefType pref_type)
2583 const gchar *header;
2585 gboolean in_quote = FALSE;
2586 if (!address || *address == '\0') return;
2593 header = N_("Bcc:");
2595 case COMPOSE_REPLYTO:
2596 header = N_("Reply-To:");
2598 case COMPOSE_NEWSGROUPS:
2599 header = N_("Newsgroups:");
2601 case COMPOSE_FOLLOWUPTO:
2602 header = N_( "Followup-To:");
2604 case COMPOSE_INREPLYTO:
2605 header = N_( "In-Reply-To:");
2612 header = prefs_common_translated_header_name(header);
2614 cur = begin = (gchar *)address;
2616 /* we separate the line by commas, but not if we're inside a quoted
2618 while (*cur != '\0') {
2620 in_quote = !in_quote;
2621 if (*cur == ',' && !in_quote) {
2622 gchar *tmp = g_strdup(begin);
2624 tmp[cur-begin]='\0';
2627 while (*tmp == ' ' || *tmp == '\t')
2629 compose_add_header_entry(compose, header, tmp, pref_type);
2636 gchar *tmp = g_strdup(begin);
2638 tmp[cur-begin]='\0';
2639 while (*tmp == ' ' || *tmp == '\t')
2641 compose_add_header_entry(compose, header, tmp, pref_type);
2646 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2648 #if !GTK_CHECK_VERSION(3, 0, 0)
2649 static GdkColor yellow;
2650 static GdkColor black;
2651 static gboolean yellow_initialised = FALSE;
2653 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2654 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2659 #if !GTK_CHECK_VERSION(3, 0, 0)
2660 if (!yellow_initialised) {
2661 gdk_color_parse("#f5f6be", &yellow);
2662 gdk_color_parse("#000000", &black);
2663 yellow_initialised = gdk_colormap_alloc_color(
2664 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2665 yellow_initialised &= gdk_colormap_alloc_color(
2666 gdk_colormap_get_system(), &black, FALSE, TRUE);
2670 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2671 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2672 if (gtk_entry_get_text(entry) &&
2673 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2674 #if !GTK_CHECK_VERSION(3, 0, 0)
2675 if (yellow_initialised) {
2677 gtk_widget_modify_base(
2678 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2679 GTK_STATE_NORMAL, &yellow);
2680 gtk_widget_modify_text(
2681 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2682 GTK_STATE_NORMAL, &black);
2683 #if !GTK_CHECK_VERSION(3, 0, 0)
2690 void compose_toolbar_cb(gint action, gpointer data)
2692 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2693 Compose *compose = (Compose*)toolbar_item->parent;
2695 cm_return_if_fail(compose != NULL);
2699 compose_send_cb(NULL, compose);
2702 compose_send_later_cb(NULL, compose);
2705 compose_draft(compose, COMPOSE_QUIT_EDITING);
2708 compose_insert_file_cb(NULL, compose);
2711 compose_attach_cb(NULL, compose);
2714 compose_insert_sig(compose, FALSE);
2717 compose_insert_sig(compose, TRUE);
2720 compose_ext_editor_cb(NULL, compose);
2722 case A_LINEWRAP_CURRENT:
2723 compose_beautify_paragraph(compose, NULL, TRUE);
2725 case A_LINEWRAP_ALL:
2726 compose_wrap_all_full(compose, TRUE);
2729 compose_address_cb(NULL, compose);
2732 case A_CHECK_SPELLING:
2733 compose_check_all(NULL, compose);
2741 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2746 gchar *subject = NULL;
2750 gchar **attach = NULL;
2751 gchar *inreplyto = NULL;
2752 MailField mfield = NO_FIELD_PRESENT;
2754 /* get mailto parts but skip from */
2755 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2758 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2759 mfield = TO_FIELD_PRESENT;
2762 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2764 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2766 if (!g_utf8_validate (subject, -1, NULL)) {
2767 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2768 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2771 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2773 mfield = SUBJECT_FIELD_PRESENT;
2776 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2777 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2780 gboolean prev_autowrap = compose->autowrap;
2782 compose->autowrap = FALSE;
2784 mark = gtk_text_buffer_get_insert(buffer);
2785 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2787 if (!g_utf8_validate (body, -1, NULL)) {
2788 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2789 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2792 gtk_text_buffer_insert(buffer, &iter, body, -1);
2794 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2796 compose->autowrap = prev_autowrap;
2797 if (compose->autowrap)
2798 compose_wrap_all(compose);
2799 mfield = BODY_FIELD_PRESENT;
2803 gint i = 0, att = 0;
2804 gchar *warn_files = NULL;
2805 while (attach[i] != NULL) {
2806 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2807 if (utf8_filename) {
2808 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2809 gchar *tmp = g_strdup_printf("%s%s\n",
2810 warn_files?warn_files:"",
2816 g_free(utf8_filename);
2818 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2823 alertpanel_notice(ngettext(
2824 "The following file has been attached: \n%s",
2825 "The following files have been attached: \n%s", att), warn_files);
2830 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2843 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2845 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2846 {"Cc:", NULL, TRUE},
2847 {"References:", NULL, FALSE},
2848 {"Bcc:", NULL, TRUE},
2849 {"Newsgroups:", NULL, TRUE},
2850 {"Followup-To:", NULL, TRUE},
2851 {"List-Post:", NULL, FALSE},
2852 {"X-Priority:", NULL, FALSE},
2853 {NULL, NULL, FALSE}};
2869 cm_return_val_if_fail(msginfo != NULL, -1);
2871 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2872 procheader_get_header_fields(fp, hentry);
2875 if (hentry[H_REPLY_TO].body != NULL) {
2876 if (hentry[H_REPLY_TO].body[0] != '\0') {
2878 conv_unmime_header(hentry[H_REPLY_TO].body,
2881 g_free(hentry[H_REPLY_TO].body);
2882 hentry[H_REPLY_TO].body = NULL;
2884 if (hentry[H_CC].body != NULL) {
2885 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2886 g_free(hentry[H_CC].body);
2887 hentry[H_CC].body = NULL;
2889 if (hentry[H_REFERENCES].body != NULL) {
2890 if (compose->mode == COMPOSE_REEDIT)
2891 compose->references = hentry[H_REFERENCES].body;
2893 compose->references = compose_parse_references
2894 (hentry[H_REFERENCES].body, msginfo->msgid);
2895 g_free(hentry[H_REFERENCES].body);
2897 hentry[H_REFERENCES].body = NULL;
2899 if (hentry[H_BCC].body != NULL) {
2900 if (compose->mode == COMPOSE_REEDIT)
2902 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2903 g_free(hentry[H_BCC].body);
2904 hentry[H_BCC].body = NULL;
2906 if (hentry[H_NEWSGROUPS].body != NULL) {
2907 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2908 hentry[H_NEWSGROUPS].body = NULL;
2910 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2911 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2912 compose->followup_to =
2913 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2916 g_free(hentry[H_FOLLOWUP_TO].body);
2917 hentry[H_FOLLOWUP_TO].body = NULL;
2919 if (hentry[H_LIST_POST].body != NULL) {
2920 gchar *to = NULL, *start = NULL;
2922 extract_address(hentry[H_LIST_POST].body);
2923 if (hentry[H_LIST_POST].body[0] != '\0') {
2924 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2926 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2927 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2930 g_free(compose->ml_post);
2931 compose->ml_post = to;
2934 g_free(hentry[H_LIST_POST].body);
2935 hentry[H_LIST_POST].body = NULL;
2938 /* CLAWS - X-Priority */
2939 if (compose->mode == COMPOSE_REEDIT)
2940 if (hentry[H_X_PRIORITY].body != NULL) {
2943 priority = atoi(hentry[H_X_PRIORITY].body);
2944 g_free(hentry[H_X_PRIORITY].body);
2946 hentry[H_X_PRIORITY].body = NULL;
2948 if (priority < PRIORITY_HIGHEST ||
2949 priority > PRIORITY_LOWEST)
2950 priority = PRIORITY_NORMAL;
2952 compose->priority = priority;
2955 if (compose->mode == COMPOSE_REEDIT) {
2956 if (msginfo->inreplyto && *msginfo->inreplyto)
2957 compose->inreplyto = g_strdup(msginfo->inreplyto);
2961 if (msginfo->msgid && *msginfo->msgid)
2962 compose->inreplyto = g_strdup(msginfo->msgid);
2964 if (!compose->references) {
2965 if (msginfo->msgid && *msginfo->msgid) {
2966 if (msginfo->inreplyto && *msginfo->inreplyto)
2967 compose->references =
2968 g_strdup_printf("<%s>\n\t<%s>",
2972 compose->references =
2973 g_strconcat("<", msginfo->msgid, ">",
2975 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2976 compose->references =
2977 g_strconcat("<", msginfo->inreplyto, ">",
2985 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2990 cm_return_val_if_fail(msginfo != NULL, -1);
2992 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2993 procheader_get_header_fields(fp, entries);
2997 while (he != NULL && he->name != NULL) {
2999 GtkListStore *model = NULL;
3001 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
3002 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
3003 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
3004 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
3005 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
3012 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
3014 GSList *ref_id_list, *cur;
3018 ref_id_list = references_list_append(NULL, ref);
3019 if (!ref_id_list) return NULL;
3020 if (msgid && *msgid)
3021 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
3026 for (cur = ref_id_list; cur != NULL; cur = cur->next)
3027 /* "<" + Message-ID + ">" + CR+LF+TAB */
3028 len += strlen((gchar *)cur->data) + 5;
3030 if (len > MAX_REFERENCES_LEN) {
3031 /* remove second message-ID */
3032 if (ref_id_list && ref_id_list->next &&
3033 ref_id_list->next->next) {
3034 g_free(ref_id_list->next->data);
3035 ref_id_list = g_slist_remove
3036 (ref_id_list, ref_id_list->next->data);
3038 slist_free_strings_full(ref_id_list);
3045 new_ref = g_string_new("");
3046 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3047 if (new_ref->len > 0)
3048 g_string_append(new_ref, "\n\t");
3049 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3052 slist_free_strings_full(ref_id_list);
3054 new_ref_str = new_ref->str;
3055 g_string_free(new_ref, FALSE);
3060 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3061 const gchar *fmt, const gchar *qmark,
3062 const gchar *body, gboolean rewrap,
3063 gboolean need_unescape,
3064 const gchar *err_msg)
3066 MsgInfo* dummyinfo = NULL;
3067 gchar *quote_str = NULL;
3069 gboolean prev_autowrap;
3070 const gchar *trimmed_body = body;
3071 gint cursor_pos = -1;
3072 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3073 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3078 SIGNAL_BLOCK(buffer);
3081 dummyinfo = compose_msginfo_new_from_compose(compose);
3082 msginfo = dummyinfo;
3085 if (qmark != NULL) {
3087 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3088 compose->gtkaspell);
3090 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3092 quote_fmt_scan_string(qmark);
3095 buf = quote_fmt_get_buffer();
3097 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3099 Xstrdup_a(quote_str, buf, goto error)
3102 if (fmt && *fmt != '\0') {
3105 while (*trimmed_body == '\n')
3109 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3110 compose->gtkaspell);
3112 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3114 if (need_unescape) {
3117 /* decode \-escape sequences in the internal representation of the quote format */
3118 tmp = g_malloc(strlen(fmt)+1);
3119 pref_get_unescaped_pref(tmp, fmt);
3120 quote_fmt_scan_string(tmp);
3124 quote_fmt_scan_string(fmt);
3128 buf = quote_fmt_get_buffer();
3130 gint line = quote_fmt_get_line();
3131 alertpanel_error(err_msg, line);
3137 prev_autowrap = compose->autowrap;
3138 compose->autowrap = FALSE;
3140 mark = gtk_text_buffer_get_insert(buffer);
3141 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3142 if (g_utf8_validate(buf, -1, NULL)) {
3143 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3145 gchar *tmpout = NULL;
3146 tmpout = conv_codeset_strdup
3147 (buf, conv_get_locale_charset_str_no_utf8(),
3149 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3151 tmpout = g_malloc(strlen(buf)*2+1);
3152 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3154 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3158 cursor_pos = quote_fmt_get_cursor_pos();
3159 if (cursor_pos == -1)
3160 cursor_pos = gtk_text_iter_get_offset(&iter);
3161 compose->set_cursor_pos = cursor_pos;
3163 gtk_text_buffer_get_start_iter(buffer, &iter);
3164 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3165 gtk_text_buffer_place_cursor(buffer, &iter);
3167 compose->autowrap = prev_autowrap;
3168 if (compose->autowrap && rewrap)
3169 compose_wrap_all(compose);
3176 SIGNAL_UNBLOCK(buffer);
3178 procmsg_msginfo_free( dummyinfo );
3183 /* if ml_post is of type addr@host and from is of type