2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2012 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "claws-features.h"
27 #ifndef PANGO_ENABLE_ENGINE
28 # define PANGO_ENABLE_ENGINE
32 #include <glib/gi18n.h>
33 #include <gdk/gdkkeysyms.h>
36 #include <pango/pango-break.h>
41 #include <sys/types.h>
47 # include <sys/wait.h>
51 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
55 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
62 #include "mainwindow.h"
64 #ifndef USE_NEW_ADDRBOOK
65 #include "addressbook.h"
67 #include "addressbook-dbus.h"
68 #include "addressadd.h"
70 #include "folderview.h"
73 #include "stock_pixmap.h"
74 #include "send_message.h"
77 #include "customheader.h"
78 #include "prefs_common.h"
79 #include "prefs_account.h"
83 #include "procheader.h"
85 #include "statusbar.h"
88 #include "quoted-printable.h"
92 #include "gtkshruler.h"
94 #include "alertpanel.h"
95 #include "manage_window.h"
97 #include "folder_item_prefs.h"
98 #include "addr_compl.h"
99 #include "quote_fmt.h"
101 #include "foldersel.h"
104 #include "message_search.h"
105 #include "combobox.h"
109 #include "autofaces.h"
110 #include "spell_entry.h"
123 #define N_ATTACH_COLS (N_COL_COLUMNS)
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
128 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
130 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
132 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
134 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
135 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
136 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
137 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
138 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
139 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
140 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
141 } ComposeCallAdvancedAction;
145 PRIORITY_HIGHEST = 1,
154 COMPOSE_INSERT_SUCCESS,
155 COMPOSE_INSERT_READ_ERROR,
156 COMPOSE_INSERT_INVALID_CHARACTER,
157 COMPOSE_INSERT_NO_FILE
158 } ComposeInsertResult;
162 COMPOSE_WRITE_FOR_SEND,
163 COMPOSE_WRITE_FOR_STORE
168 COMPOSE_QUOTE_FORCED,
175 SUBJECT_FIELD_PRESENT,
180 #define B64_LINE_SIZE 57
181 #define B64_BUFFSIZE 77
183 #define MAX_REFERENCES_LEN 999
185 static GList *compose_list = NULL;
186 static GSList *extra_headers = NULL;
188 static Compose *compose_generic_new (PrefsAccount *account,
192 GList *listAddress );
194 static Compose *compose_create (PrefsAccount *account,
199 static void compose_entry_mark_default_to (Compose *compose,
200 const gchar *address);
201 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
202 ComposeQuoteMode quote_mode,
206 static Compose *compose_forward_multiple (PrefsAccount *account,
207 GSList *msginfo_list);
208 static Compose *compose_reply (MsgInfo *msginfo,
209 ComposeQuoteMode quote_mode,
214 static Compose *compose_reply_mode (ComposeMode mode,
215 GSList *msginfo_list,
217 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
218 static void compose_update_privacy_systems_menu(Compose *compose);
220 static GtkWidget *compose_account_option_menu_create
222 static void compose_set_out_encoding (Compose *compose);
223 static void compose_set_template_menu (Compose *compose);
224 static void compose_destroy (Compose *compose);
226 static MailField compose_entries_set (Compose *compose,
228 ComposeEntryType to_type);
229 static gint compose_parse_header (Compose *compose,
231 static gint compose_parse_manual_headers (Compose *compose,
233 HeaderEntry *entries);
234 static gchar *compose_parse_references (const gchar *ref,
237 static gchar *compose_quote_fmt (Compose *compose,
243 gboolean need_unescape,
244 const gchar *err_msg);
246 static void compose_reply_set_entry (Compose *compose,
252 followup_and_reply_to);
253 static void compose_reedit_set_entry (Compose *compose,
256 static void compose_insert_sig (Compose *compose,
258 static ComposeInsertResult compose_insert_file (Compose *compose,
261 static gboolean compose_attach_append (Compose *compose,
264 const gchar *content_type,
265 const gchar *charset);
266 static void compose_attach_parts (Compose *compose,
269 static gboolean compose_beautify_paragraph (Compose *compose,
270 GtkTextIter *par_iter,
272 static void compose_wrap_all (Compose *compose);
273 static void compose_wrap_all_full (Compose *compose,
276 static void compose_set_title (Compose *compose);
277 static void compose_select_account (Compose *compose,
278 PrefsAccount *account,
281 static PrefsAccount *compose_current_mail_account(void);
282 /* static gint compose_send (Compose *compose); */
283 static gboolean compose_check_for_valid_recipient
285 static gboolean compose_check_entries (Compose *compose,
286 gboolean check_everything);
287 static gint compose_write_to_file (Compose *compose,
290 gboolean attach_parts);
291 static gint compose_write_body_to_file (Compose *compose,
293 static gint compose_remove_reedit_target (Compose *compose,
295 static void compose_remove_draft (Compose *compose);
296 static gint compose_queue_sub (Compose *compose,
300 gboolean check_subject,
301 gboolean remove_reedit_target);
302 static int compose_add_attachments (Compose *compose,
304 static gchar *compose_get_header (Compose *compose);
305 static gchar *compose_get_manual_headers_info (Compose *compose);
307 static void compose_convert_header (Compose *compose,
312 gboolean addr_field);
314 static void compose_attach_info_free (AttachInfo *ainfo);
315 static void compose_attach_remove_selected (GtkAction *action,
318 static void compose_template_apply (Compose *compose,
321 static void compose_attach_property (GtkAction *action,
323 static void compose_attach_property_create (gboolean *cancelled);
324 static void attach_property_ok (GtkWidget *widget,
325 gboolean *cancelled);
326 static void attach_property_cancel (GtkWidget *widget,
327 gboolean *cancelled);
328 static gint attach_property_delete_event (GtkWidget *widget,
330 gboolean *cancelled);
331 static gboolean attach_property_key_pressed (GtkWidget *widget,
333 gboolean *cancelled);
335 static void compose_exec_ext_editor (Compose *compose);
337 static gint compose_exec_ext_editor_real (const gchar *file);
338 static gboolean compose_ext_editor_kill (Compose *compose);
339 static gboolean compose_input_cb (GIOChannel *source,
340 GIOCondition condition,
342 static void compose_set_ext_editor_sensitive (Compose *compose,
344 #endif /* G_OS_UNIX */
346 static void compose_undo_state_changed (UndoMain *undostruct,
351 static void compose_create_header_entry (Compose *compose);
352 static void compose_add_header_entry (Compose *compose, const gchar *header,
353 gchar *text, ComposePrefType pref_type);
354 static void compose_remove_header_entries(Compose *compose);
356 static void compose_update_priority_menu_item(Compose * compose);
358 static void compose_spell_menu_changed (void *data);
359 static void compose_dict_changed (void *data);
361 static void compose_add_field_list ( Compose *compose,
362 GList *listAddress );
364 /* callback functions */
366 static void compose_notebook_size_alloc (GtkNotebook *notebook,
367 GtkAllocation *allocation,
369 static gboolean compose_edit_size_alloc (GtkEditable *widget,
370 GtkAllocation *allocation,
371 GtkSHRuler *shruler);
372 static void account_activated (GtkComboBox *optmenu,
374 static void attach_selected (GtkTreeView *tree_view,
375 GtkTreePath *tree_path,
376 GtkTreeViewColumn *column,
378 static gboolean attach_button_pressed (GtkWidget *widget,
379 GdkEventButton *event,
381 static gboolean attach_key_pressed (GtkWidget *widget,
384 static void compose_send_cb (GtkAction *action, gpointer data);
385 static void compose_send_later_cb (GtkAction *action, gpointer data);
387 static void compose_save_cb (GtkAction *action,
390 static void compose_attach_cb (GtkAction *action,
392 static void compose_insert_file_cb (GtkAction *action,
394 static void compose_insert_sig_cb (GtkAction *action,
397 static void compose_close_cb (GtkAction *action,
399 static void compose_print_cb (GtkAction *action,
402 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
404 static void compose_address_cb (GtkAction *action,
406 static void about_show_cb (GtkAction *action,
408 static void compose_template_activate_cb(GtkWidget *widget,
411 static void compose_ext_editor_cb (GtkAction *action,
414 static gint compose_delete_cb (GtkWidget *widget,
418 static void compose_undo_cb (GtkAction *action,
420 static void compose_redo_cb (GtkAction *action,
422 static void compose_cut_cb (GtkAction *action,
424 static void compose_copy_cb (GtkAction *action,
426 static void compose_paste_cb (GtkAction *action,
428 static void compose_paste_as_quote_cb (GtkAction *action,
430 static void compose_paste_no_wrap_cb (GtkAction *action,
432 static void compose_paste_wrap_cb (GtkAction *action,
434 static void compose_allsel_cb (GtkAction *action,
437 static void compose_advanced_action_cb (GtkAction *action,
440 static void compose_grab_focus_cb (GtkWidget *widget,
443 static void compose_changed_cb (GtkTextBuffer *textbuf,
446 static void compose_wrap_cb (GtkAction *action,
448 static void compose_wrap_all_cb (GtkAction *action,
450 static void compose_find_cb (GtkAction *action,
452 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
454 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
457 static void compose_toggle_ruler_cb (GtkToggleAction *action,
459 static void compose_toggle_sign_cb (GtkToggleAction *action,
461 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
463 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
464 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
465 static void activate_privacy_system (Compose *compose,
466 PrefsAccount *account,
468 static void compose_use_signing(Compose *compose, gboolean use_signing);
469 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
470 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
472 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
474 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
475 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
476 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
478 static void compose_attach_drag_received_cb (GtkWidget *widget,
479 GdkDragContext *drag_context,
482 GtkSelectionData *data,
486 static void compose_insert_drag_received_cb (GtkWidget *widget,
487 GdkDragContext *drag_context,
490 GtkSelectionData *data,
494 static void compose_header_drag_received_cb (GtkWidget *widget,
495 GdkDragContext *drag_context,
498 GtkSelectionData *data,
503 static gboolean compose_drag_drop (GtkWidget *widget,
504 GdkDragContext *drag_context,
506 guint time, gpointer user_data);
507 static gboolean completion_set_focus_to_subject
512 static void text_inserted (GtkTextBuffer *buffer,
517 static Compose *compose_generic_reply(MsgInfo *msginfo,
518 ComposeQuoteMode quote_mode,
522 gboolean followup_and_reply_to,
525 static void compose_headerentry_changed_cb (GtkWidget *entry,
526 ComposeHeaderEntry *headerentry);
527 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
529 ComposeHeaderEntry *headerentry);
530 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
531 ComposeHeaderEntry *headerentry);
533 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
535 static void compose_allow_user_actions (Compose *compose, gboolean allow);
537 static void compose_nothing_cb (GtkAction *action, gpointer data)
543 static void compose_check_all (GtkAction *action, gpointer data);
544 static void compose_highlight_all (GtkAction *action, gpointer data);
545 static void compose_check_backwards (GtkAction *action, gpointer data);
546 static void compose_check_forwards_go (GtkAction *action, gpointer data);
549 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
551 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
554 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
555 FolderItem *folder_item);
557 static void compose_attach_update_label(Compose *compose);
558 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
559 gboolean respect_default_to);
560 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
562 static GtkActionEntry compose_popup_entries[] =
564 {"Compose", NULL, "Compose" },
565 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
566 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
567 {"Compose/---", NULL, "---", NULL, NULL, NULL },
568 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
571 static GtkActionEntry compose_entries[] =
573 {"Menu", NULL, "Menu" },
575 {"Message", NULL, N_("_Message") },
576 {"Edit", NULL, N_("_Edit") },
578 {"Spelling", NULL, N_("_Spelling") },
580 {"Options", NULL, N_("_Options") },
581 {"Tools", NULL, N_("_Tools") },
582 {"Help", NULL, N_("_Help") },
584 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
585 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
586 {"Message/---", NULL, "---" },
588 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
589 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
590 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
591 /* {"Message/---", NULL, "---" }, */
592 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
593 /* {"Message/---", NULL, "---" }, */
594 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
595 /* {"Message/---", NULL, "---" }, */
596 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
599 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
600 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
601 {"Edit/---", NULL, "---" },
603 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
604 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
605 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
607 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
608 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
609 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
610 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
612 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
614 {"Edit/Advanced", NULL, N_("A_dvanced") },
615 {"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*/
616 {"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*/
617 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
618 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
619 {"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*/
620 {"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*/
621 {"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*/
622 {"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*/
623 {"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*/
624 {"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*/
625 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
626 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
627 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
628 {"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*/
630 /* {"Edit/---", NULL, "---" }, */
631 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
633 /* {"Edit/---", NULL, "---" }, */
634 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
635 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
636 /* {"Edit/---", NULL, "---" }, */
637 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
640 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
641 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
642 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
643 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
645 {"Spelling/---", NULL, "---" },
646 {"Spelling/Options", NULL, N_("_Options") },
651 {"Options/ReplyMode", NULL, N_("Reply _mode") },
652 {"Options/---", NULL, "---" },
653 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
654 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
656 /* {"Options/---", NULL, "---" }, */
658 {"Options/Priority", NULL, N_("_Priority") },
660 {"Options/Encoding", NULL, N_("Character _encoding") },
661 {"Options/Encoding/---", NULL, "---" },
662 #define ENC_ACTION(cs_char,c_char,string) \
663 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
665 {"Options/Encoding/Western", NULL, N_("Western European") },
666 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
667 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
668 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
669 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
670 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
671 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
672 {"Options/Encoding/Korean", NULL, N_("Korean") },
673 {"Options/Encoding/Thai", NULL, N_("Thai") },
676 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
678 {"Tools/Template", NULL, N_("_Template") },
679 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
680 {"Tools/Actions", NULL, N_("Actio_ns") },
681 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
684 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
687 static GtkToggleActionEntry compose_toggle_entries[] =
689 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
690 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
691 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
692 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
693 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
694 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
695 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
698 static GtkRadioActionEntry compose_radio_rm_entries[] =
700 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
701 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
702 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
703 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
706 static GtkRadioActionEntry compose_radio_prio_entries[] =
708 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
709 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
710 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
711 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
712 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
715 static GtkRadioActionEntry compose_radio_enc_entries[] =
717 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
729 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
730 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
731 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
732 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
733 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
734 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
735 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
736 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
737 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
738 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
739 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
740 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
741 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
742 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
743 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
744 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
745 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
746 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
747 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
748 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
751 static GtkTargetEntry compose_mime_types[] =
753 {"text/uri-list", 0, 0},
754 {"UTF8_STRING", 0, 0},
758 static gboolean compose_put_existing_to_front(MsgInfo *info)
760 GList *compose_list = compose_get_compose_list();
764 for (elem = compose_list; elem != NULL && elem->data != NULL;
766 Compose *c = (Compose*)elem->data;
768 if (!c->targetinfo || !c->targetinfo->msgid ||
772 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
773 gtkut_window_popup(c->window);
781 static GdkColor quote_color1 =
782 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
783 static GdkColor quote_color2 =
784 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
785 static GdkColor quote_color3 =
786 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
788 static GdkColor quote_bgcolor1 =
789 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
790 static GdkColor quote_bgcolor2 =
791 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
792 static GdkColor quote_bgcolor3 =
793 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
795 static GdkColor signature_color = {
802 static GdkColor uri_color = {
809 static void compose_create_tags(GtkTextView *text, Compose *compose)
811 GtkTextBuffer *buffer;
812 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
813 #if !GTK_CHECK_VERSION(2, 24, 0)
820 buffer = gtk_text_view_get_buffer(text);
822 if (prefs_common.enable_color) {
823 /* grab the quote colors, converting from an int to a GdkColor */
824 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
826 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
828 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
830 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
832 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
834 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
836 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
838 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
841 signature_color = quote_color1 = quote_color2 = quote_color3 =
842 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
845 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
846 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
847 "foreground-gdk", "e_color1,
848 "paragraph-background-gdk", "e_bgcolor1,
850 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
851 "foreground-gdk", "e_color2,
852 "paragraph-background-gdk", "e_bgcolor2,
854 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
855 "foreground-gdk", "e_color3,
856 "paragraph-background-gdk", "e_bgcolor3,
859 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
860 "foreground-gdk", "e_color1,
862 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
863 "foreground-gdk", "e_color2,
865 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
866 "foreground-gdk", "e_color3,
870 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
871 "foreground-gdk", &signature_color,
874 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
875 "foreground-gdk", &uri_color,
877 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
878 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
880 #if !GTK_CHECK_VERSION(2, 24, 0)
881 color[0] = quote_color1;
882 color[1] = quote_color2;
883 color[2] = quote_color3;
884 color[3] = quote_bgcolor1;
885 color[4] = quote_bgcolor2;
886 color[5] = quote_bgcolor3;
887 color[6] = signature_color;
888 color[7] = uri_color;
890 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
891 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
893 for (i = 0; i < 8; i++) {
894 if (success[i] == FALSE) {
895 g_warning("Compose: color allocation failed.\n");
896 quote_color1 = quote_color2 = quote_color3 =
897 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
898 signature_color = uri_color = black;
904 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
907 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
910 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
912 return compose_generic_new(account, mailto, item, NULL, NULL);
915 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
917 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
920 #define SCROLL_TO_CURSOR(compose) { \
921 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
922 gtk_text_view_get_buffer( \
923 GTK_TEXT_VIEW(compose->text))); \
924 gtk_text_view_scroll_mark_onscreen( \
925 GTK_TEXT_VIEW(compose->text), \
929 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
932 if (folderidentifier) {
933 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
934 prefs_common.compose_save_to_history = add_history(
935 prefs_common.compose_save_to_history, folderidentifier);
936 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
937 prefs_common.compose_save_to_history);
940 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
941 if (folderidentifier)
942 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
944 gtk_entry_set_text(GTK_ENTRY(entry), "");
947 static gchar *compose_get_save_to(Compose *compose)
950 gchar *result = NULL;
951 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
952 result = gtk_editable_get_chars(entry, 0, -1);
955 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
956 prefs_common.compose_save_to_history = add_history(
957 prefs_common.compose_save_to_history, result);
958 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
959 prefs_common.compose_save_to_history);
964 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
965 GList *attach_files, GList *listAddress )
968 GtkTextView *textview;
969 GtkTextBuffer *textbuf;
971 const gchar *subject_format = NULL;
972 const gchar *body_format = NULL;
973 gchar *mailto_from = NULL;
974 PrefsAccount *mailto_account = NULL;
975 MsgInfo* dummyinfo = NULL;
976 gint cursor_pos = -1;
977 MailField mfield = NO_FIELD_PRESENT;
981 /* check if mailto defines a from */
982 if (mailto && *mailto != '\0') {
983 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
984 /* mailto defines a from, check if we can get account prefs from it,
985 if not, the account prefs will be guessed using other ways, but we'll keep
988 mailto_account = account_find_from_address(mailto_from, TRUE);
989 if (mailto_account == NULL) {
991 Xstrdup_a(tmp_from, mailto_from, return NULL);
992 extract_address(tmp_from);
993 mailto_account = account_find_from_address(tmp_from, TRUE);
997 account = mailto_account;
1000 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1001 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
1002 account = account_find_from_id(item->prefs->default_account);
1004 /* if no account prefs set, fallback to the current one */
1005 if (!account) account = cur_account;
1006 cm_return_val_if_fail(account != NULL, NULL);
1008 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1010 /* override from name if mailto asked for it */
1012 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1013 g_free(mailto_from);
1015 /* override from name according to folder properties */
1016 if (item && item->prefs &&
1017 item->prefs->compose_with_format &&
1018 item->prefs->compose_override_from_format &&
1019 *item->prefs->compose_override_from_format != '\0') {
1024 dummyinfo = compose_msginfo_new_from_compose(compose);
1026 /* decode \-escape sequences in the internal representation of the quote format */
1027 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1028 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1031 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1032 compose->gtkaspell);
1034 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1036 quote_fmt_scan_string(tmp);
1039 buf = quote_fmt_get_buffer();
1041 alertpanel_error(_("New message From format error."));
1043 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1044 quote_fmt_reset_vartable();
1049 compose->replyinfo = NULL;
1050 compose->fwdinfo = NULL;
1052 textview = GTK_TEXT_VIEW(compose->text);
1053 textbuf = gtk_text_view_get_buffer(textview);
1054 compose_create_tags(textview, compose);
1056 undo_block(compose->undostruct);
1058 compose_set_dictionaries_from_folder_prefs(compose, item);
1061 if (account->auto_sig)
1062 compose_insert_sig(compose, FALSE);
1063 gtk_text_buffer_get_start_iter(textbuf, &iter);
1064 gtk_text_buffer_place_cursor(textbuf, &iter);
1066 if (account->protocol != A_NNTP) {
1067 if (mailto && *mailto != '\0') {
1068 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1071 compose_set_folder_prefs(compose, item, TRUE);
1073 if (item && item->ret_rcpt) {
1074 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1077 if (mailto && *mailto != '\0') {
1078 if (!strchr(mailto, '@'))
1079 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1081 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1082 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1083 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1084 mfield = TO_FIELD_PRESENT;
1087 * CLAWS: just don't allow return receipt request, even if the user
1088 * may want to send an email. simple but foolproof.
1090 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1092 compose_add_field_list( compose, listAddress );
1094 if (item && item->prefs && item->prefs->compose_with_format) {
1095 subject_format = item->prefs->compose_subject_format;
1096 body_format = item->prefs->compose_body_format;
1097 } else if (account->compose_with_format) {
1098 subject_format = account->compose_subject_format;
1099 body_format = account->compose_body_format;
1100 } else if (prefs_common.compose_with_format) {
1101 subject_format = prefs_common.compose_subject_format;
1102 body_format = prefs_common.compose_body_format;
1105 if (subject_format || body_format) {
1108 && *subject_format != '\0' )
1110 gchar *subject = NULL;
1115 dummyinfo = compose_msginfo_new_from_compose(compose);
1117 /* decode \-escape sequences in the internal representation of the quote format */
1118 tmp = g_malloc(strlen(subject_format)+1);
1119 pref_get_unescaped_pref(tmp, subject_format);
1121 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1123 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1124 compose->gtkaspell);
1126 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1128 quote_fmt_scan_string(tmp);
1131 buf = quote_fmt_get_buffer();
1133 alertpanel_error(_("New message subject format error."));
1135 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1136 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1137 quote_fmt_reset_vartable();
1141 mfield = SUBJECT_FIELD_PRESENT;
1145 && *body_format != '\0' )
1148 GtkTextBuffer *buffer;
1149 GtkTextIter start, end;
1153 dummyinfo = compose_msginfo_new_from_compose(compose);
1155 text = GTK_TEXT_VIEW(compose->text);
1156 buffer = gtk_text_view_get_buffer(text);
1157 gtk_text_buffer_get_start_iter(buffer, &start);
1158 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1159 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1161 compose_quote_fmt(compose, dummyinfo,
1163 NULL, tmp, FALSE, TRUE,
1164 _("The body of the \"New message\" template has an error at line %d."));
1165 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1166 quote_fmt_reset_vartable();
1170 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1171 gtkaspell_highlight_all(compose->gtkaspell);
1173 mfield = BODY_FIELD_PRESENT;
1177 procmsg_msginfo_free( dummyinfo );
1183 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1184 ainfo = (AttachInfo *) curr->data;
1185 compose_attach_append(compose, ainfo->file, ainfo->name,
1186 ainfo->content_type, ainfo->charset);
1190 compose_show_first_last_header(compose, TRUE);
1192 /* Set save folder */
1193 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1194 gchar *folderidentifier;
1196 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1197 folderidentifier = folder_item_get_identifier(item);
1198 compose_set_save_to(compose, folderidentifier);
1199 g_free(folderidentifier);
1202 /* Place cursor according to provided input (mfield) */
1204 case NO_FIELD_PRESENT:
1205 if (compose->header_last)
1206 gtk_widget_grab_focus(compose->header_last->entry);
1208 case TO_FIELD_PRESENT:
1209 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1211 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1214 gtk_widget_grab_focus(compose->subject_entry);
1216 case SUBJECT_FIELD_PRESENT:
1217 textview = GTK_TEXT_VIEW(compose->text);
1220 textbuf = gtk_text_view_get_buffer(textview);
1223 mark = gtk_text_buffer_get_insert(textbuf);
1224 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1225 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1227 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1228 * only defers where it comes to the variable body
1229 * is not null. If no body is present compose->text
1230 * will be null in which case you cannot place the
1231 * cursor inside the component so. An empty component
1232 * is therefore created before placing the cursor
1234 case BODY_FIELD_PRESENT:
1235 cursor_pos = quote_fmt_get_cursor_pos();
1236 if (cursor_pos == -1)
1237 gtk_widget_grab_focus(compose->header_last->entry);
1239 gtk_widget_grab_focus(compose->text);
1243 undo_unblock(compose->undostruct);
1245 if (prefs_common.auto_exteditor)
1246 compose_exec_ext_editor(compose);
1248 compose->draft_timeout_tag = -1;
1249 SCROLL_TO_CURSOR(compose);
1251 compose->modified = FALSE;
1252 compose_set_title(compose);
1254 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1259 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1260 gboolean override_pref, const gchar *system)
1262 const gchar *privacy = NULL;
1264 cm_return_if_fail(compose != NULL);
1265 cm_return_if_fail(account != NULL);
1267 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1272 else if (account->default_privacy_system
1273 && strlen(account->default_privacy_system)) {
1274 privacy = account->default_privacy_system;
1276 GSList *privacy_avail = privacy_get_system_ids();
1277 if (privacy_avail && g_slist_length(privacy_avail)) {
1278 privacy = (gchar *)(privacy_avail->data);
1281 if (privacy != NULL) {
1283 g_free(compose->privacy_system);
1284 compose->privacy_system = NULL;
1286 if (compose->privacy_system == NULL)
1287 compose->privacy_system = g_strdup(privacy);
1288 else if (*(compose->privacy_system) == '\0') {
1289 g_free(compose->privacy_system);
1290 compose->privacy_system = g_strdup(privacy);
1292 compose_update_privacy_system_menu_item(compose, FALSE);
1293 compose_use_encryption(compose, TRUE);
1297 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1299 const gchar *privacy = NULL;
1303 else if (account->default_privacy_system
1304 && strlen(account->default_privacy_system)) {
1305 privacy = account->default_privacy_system;
1307 GSList *privacy_avail = privacy_get_system_ids();
1308 if (privacy_avail && g_slist_length(privacy_avail)) {
1309 privacy = (gchar *)(privacy_avail->data);
1313 if (privacy != NULL) {
1315 g_free(compose->privacy_system);
1316 compose->privacy_system = NULL;
1318 if (compose->privacy_system == NULL)
1319 compose->privacy_system = g_strdup(privacy);
1320 compose_update_privacy_system_menu_item(compose, FALSE);
1321 compose_use_signing(compose, TRUE);
1325 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1329 Compose *compose = NULL;
1331 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1333 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1334 cm_return_val_if_fail(msginfo != NULL, NULL);
1336 list_len = g_slist_length(msginfo_list);
1340 case COMPOSE_REPLY_TO_ADDRESS:
1341 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1342 FALSE, prefs_common.default_reply_list, FALSE, body);
1344 case COMPOSE_REPLY_WITH_QUOTE:
1345 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1346 FALSE, prefs_common.default_reply_list, FALSE, body);
1348 case COMPOSE_REPLY_WITHOUT_QUOTE:
1349 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1350 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1352 case COMPOSE_REPLY_TO_SENDER:
1353 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1354 FALSE, FALSE, TRUE, body);
1356 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1357 compose = compose_followup_and_reply_to(msginfo,
1358 COMPOSE_QUOTE_CHECK,
1359 FALSE, FALSE, body);
1361 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1362 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1363 FALSE, FALSE, TRUE, body);
1365 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1366 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1367 FALSE, FALSE, TRUE, NULL);
1369 case COMPOSE_REPLY_TO_ALL:
1370 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1371 TRUE, FALSE, FALSE, body);
1373 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1374 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1375 TRUE, FALSE, FALSE, body);
1377 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1378 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1379 TRUE, FALSE, FALSE, NULL);
1381 case COMPOSE_REPLY_TO_LIST:
1382 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1383 FALSE, TRUE, FALSE, body);
1385 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1386 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1387 FALSE, TRUE, FALSE, body);
1389 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1390 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1391 FALSE, TRUE, FALSE, NULL);
1393 case COMPOSE_FORWARD:
1394 if (prefs_common.forward_as_attachment) {
1395 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1398 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1402 case COMPOSE_FORWARD_INLINE:
1403 /* check if we reply to more than one Message */
1404 if (list_len == 1) {
1405 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1408 /* more messages FALL THROUGH */
1409 case COMPOSE_FORWARD_AS_ATTACH:
1410 compose = compose_forward_multiple(NULL, msginfo_list);
1412 case COMPOSE_REDIRECT:
1413 compose = compose_redirect(NULL, msginfo, FALSE);
1416 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1419 if (compose == NULL) {
1420 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1424 compose->rmode = mode;
1425 switch (compose->rmode) {
1427 case COMPOSE_REPLY_WITH_QUOTE:
1428 case COMPOSE_REPLY_WITHOUT_QUOTE:
1429 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1430 debug_print("reply mode Normal\n");
1431 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1432 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1434 case COMPOSE_REPLY_TO_SENDER:
1435 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1436 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1437 debug_print("reply mode Sender\n");
1438 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1440 case COMPOSE_REPLY_TO_ALL:
1441 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1442 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1443 debug_print("reply mode All\n");
1444 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1446 case COMPOSE_REPLY_TO_LIST:
1447 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1448 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1449 debug_print("reply mode List\n");
1450 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1452 case COMPOSE_REPLY_TO_ADDRESS:
1453 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1461 static Compose *compose_reply(MsgInfo *msginfo,
1462 ComposeQuoteMode quote_mode,
1468 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1469 to_sender, FALSE, body);
1472 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1473 ComposeQuoteMode quote_mode,
1478 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1479 to_sender, TRUE, body);
1482 static void compose_extract_original_charset(Compose *compose)
1484 MsgInfo *info = NULL;
1485 if (compose->replyinfo) {
1486 info = compose->replyinfo;
1487 } else if (compose->fwdinfo) {
1488 info = compose->fwdinfo;
1489 } else if (compose->targetinfo) {
1490 info = compose->targetinfo;
1493 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1494 MimeInfo *partinfo = mimeinfo;
1495 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1496 partinfo = procmime_mimeinfo_next(partinfo);
1498 compose->orig_charset =
1499 g_strdup(procmime_mimeinfo_get_parameter(
1500 partinfo, "charset"));
1502 procmime_mimeinfo_free_all(mimeinfo);
1506 #define SIGNAL_BLOCK(buffer) { \
1507 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1508 G_CALLBACK(compose_changed_cb), \
1510 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1511 G_CALLBACK(text_inserted), \
1515 #define SIGNAL_UNBLOCK(buffer) { \
1516 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1517 G_CALLBACK(compose_changed_cb), \
1519 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1520 G_CALLBACK(text_inserted), \
1524 static Compose *compose_generic_reply(MsgInfo *msginfo,
1525 ComposeQuoteMode quote_mode,
1526 gboolean to_all, gboolean to_ml,
1528 gboolean followup_and_reply_to,
1532 PrefsAccount *account = NULL;
1533 GtkTextView *textview;
1534 GtkTextBuffer *textbuf;
1535 gboolean quote = FALSE;
1536 const gchar *qmark = NULL;
1537 const gchar *body_fmt = NULL;
1538 gchar *s_system = NULL;
1540 cm_return_val_if_fail(msginfo != NULL, NULL);
1541 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1543 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1545 cm_return_val_if_fail(account != NULL, NULL);
1547 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1549 compose->updating = TRUE;
1551 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1552 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1554 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1555 if (!compose->replyinfo)
1556 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1558 compose_extract_original_charset(compose);
1560 if (msginfo->folder && msginfo->folder->ret_rcpt)
1561 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1563 /* Set save folder */
1564 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1565 gchar *folderidentifier;
1567 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1568 folderidentifier = folder_item_get_identifier(msginfo->folder);
1569 compose_set_save_to(compose, folderidentifier);
1570 g_free(folderidentifier);
1573 if (compose_parse_header(compose, msginfo) < 0) {
1574 compose->updating = FALSE;
1575 compose_destroy(compose);
1579 /* override from name according to folder properties */
1580 if (msginfo->folder && msginfo->folder->prefs &&
1581 msginfo->folder->prefs->reply_with_format &&
1582 msginfo->folder->prefs->reply_override_from_format &&
1583 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1588 /* decode \-escape sequences in the internal representation of the quote format */
1589 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1590 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1593 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1594 compose->gtkaspell);
1596 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1598 quote_fmt_scan_string(tmp);
1601 buf = quote_fmt_get_buffer();
1603 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1605 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1606 quote_fmt_reset_vartable();
1611 textview = (GTK_TEXT_VIEW(compose->text));
1612 textbuf = gtk_text_view_get_buffer(textview);
1613 compose_create_tags(textview, compose);
1615 undo_block(compose->undostruct);
1617 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1618 gtkaspell_block_check(compose->gtkaspell);
1621 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1622 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1623 /* use the reply format of folder (if enabled), or the account's one
1624 (if enabled) or fallback to the global reply format, which is always
1625 enabled (even if empty), and use the relevant quotemark */
1627 if (msginfo->folder && msginfo->folder->prefs &&
1628 msginfo->folder->prefs->reply_with_format) {
1629 qmark = msginfo->folder->prefs->reply_quotemark;
1630 body_fmt = msginfo->folder->prefs->reply_body_format;
1632 } else if (account->reply_with_format) {
1633 qmark = account->reply_quotemark;
1634 body_fmt = account->reply_body_format;
1637 qmark = prefs_common.quotemark;
1638 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1639 body_fmt = gettext(prefs_common.quotefmt);
1646 /* empty quotemark is not allowed */
1647 if (qmark == NULL || *qmark == '\0')
1649 compose_quote_fmt(compose, compose->replyinfo,
1650 body_fmt, qmark, body, FALSE, TRUE,
1651 _("The body of the \"Reply\" template has an error at line %d."));
1652 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1653 quote_fmt_reset_vartable();
1656 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1657 compose_force_encryption(compose, account, FALSE, s_system);
1660 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1661 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1662 compose_force_signing(compose, account, s_system);
1666 SIGNAL_BLOCK(textbuf);
1668 if (account->auto_sig)
1669 compose_insert_sig(compose, FALSE);
1671 compose_wrap_all(compose);
1674 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1675 gtkaspell_highlight_all(compose->gtkaspell);
1676 gtkaspell_unblock_check(compose->gtkaspell);
1678 SIGNAL_UNBLOCK(textbuf);
1680 gtk_widget_grab_focus(compose->text);
1682 undo_unblock(compose->undostruct);
1684 if (prefs_common.auto_exteditor)
1685 compose_exec_ext_editor(compose);
1687 compose->modified = FALSE;
1688 compose_set_title(compose);
1690 compose->updating = FALSE;
1691 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1692 SCROLL_TO_CURSOR(compose);
1694 if (compose->deferred_destroy) {
1695 compose_destroy(compose);
1703 #define INSERT_FW_HEADER(var, hdr) \
1704 if (msginfo->var && *msginfo->var) { \
1705 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1706 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1707 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1710 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1711 gboolean as_attach, const gchar *body,
1712 gboolean no_extedit,
1716 GtkTextView *textview;
1717 GtkTextBuffer *textbuf;
1718 gint cursor_pos = -1;
1721 cm_return_val_if_fail(msginfo != NULL, NULL);
1722 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1725 !(account = compose_guess_forward_account_from_msginfo
1727 account = cur_account;
1729 if (!prefs_common.forward_as_attachment)
1730 mode = COMPOSE_FORWARD_INLINE;
1732 mode = COMPOSE_FORWARD;
1733 compose = compose_create(account, msginfo->folder, mode, batch);
1735 compose->updating = TRUE;
1736 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1737 if (!compose->fwdinfo)
1738 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1740 compose_extract_original_charset(compose);
1742 if (msginfo->subject && *msginfo->subject) {
1743 gchar *buf, *buf2, *p;
1745 buf = p = g_strdup(msginfo->subject);
1746 p += subject_get_prefix_length(p);
1747 memmove(buf, p, strlen(p) + 1);
1749 buf2 = g_strdup_printf("Fw: %s", buf);
1750 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1756 /* override from name according to folder properties */
1757 if (msginfo->folder && msginfo->folder->prefs &&
1758 msginfo->folder->prefs->forward_with_format &&
1759 msginfo->folder->prefs->forward_override_from_format &&
1760 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1764 MsgInfo *full_msginfo = NULL;
1767 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1769 full_msginfo = procmsg_msginfo_copy(msginfo);
1771 /* decode \-escape sequences in the internal representation of the quote format */
1772 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1773 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1776 gtkaspell_block_check(compose->gtkaspell);
1777 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1778 compose->gtkaspell);
1780 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1782 quote_fmt_scan_string(tmp);
1785 buf = quote_fmt_get_buffer();
1787 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1789 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1790 quote_fmt_reset_vartable();
1793 procmsg_msginfo_free(full_msginfo);
1796 textview = GTK_TEXT_VIEW(compose->text);
1797 textbuf = gtk_text_view_get_buffer(textview);
1798 compose_create_tags(textview, compose);
1800 undo_block(compose->undostruct);
1804 msgfile = procmsg_get_message_file(msginfo);
1805 if (!is_file_exist(msgfile))
1806 g_warning("%s: file not exist\n", msgfile);
1808 compose_attach_append(compose, msgfile, msgfile,
1809 "message/rfc822", NULL);
1813 const gchar *qmark = NULL;
1814 const gchar *body_fmt = NULL;
1815 MsgInfo *full_msginfo;
1817 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1819 full_msginfo = procmsg_msginfo_copy(msginfo);
1821 /* use the forward format of folder (if enabled), or the account's one
1822 (if enabled) or fallback to the global forward format, which is always
1823 enabled (even if empty), and use the relevant quotemark */
1824 if (msginfo->folder && msginfo->folder->prefs &&
1825 msginfo->folder->prefs->forward_with_format) {
1826 qmark = msginfo->folder->prefs->forward_quotemark;
1827 body_fmt = msginfo->folder->prefs->forward_body_format;
1829 } else if (account->forward_with_format) {
1830 qmark = account->forward_quotemark;
1831 body_fmt = account->forward_body_format;
1834 qmark = prefs_common.fw_quotemark;
1835 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1836 body_fmt = gettext(prefs_common.fw_quotefmt);
1841 /* empty quotemark is not allowed */
1842 if (qmark == NULL || *qmark == '\0')
1845 compose_quote_fmt(compose, full_msginfo,
1846 body_fmt, qmark, body, FALSE, TRUE,
1847 _("The body of the \"Forward\" template has an error at line %d."));
1848 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1849 quote_fmt_reset_vartable();
1850 compose_attach_parts(compose, msginfo);
1852 procmsg_msginfo_free(full_msginfo);
1855 SIGNAL_BLOCK(textbuf);
1857 if (account->auto_sig)
1858 compose_insert_sig(compose, FALSE);
1860 compose_wrap_all(compose);
1863 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1864 gtkaspell_highlight_all(compose->gtkaspell);
1865 gtkaspell_unblock_check(compose->gtkaspell);
1867 SIGNAL_UNBLOCK(textbuf);
1869 cursor_pos = quote_fmt_get_cursor_pos();
1870 if (cursor_pos == -1)
1871 gtk_widget_grab_focus(compose->header_last->entry);
1873 gtk_widget_grab_focus(compose->text);
1875 if (!no_extedit && prefs_common.auto_exteditor)
1876 compose_exec_ext_editor(compose);
1879 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1880 gchar *folderidentifier;
1882 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1883 folderidentifier = folder_item_get_identifier(msginfo->folder);
1884 compose_set_save_to(compose, folderidentifier);
1885 g_free(folderidentifier);
1888 undo_unblock(compose->undostruct);
1890 compose->modified = FALSE;
1891 compose_set_title(compose);
1893 compose->updating = FALSE;
1894 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1895 SCROLL_TO_CURSOR(compose);
1897 if (compose->deferred_destroy) {
1898 compose_destroy(compose);
1902 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1907 #undef INSERT_FW_HEADER
1909 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1912 GtkTextView *textview;
1913 GtkTextBuffer *textbuf;
1917 gboolean single_mail = TRUE;
1919 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1921 if (g_slist_length(msginfo_list) > 1)
1922 single_mail = FALSE;
1924 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1925 if (((MsgInfo *)msginfo->data)->folder == NULL)
1928 /* guess account from first selected message */
1930 !(account = compose_guess_forward_account_from_msginfo
1931 (msginfo_list->data)))
1932 account = cur_account;
1934 cm_return_val_if_fail(account != NULL, NULL);
1936 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1937 if (msginfo->data) {
1938 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1939 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1943 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1944 g_warning("no msginfo_list");
1948 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1950 compose->updating = TRUE;
1952 /* override from name according to folder properties */
1953 if (msginfo_list->data) {
1954 MsgInfo *msginfo = msginfo_list->data;
1956 if (msginfo->folder && msginfo->folder->prefs &&
1957 msginfo->folder->prefs->forward_with_format &&
1958 msginfo->folder->prefs->forward_override_from_format &&
1959 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1964 /* decode \-escape sequences in the internal representation of the quote format */
1965 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1966 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1969 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1970 compose->gtkaspell);
1972 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1974 quote_fmt_scan_string(tmp);
1977 buf = quote_fmt_get_buffer();
1979 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1981 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1982 quote_fmt_reset_vartable();
1988 textview = GTK_TEXT_VIEW(compose->text);
1989 textbuf = gtk_text_view_get_buffer(textview);
1990 compose_create_tags(textview, compose);
1992 undo_block(compose->undostruct);
1993 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1994 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1996 if (!is_file_exist(msgfile))
1997 g_warning("%s: file not exist\n", msgfile);
1999 compose_attach_append(compose, msgfile, msgfile,
2000 "message/rfc822", NULL);
2005 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2006 if (info->subject && *info->subject) {
2007 gchar *buf, *buf2, *p;
2009 buf = p = g_strdup(info->subject);
2010 p += subject_get_prefix_length(p);
2011 memmove(buf, p, strlen(p) + 1);
2013 buf2 = g_strdup_printf("Fw: %s", buf);
2014 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2020 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2021 _("Fw: multiple emails"));
2024 SIGNAL_BLOCK(textbuf);
2026 if (account->auto_sig)
2027 compose_insert_sig(compose, FALSE);
2029 compose_wrap_all(compose);
2031 SIGNAL_UNBLOCK(textbuf);
2033 gtk_text_buffer_get_start_iter(textbuf, &iter);
2034 gtk_text_buffer_place_cursor(textbuf, &iter);
2036 gtk_widget_grab_focus(compose->header_last->entry);
2037 undo_unblock(compose->undostruct);
2038 compose->modified = FALSE;
2039 compose_set_title(compose);
2041 compose->updating = FALSE;
2042 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2043 SCROLL_TO_CURSOR(compose);
2045 if (compose->deferred_destroy) {
2046 compose_destroy(compose);
2050 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2055 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2057 GtkTextIter start = *iter;
2058 GtkTextIter end_iter;
2059 int start_pos = gtk_text_iter_get_offset(&start);
2061 if (!compose->account->sig_sep)
2064 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2065 start_pos+strlen(compose->account->sig_sep));
2067 /* check sig separator */
2068 str = gtk_text_iter_get_text(&start, &end_iter);
2069 if (!strcmp(str, compose->account->sig_sep)) {
2071 /* check end of line (\n) */
2072 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2073 start_pos+strlen(compose->account->sig_sep));
2074 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2075 start_pos+strlen(compose->account->sig_sep)+1);
2076 tmp = gtk_text_iter_get_text(&start, &end_iter);
2077 if (!strcmp(tmp,"\n")) {
2089 static void compose_colorize_signature(Compose *compose)
2091 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2093 GtkTextIter end_iter;
2094 gtk_text_buffer_get_start_iter(buffer, &iter);
2095 while (gtk_text_iter_forward_line(&iter))
2096 if (compose_is_sig_separator(compose, buffer, &iter)) {
2097 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2098 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2102 #define BLOCK_WRAP() { \
2103 prev_autowrap = compose->autowrap; \
2104 buffer = gtk_text_view_get_buffer( \
2105 GTK_TEXT_VIEW(compose->text)); \
2106 compose->autowrap = FALSE; \
2108 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2109 G_CALLBACK(compose_changed_cb), \
2111 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2112 G_CALLBACK(text_inserted), \
2115 #define UNBLOCK_WRAP() { \
2116 compose->autowrap = prev_autowrap; \
2117 if (compose->autowrap) { \
2118 gint old = compose->draft_timeout_tag; \
2119 compose->draft_timeout_tag = -2; \
2120 compose_wrap_all(compose); \
2121 compose->draft_timeout_tag = old; \
2124 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2125 G_CALLBACK(compose_changed_cb), \
2127 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2128 G_CALLBACK(text_inserted), \
2132 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2134 Compose *compose = NULL;
2135 PrefsAccount *account = NULL;
2136 GtkTextView *textview;
2137 GtkTextBuffer *textbuf;
2141 gchar buf[BUFFSIZE];
2142 gboolean use_signing = FALSE;
2143 gboolean use_encryption = FALSE;
2144 gchar *privacy_system = NULL;
2145 int priority = PRIORITY_NORMAL;
2146 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2147 gboolean autowrap = prefs_common.autowrap;
2148 gboolean autoindent = prefs_common.auto_indent;
2149 HeaderEntry *manual_headers = NULL;
2151 cm_return_val_if_fail(msginfo != NULL, NULL);
2152 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2154 if (compose_put_existing_to_front(msginfo)) {
2158 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2159 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2160 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2161 gchar queueheader_buf[BUFFSIZE];
2164 /* Select Account from queue headers */
2165 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2166 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2167 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2168 account = account_find_from_id(id);
2170 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2171 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2172 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2173 account = account_find_from_id(id);
2175 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2176 sizeof(queueheader_buf), "NAID:")) {
2177 id = atoi(&queueheader_buf[strlen("NAID:")]);
2178 account = account_find_from_id(id);
2180 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2181 sizeof(queueheader_buf), "MAID:")) {
2182 id = atoi(&queueheader_buf[strlen("MAID:")]);
2183 account = account_find_from_id(id);
2185 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2186 sizeof(queueheader_buf), "S:")) {
2187 account = account_find_from_address(queueheader_buf, FALSE);
2189 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2190 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2191 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2192 use_signing = param;
2195 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2196 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2197 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2198 use_signing = param;
2201 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2202 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2203 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2204 use_encryption = param;
2206 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2207 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2208 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2209 use_encryption = param;
2211 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2212 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2213 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2216 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2217 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2218 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2221 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2222 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2223 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2225 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2226 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2227 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2229 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2230 sizeof(queueheader_buf), "X-Priority: ")) {
2231 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2234 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2235 sizeof(queueheader_buf), "RMID:")) {
2236 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2237 if (tokens[0] && tokens[1] && tokens[2]) {
2238 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2239 if (orig_item != NULL) {
2240 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2245 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2246 sizeof(queueheader_buf), "FMID:")) {
2247 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2248 if (tokens[0] && tokens[1] && tokens[2]) {
2249 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2250 if (orig_item != NULL) {
2251 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2256 /* Get manual headers */
2257 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2258 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2259 if (*listmh != '\0') {
2260 debug_print("Got manual headers: %s\n", listmh);
2261 manual_headers = procheader_entries_from_str(listmh);
2266 account = msginfo->folder->folder->account;
2269 if (!account && prefs_common.reedit_account_autosel) {
2270 gchar from[BUFFSIZE];
2271 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2272 extract_address(from);
2273 account = account_find_from_address(from, FALSE);
2277 account = cur_account;
2279 cm_return_val_if_fail(account != NULL, NULL);
2281 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2283 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2284 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2285 compose->autowrap = autowrap;
2286 compose->replyinfo = replyinfo;
2287 compose->fwdinfo = fwdinfo;
2289 compose->updating = TRUE;
2290 compose->priority = priority;
2292 if (privacy_system != NULL) {
2293 compose->privacy_system = privacy_system;
2294 compose_use_signing(compose, use_signing);
2295 compose_use_encryption(compose, use_encryption);
2296 compose_update_privacy_system_menu_item(compose, FALSE);
2298 activate_privacy_system(compose, account, FALSE);
2301 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2303 compose_extract_original_charset(compose);
2305 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2306 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2307 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2308 gchar queueheader_buf[BUFFSIZE];
2310 /* Set message save folder */
2311 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2312 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2313 compose_set_save_to(compose, &queueheader_buf[4]);
2315 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2316 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2318 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2323 if (compose_parse_header(compose, msginfo) < 0) {
2324 compose->updating = FALSE;
2325 compose_destroy(compose);
2328 compose_reedit_set_entry(compose, msginfo);
2330 textview = GTK_TEXT_VIEW(compose->text);
2331 textbuf = gtk_text_view_get_buffer(textview);
2332 compose_create_tags(textview, compose);
2334 mark = gtk_text_buffer_get_insert(textbuf);
2335 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2337 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2338 G_CALLBACK(compose_changed_cb),
2341 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2342 fp = procmime_get_first_encrypted_text_content(msginfo);
2344 compose_force_encryption(compose, account, TRUE, NULL);
2347 fp = procmime_get_first_text_content(msginfo);
2350 g_warning("Can't get text part\n");
2354 gboolean prev_autowrap;
2355 GtkTextBuffer *buffer;
2357 while (fgets(buf, sizeof(buf), fp) != NULL) {
2359 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2365 compose_attach_parts(compose, msginfo);
2367 compose_colorize_signature(compose);
2369 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2370 G_CALLBACK(compose_changed_cb),
2373 if (manual_headers != NULL) {
2374 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2375 procheader_entries_free(manual_headers);
2376 compose->updating = FALSE;
2377 compose_destroy(compose);
2380 procheader_entries_free(manual_headers);
2383 gtk_widget_grab_focus(compose->text);
2385 if (prefs_common.auto_exteditor) {
2386 compose_exec_ext_editor(compose);
2388 compose->modified = FALSE;
2389 compose_set_title(compose);
2391 compose->updating = FALSE;
2392 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2393 SCROLL_TO_CURSOR(compose);
2395 if (compose->deferred_destroy) {
2396 compose_destroy(compose);
2400 compose->sig_str = account_get_signature_str(compose->account);
2402 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2407 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2414 cm_return_val_if_fail(msginfo != NULL, NULL);
2417 account = account_get_reply_account(msginfo,
2418 prefs_common.reply_account_autosel);
2419 cm_return_val_if_fail(account != NULL, NULL);
2421 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2423 compose->updating = TRUE;
2425 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2426 compose->replyinfo = NULL;
2427 compose->fwdinfo = NULL;
2429 compose_show_first_last_header(compose, TRUE);
2431 gtk_widget_grab_focus(compose->header_last->entry);
2433 filename = procmsg_get_message_file(msginfo);
2435 if (filename == NULL) {
2436 compose->updating = FALSE;
2437 compose_destroy(compose);
2442 compose->redirect_filename = filename;
2444 /* Set save folder */
2445 item = msginfo->folder;
2446 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2447 gchar *folderidentifier;
2449 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2450 folderidentifier = folder_item_get_identifier(item);
2451 compose_set_save_to(compose, folderidentifier);
2452 g_free(folderidentifier);
2455 compose_attach_parts(compose, msginfo);
2457 if (msginfo->subject)
2458 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2460 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2462 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2463 _("The body of the \"Redirect\" template has an error at line %d."));
2464 quote_fmt_reset_vartable();
2465 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2467 compose_colorize_signature(compose);
2470 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2471 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2472 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2474 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2475 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2476 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2477 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2478 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2479 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2480 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2481 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2483 if (compose->toolbar->draft_btn)
2484 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2485 if (compose->toolbar->insert_btn)
2486 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2487 if (compose->toolbar->attach_btn)
2488 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2489 if (compose->toolbar->sig_btn)
2490 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2491 if (compose->toolbar->exteditor_btn)
2492 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2493 if (compose->toolbar->linewrap_current_btn)
2494 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2495 if (compose->toolbar->linewrap_all_btn)
2496 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2498 compose->modified = FALSE;
2499 compose_set_title(compose);
2500 compose->updating = FALSE;
2501 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2502 SCROLL_TO_CURSOR(compose);
2504 if (compose->deferred_destroy) {
2505 compose_destroy(compose);
2509 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2514 GList *compose_get_compose_list(void)
2516 return compose_list;
2519 void compose_entry_append(Compose *compose, const gchar *address,
2520 ComposeEntryType type, ComposePrefType pref_type)
2522 const gchar *header;
2524 gboolean in_quote = FALSE;
2525 if (!address || *address == '\0') return;
2532 header = N_("Bcc:");
2534 case COMPOSE_REPLYTO:
2535 header = N_("Reply-To:");
2537 case COMPOSE_NEWSGROUPS:
2538 header = N_("Newsgroups:");
2540 case COMPOSE_FOLLOWUPTO:
2541 header = N_( "Followup-To:");
2543 case COMPOSE_INREPLYTO:
2544 header = N_( "In-Reply-To:");
2551 header = prefs_common_translated_header_name(header);
2553 cur = begin = (gchar *)address;
2555 /* we separate the line by commas, but not if we're inside a quoted
2557 while (*cur != '\0') {
2559 in_quote = !in_quote;
2560 if (*cur == ',' && !in_quote) {
2561 gchar *tmp = g_strdup(begin);
2563 tmp[cur-begin]='\0';
2566 while (*tmp == ' ' || *tmp == '\t')
2568 compose_add_header_entry(compose, header, tmp, pref_type);
2575 gchar *tmp = g_strdup(begin);
2577 tmp[cur-begin]='\0';
2578 while (*tmp == ' ' || *tmp == '\t')
2580 compose_add_header_entry(compose, header, tmp, pref_type);
2585 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2587 #if !GTK_CHECK_VERSION(3, 0, 0)
2588 static GdkColor yellow;
2589 static GdkColor black;
2590 static gboolean yellow_initialised = FALSE;
2592 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2593 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2598 #if !GTK_CHECK_VERSION(3, 0, 0)
2599 if (!yellow_initialised) {
2600 gdk_color_parse("#f5f6be", &yellow);
2601 gdk_color_parse("#000000", &black);
2602 yellow_initialised = gdk_colormap_alloc_color(
2603 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2604 yellow_initialised &= gdk_colormap_alloc_color(
2605 gdk_colormap_get_system(), &black, FALSE, TRUE);
2609 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2610 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2611 if (gtk_entry_get_text(entry) &&
2612 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2613 #if !GTK_CHECK_VERSION(3, 0, 0)
2614 if (yellow_initialised) {
2616 gtk_widget_modify_base(
2617 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2618 GTK_STATE_NORMAL, &yellow);
2619 gtk_widget_modify_text(
2620 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2621 GTK_STATE_NORMAL, &black);
2622 #if !GTK_CHECK_VERSION(3, 0, 0)
2629 void compose_toolbar_cb(gint action, gpointer data)
2631 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2632 Compose *compose = (Compose*)toolbar_item->parent;
2634 cm_return_if_fail(compose != NULL);
2638 compose_send_cb(NULL, compose);
2641 compose_send_later_cb(NULL, compose);
2644 compose_draft(compose, COMPOSE_QUIT_EDITING);
2647 compose_insert_file_cb(NULL, compose);
2650 compose_attach_cb(NULL, compose);
2653 compose_insert_sig(compose, FALSE);
2656 compose_ext_editor_cb(NULL, compose);
2658 case A_LINEWRAP_CURRENT:
2659 compose_beautify_paragraph(compose, NULL, TRUE);
2661 case A_LINEWRAP_ALL:
2662 compose_wrap_all_full(compose, TRUE);
2665 compose_address_cb(NULL, compose);
2668 case A_CHECK_SPELLING:
2669 compose_check_all(NULL, compose);
2677 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2682 gchar *subject = NULL;
2686 gchar **attach = NULL;
2687 gchar *inreplyto = NULL;
2688 MailField mfield = NO_FIELD_PRESENT;
2690 /* get mailto parts but skip from */
2691 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2694 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2695 mfield = TO_FIELD_PRESENT;
2698 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2700 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2702 if (!g_utf8_validate (subject, -1, NULL)) {
2703 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2704 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2707 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2709 mfield = SUBJECT_FIELD_PRESENT;
2712 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2713 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2716 gboolean prev_autowrap = compose->autowrap;
2718 compose->autowrap = FALSE;
2720 mark = gtk_text_buffer_get_insert(buffer);
2721 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2723 if (!g_utf8_validate (body, -1, NULL)) {
2724 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2725 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2728 gtk_text_buffer_insert(buffer, &iter, body, -1);
2730 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2732 compose->autowrap = prev_autowrap;
2733 if (compose->autowrap)
2734 compose_wrap_all(compose);
2735 mfield = BODY_FIELD_PRESENT;
2739 gint i = 0, att = 0;
2740 gchar *warn_files = NULL;
2741 while (attach[i] != NULL) {
2742 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2743 if (utf8_filename) {
2744 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2745 gchar *tmp = g_strdup_printf("%s%s\n",
2746 warn_files?warn_files:"",
2752 g_free(utf8_filename);
2754 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2759 alertpanel_notice(ngettext(
2760 "The following file has been attached: \n%s",
2761 "The following files have been attached: \n%s", att), warn_files);
2766 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2779 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2781 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2782 {"Cc:", NULL, TRUE},
2783 {"References:", NULL, FALSE},
2784 {"Bcc:", NULL, TRUE},
2785 {"Newsgroups:", NULL, TRUE},
2786 {"Followup-To:", NULL, TRUE},
2787 {"List-Post:", NULL, FALSE},
2788 {"X-Priority:", NULL, FALSE},
2789 {NULL, NULL, FALSE}};
2805 cm_return_val_if_fail(msginfo != NULL, -1);
2807 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2808 procheader_get_header_fields(fp, hentry);
2811 if (hentry[H_REPLY_TO].body != NULL) {
2812 if (hentry[H_REPLY_TO].body[0] != '\0') {
2814 conv_unmime_header(hentry[H_REPLY_TO].body,
2817 g_free(hentry[H_REPLY_TO].body);
2818 hentry[H_REPLY_TO].body = NULL;
2820 if (hentry[H_CC].body != NULL) {
2821 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2822 g_free(hentry[H_CC].body);
2823 hentry[H_CC].body = NULL;
2825 if (hentry[H_REFERENCES].body != NULL) {
2826 if (compose->mode == COMPOSE_REEDIT)
2827 compose->references = hentry[H_REFERENCES].body;
2829 compose->references = compose_parse_references
2830 (hentry[H_REFERENCES].body, msginfo->msgid);
2831 g_free(hentry[H_REFERENCES].body);
2833 hentry[H_REFERENCES].body = NULL;
2835 if (hentry[H_BCC].body != NULL) {
2836 if (compose->mode == COMPOSE_REEDIT)
2838 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2839 g_free(hentry[H_BCC].body);
2840 hentry[H_BCC].body = NULL;
2842 if (hentry[H_NEWSGROUPS].body != NULL) {
2843 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2844 hentry[H_NEWSGROUPS].body = NULL;
2846 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2847 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2848 compose->followup_to =
2849 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2852 g_free(hentry[H_FOLLOWUP_TO].body);
2853 hentry[H_FOLLOWUP_TO].body = NULL;
2855 if (hentry[H_LIST_POST].body != NULL) {
2856 gchar *to = NULL, *start = NULL;
2858 extract_address(hentry[H_LIST_POST].body);
2859 if (hentry[H_LIST_POST].body[0] != '\0') {
2860 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2862 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2863 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2866 g_free(compose->ml_post);
2867 compose->ml_post = to;
2870 g_free(hentry[H_LIST_POST].body);
2871 hentry[H_LIST_POST].body = NULL;
2874 /* CLAWS - X-Priority */
2875 if (compose->mode == COMPOSE_REEDIT)
2876 if (hentry[H_X_PRIORITY].body != NULL) {
2879 priority = atoi(hentry[H_X_PRIORITY].body);
2880 g_free(hentry[H_X_PRIORITY].body);
2882 hentry[H_X_PRIORITY].body = NULL;
2884 if (priority < PRIORITY_HIGHEST ||
2885 priority > PRIORITY_LOWEST)
2886 priority = PRIORITY_NORMAL;
2888 compose->priority = priority;
2891 if (compose->mode == COMPOSE_REEDIT) {
2892 if (msginfo->inreplyto && *msginfo->inreplyto)
2893 compose->inreplyto = g_strdup(msginfo->inreplyto);
2897 if (msginfo->msgid && *msginfo->msgid)
2898 compose->inreplyto = g_strdup(msginfo->msgid);
2900 if (!compose->references) {
2901 if (msginfo->msgid && *msginfo->msgid) {
2902 if (msginfo->inreplyto && *msginfo->inreplyto)
2903 compose->references =
2904 g_strdup_printf("<%s>\n\t<%s>",
2908 compose->references =
2909 g_strconcat("<", msginfo->msgid, ">",
2911 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2912 compose->references =
2913 g_strconcat("<", msginfo->inreplyto, ">",
2921 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2926 cm_return_val_if_fail(msginfo != NULL, -1);
2928 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2929 procheader_get_header_fields(fp, entries);
2933 while (he != NULL && he->name != NULL) {
2935 GtkListStore *model = NULL;
2937 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
2938 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
2939 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
2940 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
2941 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
2948 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2950 GSList *ref_id_list, *cur;
2954 ref_id_list = references_list_append(NULL, ref);
2955 if (!ref_id_list) return NULL;
2956 if (msgid && *msgid)
2957 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2962 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2963 /* "<" + Message-ID + ">" + CR+LF+TAB */
2964 len += strlen((gchar *)cur->data) + 5;
2966 if (len > MAX_REFERENCES_LEN) {
2967 /* remove second message-ID */
2968 if (ref_id_list && ref_id_list->next &&
2969 ref_id_list->next->next) {
2970 g_free(ref_id_list->next->data);
2971 ref_id_list = g_slist_remove
2972 (ref_id_list, ref_id_list->next->data);
2974 slist_free_strings_full(ref_id_list);
2981 new_ref = g_string_new("");
2982 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2983 if (new_ref->len > 0)
2984 g_string_append(new_ref, "\n\t");
2985 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2988 slist_free_strings_full(ref_id_list);
2990 new_ref_str = new_ref->str;
2991 g_string_free(new_ref, FALSE);
2996 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2997 const gchar *fmt, const gchar *qmark,
2998 const gchar *body, gboolean rewrap,
2999 gboolean need_unescape,
3000 const gchar *err_msg)
3002 MsgInfo* dummyinfo = NULL;
3003 gchar *quote_str = NULL;
3005 gboolean prev_autowrap;
3006 const gchar *trimmed_body = body;
3007 gint cursor_pos = -1;
3008 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3009 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3014 SIGNAL_BLOCK(buffer);
3017 dummyinfo = compose_msginfo_new_from_compose(compose);
3018 msginfo = dummyinfo;
3021 if (qmark != NULL) {
3023 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3024 compose->gtkaspell);
3026 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3028 quote_fmt_scan_string(qmark);
3031 buf = quote_fmt_get_buffer();
3033 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3035 Xstrdup_a(quote_str, buf, goto error)
3038 if (fmt && *fmt != '\0') {
3041 while (*trimmed_body == '\n')
3045 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3046 compose->gtkaspell);
3048 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3050 if (need_unescape) {
3053 /* decode \-escape sequences in the internal representation of the quote format */
3054 tmp = g_malloc(strlen(fmt)+1);
3055 pref_get_unescaped_pref(tmp, fmt);
3056 quote_fmt_scan_string(tmp);
3060 quote_fmt_scan_string(fmt);
3064 buf = quote_fmt_get_buffer();
3066 gint line = quote_fmt_get_line();
3067 alertpanel_error(err_msg, line);
3073 prev_autowrap = compose->autowrap;
3074 compose->autowrap = FALSE;
3076 mark = gtk_text_buffer_get_insert(buffer);
3077 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3078 if (g_utf8_validate(buf, -1, NULL)) {
3079 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3081 gchar *tmpout = NULL;
3082 tmpout = conv_codeset_strdup
3083 (buf, conv_get_locale_charset_str_no_utf8(),
3085 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3087 tmpout = g_malloc(strlen(buf)*2+1);
3088 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3090 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3094 cursor_pos = quote_fmt_get_cursor_pos();
3095 if (cursor_pos == -1)
3096 cursor_pos = gtk_text_iter_get_offset(&iter);
3097 compose->set_cursor_pos = cursor_pos;
3099 gtk_text_buffer_get_start_iter(buffer, &iter);
3100 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3101 gtk_text_buffer_place_cursor(buffer, &iter);
3103 compose->autowrap = prev_autowrap;
3104 if (compose->autowrap && rewrap)
3105 compose_wrap_all(compose);
3112 SIGNAL_UNBLOCK(buffer);
3114 procmsg_msginfo_free( dummyinfo );
3119 /* if ml_post is of type addr@host and from is of type
3120 * addr-anything@host, return TRUE
3122 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3124 gchar *left_ml = NULL;
3125 gchar *right_ml = NULL;
3126 gchar *left_from = NULL;
3127 gchar *right_from = NULL;
3128 gboolean result = FALSE;
3130 if (!ml_post || !from)
3133 left_ml = g_strdup(ml_post);
3134 if (strstr(left_ml, "@")) {
3135 right_ml = strstr(left_ml, "@")+1;
3136 *(strstr(left_ml, "@")) = '\0';
3139 left_from = g_strdup(from);
3140 if (strstr(left_from, "@")) {
3141 right_from = strstr(left_from, "@")+1;
3142 *(strstr(left_from, "@")) = '\0';
3145 if (left_ml && left_from && right_ml && right_from
3146 && !strncmp(left_from, left_ml, strlen(left_ml))
3147 && !strcmp(right_from, right_ml)) {
3156 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3157 gboolean respect_default_to)
3161 if (!folder || !folder->prefs)
3164 if (respect_default_to && folder->prefs->enable_default_to) {
3165 compose_entry_append(compose, folder->prefs->default_to,
3166 COMPOSE_TO, PREF_FOLDER);
3167 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3169 if (folder->prefs->enable_default_cc)
3170 compose_entry_append(compose, folder->prefs->default_cc,
3171 COMPOSE_CC, PREF_FOLDER);
3172 if (folder->prefs->enable_default_bcc)
3173 compose_entry_append(compose, folder->prefs->default_bcc,
3174 COMPOSE_BCC, PREF_FOLDER);
3175 if (folder->prefs->enable_default_replyto)
3176 compose_entry_append(compose, folder->prefs->default_replyto,
3177 COMPOSE_REPLYTO, PREF_FOLDER);
3180 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)