2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2011 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #ifndef USE_NEW_ADDRBOOK
64 #include "addressbook.h"
66 #include "addressbook-dbus.h"
67 #include "addressadd.h"
69 #include "folderview.h"
72 #include "stock_pixmap.h"
73 #include "send_message.h"
76 #include "customheader.h"
77 #include "prefs_common.h"
78 #include "prefs_account.h"
82 #include "procheader.h"
84 #include "statusbar.h"
87 #include "quoted-printable.h"
91 #include "gtkshruler.h"
93 #include "alertpanel.h"
94 #include "manage_window.h"
96 #include "addr_compl.h"
97 #include "quote_fmt.h"
99 #include "foldersel.h"
102 #include "message_search.h"
103 #include "combobox.h"
107 #include "autofaces.h"
108 #include "spell_entry.h"
121 #define N_ATTACH_COLS (N_COL_COLUMNS)
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
129 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
131 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
132 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
134 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
135 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
136 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
137 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
138 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
139 } ComposeCallAdvancedAction;
143 PRIORITY_HIGHEST = 1,
152 COMPOSE_INSERT_SUCCESS,
153 COMPOSE_INSERT_READ_ERROR,
154 COMPOSE_INSERT_INVALID_CHARACTER,
155 COMPOSE_INSERT_NO_FILE
156 } ComposeInsertResult;
160 COMPOSE_WRITE_FOR_SEND,
161 COMPOSE_WRITE_FOR_STORE
166 COMPOSE_QUOTE_FORCED,
173 SUBJECT_FIELD_PRESENT,
178 #define B64_LINE_SIZE 57
179 #define B64_BUFFSIZE 77
181 #define MAX_REFERENCES_LEN 999
183 static GList *compose_list = NULL;
185 static Compose *compose_generic_new (PrefsAccount *account,
189 GList *listAddress );
191 static Compose *compose_create (PrefsAccount *account,
196 static void compose_entry_mark_default_to (Compose *compose,
197 const gchar *address);
198 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
199 ComposeQuoteMode quote_mode,
203 static Compose *compose_forward_multiple (PrefsAccount *account,
204 GSList *msginfo_list);
205 static Compose *compose_reply (MsgInfo *msginfo,
206 ComposeQuoteMode quote_mode,
211 static Compose *compose_reply_mode (ComposeMode mode,
212 GSList *msginfo_list,
214 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
215 static void compose_update_privacy_systems_menu(Compose *compose);
217 static GtkWidget *compose_account_option_menu_create
219 static void compose_set_out_encoding (Compose *compose);
220 static void compose_set_template_menu (Compose *compose);
221 static void compose_destroy (Compose *compose);
223 static MailField compose_entries_set (Compose *compose,
225 ComposeEntryType to_type);
226 static gint compose_parse_header (Compose *compose,
228 static gint compose_parse_manual_headers (Compose *compose,
230 HeaderEntry *entries);
231 static gchar *compose_parse_references (const gchar *ref,
234 static gchar *compose_quote_fmt (Compose *compose,
240 gboolean need_unescape,
241 const gchar *err_msg);
243 static void compose_reply_set_entry (Compose *compose,
249 followup_and_reply_to);
250 static void compose_reedit_set_entry (Compose *compose,
253 static void compose_insert_sig (Compose *compose,
255 static ComposeInsertResult compose_insert_file (Compose *compose,
258 static gboolean compose_attach_append (Compose *compose,
261 const gchar *content_type,
262 const gchar *charset);
263 static void compose_attach_parts (Compose *compose,
266 static gboolean compose_beautify_paragraph (Compose *compose,
267 GtkTextIter *par_iter,
269 static void compose_wrap_all (Compose *compose);
270 static void compose_wrap_all_full (Compose *compose,
273 static void compose_set_title (Compose *compose);
274 static void compose_select_account (Compose *compose,
275 PrefsAccount *account,
278 static PrefsAccount *compose_current_mail_account(void);
279 /* static gint compose_send (Compose *compose); */
280 static gboolean compose_check_for_valid_recipient
282 static gboolean compose_check_entries (Compose *compose,
283 gboolean check_everything);
284 static gint compose_write_to_file (Compose *compose,
287 gboolean attach_parts);
288 static gint compose_write_body_to_file (Compose *compose,
290 static gint compose_remove_reedit_target (Compose *compose,
292 static void compose_remove_draft (Compose *compose);
293 static gint compose_queue_sub (Compose *compose,
297 gboolean check_subject,
298 gboolean remove_reedit_target);
299 static int compose_add_attachments (Compose *compose,
301 static gchar *compose_get_header (Compose *compose);
302 static gchar *compose_get_manual_headers_info (Compose *compose);
304 static void compose_convert_header (Compose *compose,
309 gboolean addr_field);
311 static void compose_attach_info_free (AttachInfo *ainfo);
312 static void compose_attach_remove_selected (GtkAction *action,
315 static void compose_template_apply (Compose *compose,
318 static void compose_attach_property (GtkAction *action,
320 static void compose_attach_property_create (gboolean *cancelled);
321 static void attach_property_ok (GtkWidget *widget,
322 gboolean *cancelled);
323 static void attach_property_cancel (GtkWidget *widget,
324 gboolean *cancelled);
325 static gint attach_property_delete_event (GtkWidget *widget,
327 gboolean *cancelled);
328 static gboolean attach_property_key_pressed (GtkWidget *widget,
330 gboolean *cancelled);
332 static void compose_exec_ext_editor (Compose *compose);
334 static gint compose_exec_ext_editor_real (const gchar *file);
335 static gboolean compose_ext_editor_kill (Compose *compose);
336 static gboolean compose_input_cb (GIOChannel *source,
337 GIOCondition condition,
339 static void compose_set_ext_editor_sensitive (Compose *compose,
341 #endif /* G_OS_UNIX */
343 static void compose_undo_state_changed (UndoMain *undostruct,
348 static void compose_create_header_entry (Compose *compose);
349 static void compose_add_header_entry (Compose *compose, const gchar *header,
350 gchar *text, ComposePrefType pref_type);
351 static void compose_remove_header_entries(Compose *compose);
353 static void compose_update_priority_menu_item(Compose * compose);
355 static void compose_spell_menu_changed (void *data);
356 static void compose_dict_changed (void *data);
358 static void compose_add_field_list ( Compose *compose,
359 GList *listAddress );
361 /* callback functions */
363 static void compose_notebook_size_alloc (GtkNotebook *notebook,
364 GtkAllocation *allocation,
366 static gboolean compose_edit_size_alloc (GtkEditable *widget,
367 GtkAllocation *allocation,
368 GtkSHRuler *shruler);
369 static void account_activated (GtkComboBox *optmenu,
371 static void attach_selected (GtkTreeView *tree_view,
372 GtkTreePath *tree_path,
373 GtkTreeViewColumn *column,
375 static gboolean attach_button_pressed (GtkWidget *widget,
376 GdkEventButton *event,
378 static gboolean attach_key_pressed (GtkWidget *widget,
381 static void compose_send_cb (GtkAction *action, gpointer data);
382 static void compose_send_later_cb (GtkAction *action, gpointer data);
384 static void compose_save_cb (GtkAction *action,
387 static void compose_attach_cb (GtkAction *action,
389 static void compose_insert_file_cb (GtkAction *action,
391 static void compose_insert_sig_cb (GtkAction *action,
394 static void compose_close_cb (GtkAction *action,
396 static void compose_print_cb (GtkAction *action,
399 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
401 static void compose_address_cb (GtkAction *action,
403 static void about_show_cb (GtkAction *action,
405 static void compose_template_activate_cb(GtkWidget *widget,
408 static void compose_ext_editor_cb (GtkAction *action,
411 static gint compose_delete_cb (GtkWidget *widget,
415 static void compose_undo_cb (GtkAction *action,
417 static void compose_redo_cb (GtkAction *action,
419 static void compose_cut_cb (GtkAction *action,
421 static void compose_copy_cb (GtkAction *action,
423 static void compose_paste_cb (GtkAction *action,
425 static void compose_paste_as_quote_cb (GtkAction *action,
427 static void compose_paste_no_wrap_cb (GtkAction *action,
429 static void compose_paste_wrap_cb (GtkAction *action,
431 static void compose_allsel_cb (GtkAction *action,
434 static void compose_advanced_action_cb (GtkAction *action,
437 static void compose_grab_focus_cb (GtkWidget *widget,
440 static void compose_changed_cb (GtkTextBuffer *textbuf,
443 static void compose_wrap_cb (GtkAction *action,
445 static void compose_wrap_all_cb (GtkAction *action,
447 static void compose_find_cb (GtkAction *action,
449 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
451 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
454 static void compose_toggle_ruler_cb (GtkToggleAction *action,
456 static void compose_toggle_sign_cb (GtkToggleAction *action,
458 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
460 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
461 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
462 static void activate_privacy_system (Compose *compose,
463 PrefsAccount *account,
465 static void compose_use_signing(Compose *compose, gboolean use_signing);
466 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
467 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
469 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
471 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
472 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
473 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
475 static void compose_attach_drag_received_cb (GtkWidget *widget,
476 GdkDragContext *drag_context,
479 GtkSelectionData *data,
483 static void compose_insert_drag_received_cb (GtkWidget *widget,
484 GdkDragContext *drag_context,
487 GtkSelectionData *data,
491 static void compose_header_drag_received_cb (GtkWidget *widget,
492 GdkDragContext *drag_context,
495 GtkSelectionData *data,
500 static gboolean compose_drag_drop (GtkWidget *widget,
501 GdkDragContext *drag_context,
503 guint time, gpointer user_data);
505 static void text_inserted (GtkTextBuffer *buffer,
510 static Compose *compose_generic_reply(MsgInfo *msginfo,
511 ComposeQuoteMode quote_mode,
515 gboolean followup_and_reply_to,
518 static void compose_headerentry_changed_cb (GtkWidget *entry,
519 ComposeHeaderEntry *headerentry);
520 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
522 ComposeHeaderEntry *headerentry);
523 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
524 ComposeHeaderEntry *headerentry);
526 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
528 static void compose_allow_user_actions (Compose *compose, gboolean allow);
530 static void compose_nothing_cb (GtkAction *action, gpointer data)
536 static void compose_check_all (GtkAction *action, gpointer data);
537 static void compose_highlight_all (GtkAction *action, gpointer data);
538 static void compose_check_backwards (GtkAction *action, gpointer data);
539 static void compose_check_forwards_go (GtkAction *action, gpointer data);
542 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
544 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
547 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
548 FolderItem *folder_item);
550 static void compose_attach_update_label(Compose *compose);
551 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
552 gboolean respect_default_to);
554 static GtkActionEntry compose_popup_entries[] =
556 {"Compose", NULL, "Compose" },
557 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
558 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
559 {"Compose/---", NULL, "---", NULL, NULL, NULL },
560 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
563 static GtkActionEntry compose_entries[] =
565 {"Menu", NULL, "Menu" },
567 {"Message", NULL, N_("_Message") },
568 {"Edit", NULL, N_("_Edit") },
570 {"Spelling", NULL, N_("_Spelling") },
572 {"Options", NULL, N_("_Options") },
573 {"Tools", NULL, N_("_Tools") },
574 {"Help", NULL, N_("_Help") },
576 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
577 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
578 {"Message/---", NULL, "---" },
580 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
581 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
582 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
583 /* {"Message/---", NULL, "---" }, */
584 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
585 /* {"Message/---", NULL, "---" }, */
586 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
587 /* {"Message/---", NULL, "---" }, */
588 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
591 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
592 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
593 {"Edit/---", NULL, "---" },
595 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
596 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
597 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
599 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
600 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
601 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
602 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
604 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
606 {"Edit/Advanced", NULL, N_("A_dvanced") },
607 {"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*/
608 {"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*/
609 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
610 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
611 {"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*/
612 {"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*/
613 {"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*/
614 {"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*/
615 {"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*/
616 {"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*/
617 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
618 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
619 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
620 {"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*/
622 /* {"Edit/---", NULL, "---" }, */
623 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
625 /* {"Edit/---", NULL, "---" }, */
626 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
627 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
628 /* {"Edit/---", NULL, "---" }, */
629 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
632 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
633 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
634 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
635 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
637 {"Spelling/---", NULL, "---" },
638 {"Spelling/Options", NULL, N_("_Options") },
643 {"Options/ReplyMode", NULL, N_("Reply _mode") },
644 {"Options/---", NULL, "---" },
645 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
646 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
648 /* {"Options/---", NULL, "---" }, */
650 {"Options/Priority", NULL, N_("_Priority") },
652 {"Options/Encoding", NULL, N_("Character _encoding") },
653 {"Options/Encoding/---", NULL, "---" },
654 #define ENC_ACTION(cs_char,c_char,string) \
655 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
657 {"Options/Encoding/Western", NULL, N_("Western European") },
658 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
659 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
660 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
661 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
662 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
663 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
664 {"Options/Encoding/Korean", NULL, N_("Korean") },
665 {"Options/Encoding/Thai", NULL, N_("Thai") },
668 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
670 {"Tools/Template", NULL, N_("_Template") },
671 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
672 {"Tools/Actions", NULL, N_("Actio_ns") },
673 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
676 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
679 static GtkToggleActionEntry compose_toggle_entries[] =
681 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
682 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
683 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
684 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
685 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
686 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
687 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
690 static GtkRadioActionEntry compose_radio_rm_entries[] =
692 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
693 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
694 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
695 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
698 static GtkRadioActionEntry compose_radio_prio_entries[] =
700 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
701 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
702 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
703 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
704 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
707 static GtkRadioActionEntry compose_radio_enc_entries[] =
709 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
729 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
730 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
731 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
732 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
733 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
734 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
735 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
736 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
737 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
738 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
739 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
740 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
743 static GtkTargetEntry compose_mime_types[] =
745 {"text/uri-list", 0, 0},
746 {"UTF8_STRING", 0, 0},
750 static gboolean compose_put_existing_to_front(MsgInfo *info)
752 GList *compose_list = compose_get_compose_list();
756 for (elem = compose_list; elem != NULL && elem->data != NULL;
758 Compose *c = (Compose*)elem->data;
760 if (!c->targetinfo || !c->targetinfo->msgid ||
764 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
765 gtkut_window_popup(c->window);
773 static GdkColor quote_color1 =
774 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
775 static GdkColor quote_color2 =
776 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
777 static GdkColor quote_color3 =
778 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
780 static GdkColor quote_bgcolor1 =
781 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
782 static GdkColor quote_bgcolor2 =
783 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
784 static GdkColor quote_bgcolor3 =
785 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
787 static GdkColor signature_color = {
794 static GdkColor uri_color = {
801 static void compose_create_tags(GtkTextView *text, Compose *compose)
803 GtkTextBuffer *buffer;
804 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
805 #if !GTK_CHECK_VERSION(2, 24, 0)
812 buffer = gtk_text_view_get_buffer(text);
814 if (prefs_common.enable_color) {
815 /* grab the quote colors, converting from an int to a GdkColor */
816 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
818 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
820 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
822 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
824 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
826 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
828 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
830 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
833 signature_color = quote_color1 = quote_color2 = quote_color3 =
834 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
837 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
838 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
839 "foreground-gdk", "e_color1,
840 "paragraph-background-gdk", "e_bgcolor1,
842 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
843 "foreground-gdk", "e_color2,
844 "paragraph-background-gdk", "e_bgcolor2,
846 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
847 "foreground-gdk", "e_color3,
848 "paragraph-background-gdk", "e_bgcolor3,
851 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
852 "foreground-gdk", "e_color1,
854 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
855 "foreground-gdk", "e_color2,
857 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
858 "foreground-gdk", "e_color3,
862 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
863 "foreground-gdk", &signature_color,
866 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
867 "foreground-gdk", &uri_color,
869 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
870 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
872 color[0] = quote_color1;
873 color[1] = quote_color2;
874 color[2] = quote_color3;
875 color[3] = quote_bgcolor1;
876 color[4] = quote_bgcolor2;
877 color[5] = quote_bgcolor3;
878 color[6] = signature_color;
879 color[7] = uri_color;
880 #if !GTK_CHECK_VERSION(2, 24, 0)
881 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
882 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
884 for (i = 0; i < 8; i++) {
885 if (success[i] == FALSE) {
888 g_warning("Compose: color allocation failed.\n");
889 style = gtk_widget_get_style(GTK_WIDGET(text));
890 quote_color1 = quote_color2 = quote_color3 =
891 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
892 signature_color = uri_color = black;
898 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
901 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
904 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
906 return compose_generic_new(account, mailto, item, NULL, NULL);
909 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
911 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
914 #define SCROLL_TO_CURSOR(compose) { \
915 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
916 gtk_text_view_get_buffer( \
917 GTK_TEXT_VIEW(compose->text))); \
918 gtk_text_view_scroll_mark_onscreen( \
919 GTK_TEXT_VIEW(compose->text), \
923 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
926 if (folderidentifier) {
927 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
928 prefs_common.compose_save_to_history = add_history(
929 prefs_common.compose_save_to_history, folderidentifier);
930 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
931 prefs_common.compose_save_to_history);
934 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
935 if (folderidentifier)
936 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
938 gtk_entry_set_text(GTK_ENTRY(entry), "");
941 static gchar *compose_get_save_to(Compose *compose)
944 gchar *result = NULL;
945 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
946 result = gtk_editable_get_chars(entry, 0, -1);
949 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
950 prefs_common.compose_save_to_history = add_history(
951 prefs_common.compose_save_to_history, result);
952 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
953 prefs_common.compose_save_to_history);
958 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
959 GList *attach_files, GList *listAddress )
962 GtkTextView *textview;
963 GtkTextBuffer *textbuf;
965 const gchar *subject_format = NULL;
966 const gchar *body_format = NULL;
967 gchar *mailto_from = NULL;
968 PrefsAccount *mailto_account = NULL;
969 MsgInfo* dummyinfo = NULL;
970 gint cursor_pos = -1;
971 MailField mfield = NO_FIELD_PRESENT;
975 /* check if mailto defines a from */
976 if (mailto && *mailto != '\0') {
977 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
978 /* mailto defines a from, check if we can get account prefs from it,
979 if not, the account prefs will be guessed using other ways, but we'll keep
982 mailto_account = account_find_from_address(mailto_from, TRUE);
984 account = mailto_account;
987 /* if no account prefs set from mailto, set if from folder prefs (if any) */
988 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
989 account = account_find_from_id(item->prefs->default_account);
991 /* if no account prefs set, fallback to the current one */
992 if (!account) account = cur_account;
993 cm_return_val_if_fail(account != NULL, NULL);
995 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
997 /* override from name if mailto asked for it */
999 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1000 g_free(mailto_from);
1002 /* override from name according to folder properties */
1003 if (item && item->prefs &&
1004 item->prefs->compose_with_format &&
1005 item->prefs->compose_override_from_format &&
1006 *item->prefs->compose_override_from_format != '\0') {
1011 dummyinfo = compose_msginfo_new_from_compose(compose);
1013 /* decode \-escape sequences in the internal representation of the quote format */
1014 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1015 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1018 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1019 compose->gtkaspell);
1021 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1023 quote_fmt_scan_string(tmp);
1026 buf = quote_fmt_get_buffer();
1028 alertpanel_error(_("New message From format error."));
1030 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1031 quote_fmt_reset_vartable();
1036 compose->replyinfo = NULL;
1037 compose->fwdinfo = NULL;
1039 textview = GTK_TEXT_VIEW(compose->text);
1040 textbuf = gtk_text_view_get_buffer(textview);
1041 compose_create_tags(textview, compose);
1043 undo_block(compose->undostruct);
1045 compose_set_dictionaries_from_folder_prefs(compose, item);
1048 if (account->auto_sig)
1049 compose_insert_sig(compose, FALSE);
1050 gtk_text_buffer_get_start_iter(textbuf, &iter);
1051 gtk_text_buffer_place_cursor(textbuf, &iter);
1053 if (account->protocol != A_NNTP) {
1054 if (mailto && *mailto != '\0') {
1055 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1058 compose_set_folder_prefs(compose, item, TRUE);
1060 if (item && item->ret_rcpt) {
1061 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1064 if (mailto && *mailto != '\0') {
1065 if (!strchr(mailto, '@'))
1066 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1068 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1069 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1070 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1071 mfield = TO_FIELD_PRESENT;
1074 * CLAWS: just don't allow return receipt request, even if the user
1075 * may want to send an email. simple but foolproof.
1077 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1079 compose_add_field_list( compose, listAddress );
1081 if (item && item->prefs && item->prefs->compose_with_format) {
1082 subject_format = item->prefs->compose_subject_format;
1083 body_format = item->prefs->compose_body_format;
1084 } else if (account->compose_with_format) {
1085 subject_format = account->compose_subject_format;
1086 body_format = account->compose_body_format;
1087 } else if (prefs_common.compose_with_format) {
1088 subject_format = prefs_common.compose_subject_format;
1089 body_format = prefs_common.compose_body_format;
1092 if (subject_format || body_format) {
1095 && *subject_format != '\0' )
1097 gchar *subject = NULL;
1102 dummyinfo = compose_msginfo_new_from_compose(compose);
1104 /* decode \-escape sequences in the internal representation of the quote format */
1105 tmp = g_malloc(strlen(subject_format)+1);
1106 pref_get_unescaped_pref(tmp, subject_format);
1108 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1110 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1111 compose->gtkaspell);
1113 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1115 quote_fmt_scan_string(tmp);
1118 buf = quote_fmt_get_buffer();
1120 alertpanel_error(_("New message subject format error."));
1122 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1123 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1124 quote_fmt_reset_vartable();
1128 mfield = SUBJECT_FIELD_PRESENT;
1132 && *body_format != '\0' )
1135 GtkTextBuffer *buffer;
1136 GtkTextIter start, end;
1140 dummyinfo = compose_msginfo_new_from_compose(compose);
1142 text = GTK_TEXT_VIEW(compose->text);
1143 buffer = gtk_text_view_get_buffer(text);
1144 gtk_text_buffer_get_start_iter(buffer, &start);
1145 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1146 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1148 compose_quote_fmt(compose, dummyinfo,
1150 NULL, tmp, FALSE, TRUE,
1151 _("The body of the \"New message\" template has an error at line %d."));
1152 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1153 quote_fmt_reset_vartable();
1157 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1158 gtkaspell_highlight_all(compose->gtkaspell);
1160 mfield = BODY_FIELD_PRESENT;
1164 procmsg_msginfo_free( dummyinfo );
1170 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1171 ainfo = (AttachInfo *) curr->data;
1172 compose_attach_append(compose, ainfo->file, ainfo->name,
1173 ainfo->content_type, ainfo->charset);
1177 compose_show_first_last_header(compose, TRUE);
1179 /* Set save folder */
1180 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1181 gchar *folderidentifier;
1183 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1184 folderidentifier = folder_item_get_identifier(item);
1185 compose_set_save_to(compose, folderidentifier);
1186 g_free(folderidentifier);
1189 /* Place cursor according to provided input (mfield) */
1191 case NO_FIELD_PRESENT:
1192 if (compose->header_last)
1193 gtk_widget_grab_focus(compose->header_last->entry);
1195 case TO_FIELD_PRESENT:
1196 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1198 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1201 gtk_widget_grab_focus(compose->subject_entry);
1203 case SUBJECT_FIELD_PRESENT:
1204 textview = GTK_TEXT_VIEW(compose->text);
1207 textbuf = gtk_text_view_get_buffer(textview);
1210 mark = gtk_text_buffer_get_insert(textbuf);
1211 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1212 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1214 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1215 * only defers where it comes to the variable body
1216 * is not null. If no body is present compose->text
1217 * will be null in which case you cannot place the
1218 * cursor inside the component so. An empty component
1219 * is therefore created before placing the cursor
1221 case BODY_FIELD_PRESENT:
1222 cursor_pos = quote_fmt_get_cursor_pos();
1223 if (cursor_pos == -1)
1224 gtk_widget_grab_focus(compose->header_last->entry);
1226 gtk_widget_grab_focus(compose->text);
1230 undo_unblock(compose->undostruct);
1232 if (prefs_common.auto_exteditor)
1233 compose_exec_ext_editor(compose);
1235 compose->draft_timeout_tag = -1;
1236 SCROLL_TO_CURSOR(compose);
1238 compose->modified = FALSE;
1239 compose_set_title(compose);
1241 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1246 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1247 gboolean override_pref, const gchar *system)
1249 const gchar *privacy = NULL;
1251 cm_return_if_fail(compose != NULL);
1252 cm_return_if_fail(account != NULL);
1254 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1259 else if (account->default_privacy_system
1260 && strlen(account->default_privacy_system)) {
1261 privacy = account->default_privacy_system;
1263 GSList *privacy_avail = privacy_get_system_ids();
1264 if (privacy_avail && g_slist_length(privacy_avail)) {
1265 privacy = (gchar *)(privacy_avail->data);
1268 if (privacy != NULL) {
1270 g_free(compose->privacy_system);
1271 compose->privacy_system = NULL;
1273 if (compose->privacy_system == NULL)
1274 compose->privacy_system = g_strdup(privacy);
1275 else if (*(compose->privacy_system) == '\0') {
1276 g_free(compose->privacy_system);
1277 compose->privacy_system = g_strdup(privacy);
1279 compose_update_privacy_system_menu_item(compose, FALSE);
1280 compose_use_encryption(compose, TRUE);
1284 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1286 const gchar *privacy = NULL;
1290 else if (account->default_privacy_system
1291 && strlen(account->default_privacy_system)) {
1292 privacy = account->default_privacy_system;
1294 GSList *privacy_avail = privacy_get_system_ids();
1295 if (privacy_avail && g_slist_length(privacy_avail)) {
1296 privacy = (gchar *)(privacy_avail->data);
1300 if (privacy != NULL) {
1302 g_free(compose->privacy_system);
1303 compose->privacy_system = NULL;
1305 if (compose->privacy_system == NULL)
1306 compose->privacy_system = g_strdup(privacy);
1307 compose_update_privacy_system_menu_item(compose, FALSE);
1308 compose_use_signing(compose, TRUE);
1312 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1316 Compose *compose = NULL;
1318 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1320 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1321 cm_return_val_if_fail(msginfo != NULL, NULL);
1323 list_len = g_slist_length(msginfo_list);
1327 case COMPOSE_REPLY_TO_ADDRESS:
1328 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1329 FALSE, prefs_common.default_reply_list, FALSE, body);
1331 case COMPOSE_REPLY_WITH_QUOTE:
1332 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1333 FALSE, prefs_common.default_reply_list, FALSE, body);
1335 case COMPOSE_REPLY_WITHOUT_QUOTE:
1336 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1337 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1339 case COMPOSE_REPLY_TO_SENDER:
1340 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1341 FALSE, FALSE, TRUE, body);
1343 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1344 compose = compose_followup_and_reply_to(msginfo,
1345 COMPOSE_QUOTE_CHECK,
1346 FALSE, FALSE, body);
1348 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1349 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1350 FALSE, FALSE, TRUE, body);
1352 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1353 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1354 FALSE, FALSE, TRUE, NULL);
1356 case COMPOSE_REPLY_TO_ALL:
1357 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1358 TRUE, FALSE, FALSE, body);
1360 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1361 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1362 TRUE, FALSE, FALSE, body);
1364 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1365 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1366 TRUE, FALSE, FALSE, NULL);
1368 case COMPOSE_REPLY_TO_LIST:
1369 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1370 FALSE, TRUE, FALSE, body);
1372 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1373 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1374 FALSE, TRUE, FALSE, body);
1376 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1377 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1378 FALSE, TRUE, FALSE, NULL);
1380 case COMPOSE_FORWARD:
1381 if (prefs_common.forward_as_attachment) {
1382 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1385 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1389 case COMPOSE_FORWARD_INLINE:
1390 /* check if we reply to more than one Message */
1391 if (list_len == 1) {
1392 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1395 /* more messages FALL THROUGH */
1396 case COMPOSE_FORWARD_AS_ATTACH:
1397 compose = compose_forward_multiple(NULL, msginfo_list);
1399 case COMPOSE_REDIRECT:
1400 compose = compose_redirect(NULL, msginfo, FALSE);
1403 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1406 if (compose == NULL) {
1407 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1411 compose->rmode = mode;
1412 switch (compose->rmode) {
1414 case COMPOSE_REPLY_WITH_QUOTE:
1415 case COMPOSE_REPLY_WITHOUT_QUOTE:
1416 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1417 debug_print("reply mode Normal\n");
1418 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1419 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1421 case COMPOSE_REPLY_TO_SENDER:
1422 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1423 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1424 debug_print("reply mode Sender\n");
1425 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1427 case COMPOSE_REPLY_TO_ALL:
1428 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1429 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1430 debug_print("reply mode All\n");
1431 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1433 case COMPOSE_REPLY_TO_LIST:
1434 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1435 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1436 debug_print("reply mode List\n");
1437 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1439 case COMPOSE_REPLY_TO_ADDRESS:
1440 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1448 static Compose *compose_reply(MsgInfo *msginfo,
1449 ComposeQuoteMode quote_mode,
1455 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1456 to_sender, FALSE, body);
1459 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1460 ComposeQuoteMode quote_mode,
1465 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1466 to_sender, TRUE, body);
1469 static void compose_extract_original_charset(Compose *compose)
1471 MsgInfo *info = NULL;
1472 if (compose->replyinfo) {
1473 info = compose->replyinfo;
1474 } else if (compose->fwdinfo) {
1475 info = compose->fwdinfo;
1476 } else if (compose->targetinfo) {
1477 info = compose->targetinfo;
1480 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1481 MimeInfo *partinfo = mimeinfo;
1482 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1483 partinfo = procmime_mimeinfo_next(partinfo);
1485 compose->orig_charset =
1486 g_strdup(procmime_mimeinfo_get_parameter(
1487 partinfo, "charset"));
1489 procmime_mimeinfo_free_all(mimeinfo);
1493 #define SIGNAL_BLOCK(buffer) { \
1494 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1495 G_CALLBACK(compose_changed_cb), \
1497 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1498 G_CALLBACK(text_inserted), \
1502 #define SIGNAL_UNBLOCK(buffer) { \
1503 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1504 G_CALLBACK(compose_changed_cb), \
1506 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1507 G_CALLBACK(text_inserted), \
1511 static Compose *compose_generic_reply(MsgInfo *msginfo,
1512 ComposeQuoteMode quote_mode,
1513 gboolean to_all, gboolean to_ml,
1515 gboolean followup_and_reply_to,
1519 PrefsAccount *account = NULL;
1520 GtkTextView *textview;
1521 GtkTextBuffer *textbuf;
1522 gboolean quote = FALSE;
1523 const gchar *qmark = NULL;
1524 const gchar *body_fmt = NULL;
1525 gchar *s_system = NULL;
1527 cm_return_val_if_fail(msginfo != NULL, NULL);
1528 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1530 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1532 cm_return_val_if_fail(account != NULL, NULL);
1534 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1536 compose->updating = TRUE;
1538 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1539 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1541 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1542 if (!compose->replyinfo)
1543 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1545 compose_extract_original_charset(compose);
1547 if (msginfo->folder && msginfo->folder->ret_rcpt)
1548 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1550 /* Set save folder */
1551 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1552 gchar *folderidentifier;
1554 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1555 folderidentifier = folder_item_get_identifier(msginfo->folder);
1556 compose_set_save_to(compose, folderidentifier);
1557 g_free(folderidentifier);
1560 if (compose_parse_header(compose, msginfo) < 0) {
1561 compose->updating = FALSE;
1562 compose_destroy(compose);
1566 /* override from name according to folder properties */
1567 if (msginfo->folder && msginfo->folder->prefs &&
1568 msginfo->folder->prefs->reply_with_format &&
1569 msginfo->folder->prefs->reply_override_from_format &&
1570 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1575 /* decode \-escape sequences in the internal representation of the quote format */
1576 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1577 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1580 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1581 compose->gtkaspell);
1583 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1585 quote_fmt_scan_string(tmp);
1588 buf = quote_fmt_get_buffer();
1590 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1592 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1593 quote_fmt_reset_vartable();
1598 textview = (GTK_TEXT_VIEW(compose->text));
1599 textbuf = gtk_text_view_get_buffer(textview);
1600 compose_create_tags(textview, compose);
1602 undo_block(compose->undostruct);
1604 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1605 gtkaspell_block_check(compose->gtkaspell);
1608 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1609 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1610 /* use the reply format of folder (if enabled), or the account's one
1611 (if enabled) or fallback to the global reply format, which is always
1612 enabled (even if empty), and use the relevant quotemark */
1614 if (msginfo->folder && msginfo->folder->prefs &&
1615 msginfo->folder->prefs->reply_with_format) {
1616 qmark = msginfo->folder->prefs->reply_quotemark;
1617 body_fmt = msginfo->folder->prefs->reply_body_format;
1619 } else if (account->reply_with_format) {
1620 qmark = account->reply_quotemark;
1621 body_fmt = account->reply_body_format;
1624 qmark = prefs_common.quotemark;
1625 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1626 body_fmt = gettext(prefs_common.quotefmt);
1633 /* empty quotemark is not allowed */
1634 if (qmark == NULL || *qmark == '\0')
1636 compose_quote_fmt(compose, compose->replyinfo,
1637 body_fmt, qmark, body, FALSE, TRUE,
1638 _("The body of the \"Reply\" template has an error at line %d."));
1639 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1640 quote_fmt_reset_vartable();
1643 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1644 compose_force_encryption(compose, account, FALSE, s_system);
1647 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1648 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1649 compose_force_signing(compose, account, s_system);
1653 SIGNAL_BLOCK(textbuf);
1655 if (account->auto_sig)
1656 compose_insert_sig(compose, FALSE);
1658 compose_wrap_all(compose);
1661 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1662 gtkaspell_highlight_all(compose->gtkaspell);
1663 gtkaspell_unblock_check(compose->gtkaspell);
1665 SIGNAL_UNBLOCK(textbuf);
1667 gtk_widget_grab_focus(compose->text);
1669 undo_unblock(compose->undostruct);
1671 if (prefs_common.auto_exteditor)
1672 compose_exec_ext_editor(compose);
1674 compose->modified = FALSE;
1675 compose_set_title(compose);
1677 compose->updating = FALSE;
1678 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1679 SCROLL_TO_CURSOR(compose);
1681 if (compose->deferred_destroy) {
1682 compose_destroy(compose);
1690 #define INSERT_FW_HEADER(var, hdr) \
1691 if (msginfo->var && *msginfo->var) { \
1692 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1693 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1694 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1697 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1698 gboolean as_attach, const gchar *body,
1699 gboolean no_extedit,
1703 GtkTextView *textview;
1704 GtkTextBuffer *textbuf;
1705 gint cursor_pos = -1;
1708 cm_return_val_if_fail(msginfo != NULL, NULL);
1709 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1712 !(account = compose_guess_forward_account_from_msginfo
1714 account = cur_account;
1716 if (!prefs_common.forward_as_attachment)
1717 mode = COMPOSE_FORWARD_INLINE;
1719 mode = COMPOSE_FORWARD;
1720 compose = compose_create(account, msginfo->folder, mode, batch);
1722 compose->updating = TRUE;
1723 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1724 if (!compose->fwdinfo)
1725 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1727 compose_extract_original_charset(compose);
1729 if (msginfo->subject && *msginfo->subject) {
1730 gchar *buf, *buf2, *p;
1732 buf = p = g_strdup(msginfo->subject);
1733 p += subject_get_prefix_length(p);
1734 memmove(buf, p, strlen(p) + 1);
1736 buf2 = g_strdup_printf("Fw: %s", buf);
1737 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1743 /* override from name according to folder properties */
1744 if (msginfo->folder && msginfo->folder->prefs &&
1745 msginfo->folder->prefs->forward_with_format &&
1746 msginfo->folder->prefs->forward_override_from_format &&
1747 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1751 MsgInfo *full_msginfo = NULL;
1754 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1756 full_msginfo = procmsg_msginfo_copy(msginfo);
1758 /* decode \-escape sequences in the internal representation of the quote format */
1759 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1760 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1763 gtkaspell_block_check(compose->gtkaspell);
1764 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1765 compose->gtkaspell);
1767 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1769 quote_fmt_scan_string(tmp);
1772 buf = quote_fmt_get_buffer();
1774 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1776 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1777 quote_fmt_reset_vartable();
1780 procmsg_msginfo_free(full_msginfo);
1783 textview = GTK_TEXT_VIEW(compose->text);
1784 textbuf = gtk_text_view_get_buffer(textview);
1785 compose_create_tags(textview, compose);
1787 undo_block(compose->undostruct);
1791 msgfile = procmsg_get_message_file(msginfo);
1792 if (!is_file_exist(msgfile))
1793 g_warning("%s: file not exist\n", msgfile);
1795 compose_attach_append(compose, msgfile, msgfile,
1796 "message/rfc822", NULL);
1800 const gchar *qmark = NULL;
1801 const gchar *body_fmt = NULL;
1802 MsgInfo *full_msginfo;
1804 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1805 body_fmt = gettext(prefs_common.fw_quotefmt);
1809 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1811 full_msginfo = procmsg_msginfo_copy(msginfo);
1813 /* use the forward format of folder (if enabled), or the account's one
1814 (if enabled) or fallback to the global forward format, which is always
1815 enabled (even if empty), and use the relevant quotemark */
1816 if (msginfo->folder && msginfo->folder->prefs &&
1817 msginfo->folder->prefs->forward_with_format) {
1818 qmark = msginfo->folder->prefs->forward_quotemark;
1819 body_fmt = msginfo->folder->prefs->forward_body_format;
1821 } else if (account->forward_with_format) {
1822 qmark = account->forward_quotemark;
1823 body_fmt = account->forward_body_format;
1826 qmark = prefs_common.fw_quotemark;
1827 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1828 body_fmt = gettext(prefs_common.fw_quotefmt);
1833 /* empty quotemark is not allowed */
1834 if (qmark == NULL || *qmark == '\0')
1837 compose_quote_fmt(compose, full_msginfo,
1838 body_fmt, qmark, body, FALSE, TRUE,
1839 _("The body of the \"Forward\" template has an error at line %d."));
1840 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1841 quote_fmt_reset_vartable();
1842 compose_attach_parts(compose, msginfo);
1844 procmsg_msginfo_free(full_msginfo);
1847 SIGNAL_BLOCK(textbuf);
1849 if (account->auto_sig)
1850 compose_insert_sig(compose, FALSE);
1852 compose_wrap_all(compose);
1855 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1856 gtkaspell_highlight_all(compose->gtkaspell);
1857 gtkaspell_unblock_check(compose->gtkaspell);
1859 SIGNAL_UNBLOCK(textbuf);
1861 cursor_pos = quote_fmt_get_cursor_pos();
1862 if (cursor_pos == -1)
1863 gtk_widget_grab_focus(compose->header_last->entry);
1865 gtk_widget_grab_focus(compose->text);
1867 if (!no_extedit && prefs_common.auto_exteditor)
1868 compose_exec_ext_editor(compose);
1871 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1872 gchar *folderidentifier;
1874 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1875 folderidentifier = folder_item_get_identifier(msginfo->folder);
1876 compose_set_save_to(compose, folderidentifier);
1877 g_free(folderidentifier);
1880 undo_unblock(compose->undostruct);
1882 compose->modified = FALSE;
1883 compose_set_title(compose);
1885 compose->updating = FALSE;
1886 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1887 SCROLL_TO_CURSOR(compose);
1889 if (compose->deferred_destroy) {
1890 compose_destroy(compose);
1894 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1899 #undef INSERT_FW_HEADER
1901 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1904 GtkTextView *textview;
1905 GtkTextBuffer *textbuf;
1909 gboolean single_mail = TRUE;
1911 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1913 if (g_slist_length(msginfo_list) > 1)
1914 single_mail = FALSE;
1916 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1917 if (((MsgInfo *)msginfo->data)->folder == NULL)
1920 /* guess account from first selected message */
1922 !(account = compose_guess_forward_account_from_msginfo
1923 (msginfo_list->data)))
1924 account = cur_account;
1926 cm_return_val_if_fail(account != NULL, NULL);
1928 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1929 if (msginfo->data) {
1930 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1931 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1935 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1936 g_warning("no msginfo_list");
1940 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1942 compose->updating = TRUE;
1944 /* override from name according to folder properties */
1945 if (msginfo_list->data) {
1946 MsgInfo *msginfo = msginfo_list->data;
1948 if (msginfo->folder && msginfo->folder->prefs &&
1949 msginfo->folder->prefs->forward_with_format &&
1950 msginfo->folder->prefs->forward_override_from_format &&
1951 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1956 /* decode \-escape sequences in the internal representation of the quote format */
1957 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1958 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1961 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1962 compose->gtkaspell);
1964 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1966 quote_fmt_scan_string(tmp);
1969 buf = quote_fmt_get_buffer();
1971 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1973 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1974 quote_fmt_reset_vartable();
1980 textview = GTK_TEXT_VIEW(compose->text);
1981 textbuf = gtk_text_view_get_buffer(textview);
1982 compose_create_tags(textview, compose);
1984 undo_block(compose->undostruct);
1985 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1986 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1988 if (!is_file_exist(msgfile))
1989 g_warning("%s: file not exist\n", msgfile);
1991 compose_attach_append(compose, msgfile, msgfile,
1992 "message/rfc822", NULL);
1997 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1998 if (info->subject && *info->subject) {
1999 gchar *buf, *buf2, *p;
2001 buf = p = g_strdup(info->subject);
2002 p += subject_get_prefix_length(p);
2003 memmove(buf, p, strlen(p) + 1);
2005 buf2 = g_strdup_printf("Fw: %s", buf);
2006 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2012 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2013 _("Fw: multiple emails"));
2016 SIGNAL_BLOCK(textbuf);
2018 if (account->auto_sig)
2019 compose_insert_sig(compose, FALSE);
2021 compose_wrap_all(compose);
2023 SIGNAL_UNBLOCK(textbuf);
2025 gtk_text_buffer_get_start_iter(textbuf, &iter);
2026 gtk_text_buffer_place_cursor(textbuf, &iter);
2028 gtk_widget_grab_focus(compose->header_last->entry);
2029 undo_unblock(compose->undostruct);
2030 compose->modified = FALSE;
2031 compose_set_title(compose);
2033 compose->updating = FALSE;
2034 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2035 SCROLL_TO_CURSOR(compose);
2037 if (compose->deferred_destroy) {
2038 compose_destroy(compose);
2042 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2047 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2049 GtkTextIter start = *iter;
2050 GtkTextIter end_iter;
2051 int start_pos = gtk_text_iter_get_offset(&start);
2053 if (!compose->account->sig_sep)
2056 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2057 start_pos+strlen(compose->account->sig_sep));
2059 /* check sig separator */
2060 str = gtk_text_iter_get_text(&start, &end_iter);
2061 if (!strcmp(str, compose->account->sig_sep)) {
2063 /* check end of line (\n) */
2064 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2065 start_pos+strlen(compose->account->sig_sep));
2066 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2067 start_pos+strlen(compose->account->sig_sep)+1);
2068 tmp = gtk_text_iter_get_text(&start, &end_iter);
2069 if (!strcmp(tmp,"\n")) {
2081 static void compose_colorize_signature(Compose *compose)
2083 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2085 GtkTextIter end_iter;
2086 gtk_text_buffer_get_start_iter(buffer, &iter);
2087 while (gtk_text_iter_forward_line(&iter))
2088 if (compose_is_sig_separator(compose, buffer, &iter)) {
2089 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2090 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2094 #define BLOCK_WRAP() { \
2095 prev_autowrap = compose->autowrap; \
2096 buffer = gtk_text_view_get_buffer( \
2097 GTK_TEXT_VIEW(compose->text)); \
2098 compose->autowrap = FALSE; \
2100 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2101 G_CALLBACK(compose_changed_cb), \
2103 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2104 G_CALLBACK(text_inserted), \
2107 #define UNBLOCK_WRAP() { \
2108 compose->autowrap = prev_autowrap; \
2109 if (compose->autowrap) { \
2110 gint old = compose->draft_timeout_tag; \
2111 compose->draft_timeout_tag = -2; \
2112 compose_wrap_all(compose); \
2113 compose->draft_timeout_tag = old; \
2116 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2117 G_CALLBACK(compose_changed_cb), \
2119 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2120 G_CALLBACK(text_inserted), \
2124 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2126 Compose *compose = NULL;
2127 PrefsAccount *account = NULL;
2128 GtkTextView *textview;
2129 GtkTextBuffer *textbuf;
2133 gchar buf[BUFFSIZE];
2134 gboolean use_signing = FALSE;
2135 gboolean use_encryption = FALSE;
2136 gchar *privacy_system = NULL;
2137 int priority = PRIORITY_NORMAL;
2138 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2139 gboolean autowrap = prefs_common.autowrap;
2140 gboolean autoindent = prefs_common.auto_indent;
2141 HeaderEntry *manual_headers = NULL;
2143 cm_return_val_if_fail(msginfo != NULL, NULL);
2144 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2146 if (compose_put_existing_to_front(msginfo)) {
2150 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2151 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2152 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2153 gchar queueheader_buf[BUFFSIZE];
2156 /* Select Account from queue headers */
2157 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2158 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2159 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2160 account = account_find_from_id(id);
2162 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2163 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2164 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2165 account = account_find_from_id(id);
2167 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2168 sizeof(queueheader_buf), "NAID:")) {
2169 id = atoi(&queueheader_buf[strlen("NAID:")]);
2170 account = account_find_from_id(id);
2172 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2173 sizeof(queueheader_buf), "MAID:")) {
2174 id = atoi(&queueheader_buf[strlen("MAID:")]);
2175 account = account_find_from_id(id);
2177 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2178 sizeof(queueheader_buf), "S:")) {
2179 account = account_find_from_address(queueheader_buf, FALSE);
2181 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2182 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2183 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2184 use_signing = param;
2187 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2188 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2189 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2190 use_signing = param;
2193 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2194 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2195 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2196 use_encryption = param;
2198 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2199 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2200 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2201 use_encryption = param;
2203 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2204 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2205 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2208 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2209 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2210 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2213 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2214 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2215 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2217 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2218 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2219 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2221 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2222 sizeof(queueheader_buf), "X-Priority: ")) {
2223 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2226 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2227 sizeof(queueheader_buf), "RMID:")) {
2228 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2229 if (tokens[0] && tokens[1] && tokens[2]) {
2230 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2231 if (orig_item != NULL) {
2232 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2237 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2238 sizeof(queueheader_buf), "FMID:")) {
2239 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2240 if (tokens[0] && tokens[1] && tokens[2]) {
2241 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2242 if (orig_item != NULL) {
2243 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2248 /* Get manual headers */
2249 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2250 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2251 if (*listmh != '\0') {
2252 debug_print("Got manual headers: %s\n", listmh);
2253 manual_headers = procheader_entries_from_str(listmh);
2258 account = msginfo->folder->folder->account;
2261 if (!account && prefs_common.reedit_account_autosel) {
2262 gchar from[BUFFSIZE];
2263 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2264 extract_address(from);
2265 account = account_find_from_address(from, FALSE);
2269 account = cur_account;
2271 cm_return_val_if_fail(account != NULL, NULL);
2273 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2275 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2276 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2277 compose->autowrap = autowrap;
2278 compose->replyinfo = replyinfo;
2279 compose->fwdinfo = fwdinfo;
2281 compose->updating = TRUE;
2282 compose->priority = priority;
2284 if (privacy_system != NULL) {
2285 compose->privacy_system = privacy_system;
2286 compose_use_signing(compose, use_signing);
2287 compose_use_encryption(compose, use_encryption);
2288 compose_update_privacy_system_menu_item(compose, FALSE);
2290 activate_privacy_system(compose, account, FALSE);
2293 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2295 compose_extract_original_charset(compose);
2297 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2298 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2299 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2300 gchar queueheader_buf[BUFFSIZE];
2302 /* Set message save folder */
2303 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2304 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2305 compose_set_save_to(compose, &queueheader_buf[4]);
2307 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2308 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2310 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2315 if (compose_parse_header(compose, msginfo) < 0) {
2316 compose->updating = FALSE;
2317 compose_destroy(compose);
2320 compose_reedit_set_entry(compose, msginfo);
2322 textview = GTK_TEXT_VIEW(compose->text);
2323 textbuf = gtk_text_view_get_buffer(textview);
2324 compose_create_tags(textview, compose);
2326 mark = gtk_text_buffer_get_insert(textbuf);
2327 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2329 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2330 G_CALLBACK(compose_changed_cb),
2333 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2334 fp = procmime_get_first_encrypted_text_content(msginfo);
2336 compose_force_encryption(compose, account, TRUE, NULL);
2339 fp = procmime_get_first_text_content(msginfo);
2342 g_warning("Can't get text part\n");
2346 gboolean prev_autowrap = compose->autowrap;
2347 GtkTextBuffer *buffer = textbuf;
2349 while (fgets(buf, sizeof(buf), fp) != NULL) {
2351 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2357 compose_attach_parts(compose, msginfo);
2359 compose_colorize_signature(compose);
2361 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2362 G_CALLBACK(compose_changed_cb),
2365 if (manual_headers != NULL) {
2366 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2367 procheader_entries_free(manual_headers);
2368 compose->updating = FALSE;
2369 compose_destroy(compose);
2372 procheader_entries_free(manual_headers);
2375 gtk_widget_grab_focus(compose->text);
2377 if (prefs_common.auto_exteditor) {
2378 compose_exec_ext_editor(compose);
2380 compose->modified = FALSE;
2381 compose_set_title(compose);
2383 compose->updating = FALSE;
2384 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2385 SCROLL_TO_CURSOR(compose);
2387 if (compose->deferred_destroy) {
2388 compose_destroy(compose);
2392 compose->sig_str = account_get_signature_str(compose->account);
2394 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2399 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2406 cm_return_val_if_fail(msginfo != NULL, NULL);
2409 account = account_get_reply_account(msginfo,
2410 prefs_common.reply_account_autosel);
2411 cm_return_val_if_fail(account != NULL, NULL);
2413 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2415 compose->updating = TRUE;
2417 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2418 compose->replyinfo = NULL;
2419 compose->fwdinfo = NULL;
2421 compose_show_first_last_header(compose, TRUE);
2423 gtk_widget_grab_focus(compose->header_last->entry);
2425 filename = procmsg_get_message_file(msginfo);
2427 if (filename == NULL) {
2428 compose->updating = FALSE;
2429 compose_destroy(compose);
2434 compose->redirect_filename = filename;
2436 /* Set save folder */
2437 item = msginfo->folder;
2438 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2439 gchar *folderidentifier;
2441 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2442 folderidentifier = folder_item_get_identifier(item);
2443 compose_set_save_to(compose, folderidentifier);
2444 g_free(folderidentifier);
2447 compose_attach_parts(compose, msginfo);
2449 if (msginfo->subject)
2450 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2452 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2454 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2455 _("The body of the \"Redirect\" template has an error at line %d."));
2456 quote_fmt_reset_vartable();
2457 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2459 compose_colorize_signature(compose);
2462 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2463 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2464 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2466 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2467 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2468 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2469 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2470 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2471 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2472 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2473 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2475 if (compose->toolbar->draft_btn)
2476 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2477 if (compose->toolbar->insert_btn)
2478 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2479 if (compose->toolbar->attach_btn)
2480 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2481 if (compose->toolbar->sig_btn)
2482 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2483 if (compose->toolbar->exteditor_btn)
2484 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2485 if (compose->toolbar->linewrap_current_btn)
2486 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2487 if (compose->toolbar->linewrap_all_btn)
2488 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2490 compose->modified = FALSE;
2491 compose_set_title(compose);
2492 compose->updating = FALSE;
2493 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2494 SCROLL_TO_CURSOR(compose);
2496 if (compose->deferred_destroy) {
2497 compose_destroy(compose);
2501 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2506 GList *compose_get_compose_list(void)
2508 return compose_list;
2511 void compose_entry_append(Compose *compose, const gchar *address,
2512 ComposeEntryType type, ComposePrefType pref_type)
2514 const gchar *header;
2516 gboolean in_quote = FALSE;
2517 if (!address || *address == '\0') return;
2524 header = N_("Bcc:");
2526 case COMPOSE_REPLYTO:
2527 header = N_("Reply-To:");
2529 case COMPOSE_NEWSGROUPS:
2530 header = N_("Newsgroups:");
2532 case COMPOSE_FOLLOWUPTO:
2533 header = N_( "Followup-To:");
2535 case COMPOSE_INREPLYTO:
2536 header = N_( "In-Reply-To:");
2543 header = prefs_common_translated_header_name(header);
2545 cur = begin = (gchar *)address;
2547 /* we separate the line by commas, but not if we're inside a quoted
2549 while (*cur != '\0') {
2551 in_quote = !in_quote;
2552 if (*cur == ',' && !in_quote) {
2553 gchar *tmp = g_strdup(begin);
2555 tmp[cur-begin]='\0';
2558 while (*tmp == ' ' || *tmp == '\t')
2560 compose_add_header_entry(compose, header, tmp, pref_type);
2567 gchar *tmp = g_strdup(begin);
2569 tmp[cur-begin]='\0';
2572 while (*tmp == ' ' || *tmp == '\t')
2574 compose_add_header_entry(compose, header, tmp, pref_type);
2579 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2581 #if !GTK_CHECK_VERSION(3, 0, 0)
2582 static GdkColor yellow;
2583 static GdkColor black;
2584 static gboolean yellow_initialised = FALSE;
2586 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2587 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2592 #if !GTK_CHECK_VERSION(3, 0, 0)
2593 if (!yellow_initialised) {
2594 gdk_color_parse("#f5f6be", &yellow);
2595 gdk_color_parse("#000000", &black);
2596 yellow_initialised = gdk_colormap_alloc_color(
2597 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2598 yellow_initialised &= gdk_colormap_alloc_color(
2599 gdk_colormap_get_system(), &black, FALSE, TRUE);
2603 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2604 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2605 if (gtk_entry_get_text(entry) &&
2606 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2607 #if !GTK_CHECK_VERSION(3, 0, 0)
2608 if (yellow_initialised) {
2610 gtk_widget_modify_base(
2611 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2612 GTK_STATE_NORMAL, &yellow);
2613 gtk_widget_modify_text(
2614 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2615 GTK_STATE_NORMAL, &black);
2616 #if !GTK_CHECK_VERSION(3, 0, 0)
2623 void compose_toolbar_cb(gint action, gpointer data)
2625 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2626 Compose *compose = (Compose*)toolbar_item->parent;
2628 cm_return_if_fail(compose != NULL);
2632 compose_send_cb(NULL, compose);
2635 compose_send_later_cb(NULL, compose);
2638 compose_draft(compose, COMPOSE_QUIT_EDITING);
2641 compose_insert_file_cb(NULL, compose);
2644 compose_attach_cb(NULL, compose);
2647 compose_insert_sig(compose, FALSE);
2650 compose_ext_editor_cb(NULL, compose);
2652 case A_LINEWRAP_CURRENT:
2653 compose_beautify_paragraph(compose, NULL, TRUE);
2655 case A_LINEWRAP_ALL:
2656 compose_wrap_all_full(compose, TRUE);
2659 compose_address_cb(NULL, compose);
2662 case A_CHECK_SPELLING:
2663 compose_check_all(NULL, compose);
2671 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2676 gchar *subject = NULL;
2680 gchar **attach = NULL;
2681 gchar *inreplyto = NULL;
2682 MailField mfield = NO_FIELD_PRESENT;
2684 /* get mailto parts but skip from */
2685 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2688 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2689 mfield = TO_FIELD_PRESENT;
2692 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2694 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2696 if (!g_utf8_validate (subject, -1, NULL)) {
2697 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2698 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2701 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2703 mfield = SUBJECT_FIELD_PRESENT;
2706 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2707 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2710 gboolean prev_autowrap = compose->autowrap;
2712 compose->autowrap = FALSE;
2714 mark = gtk_text_buffer_get_insert(buffer);
2715 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2717 if (!g_utf8_validate (body, -1, NULL)) {
2718 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2719 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2722 gtk_text_buffer_insert(buffer, &iter, body, -1);
2724 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2726 compose->autowrap = prev_autowrap;
2727 if (compose->autowrap)
2728 compose_wrap_all(compose);
2729 mfield = BODY_FIELD_PRESENT;
2733 gint i = 0, att = 0;
2734 gchar *warn_files = NULL;
2735 while (attach[i] != NULL) {
2736 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2737 if (utf8_filename) {
2738 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2739 gchar *tmp = g_strdup_printf("%s%s\n",
2740 warn_files?warn_files:"",
2746 g_free(utf8_filename);
2748 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2753 alertpanel_notice(ngettext(
2754 "The following file has been attached: \n%s",
2755 "The following files have been attached: \n%s", att), warn_files);
2760 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2773 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2775 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2776 {"Cc:", NULL, TRUE},
2777 {"References:", NULL, FALSE},
2778 {"Bcc:", NULL, TRUE},
2779 {"Newsgroups:", NULL, TRUE},
2780 {"Followup-To:", NULL, TRUE},
2781 {"List-Post:", NULL, FALSE},
2782 {"X-Priority:", NULL, FALSE},
2783 {NULL, NULL, FALSE}};
2799 cm_return_val_if_fail(msginfo != NULL, -1);
2801 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2802 procheader_get_header_fields(fp, hentry);
2805 if (hentry[H_REPLY_TO].body != NULL) {
2806 if (hentry[H_REPLY_TO].body[0] != '\0') {
2808 conv_unmime_header(hentry[H_REPLY_TO].body,
2811 g_free(hentry[H_REPLY_TO].body);
2812 hentry[H_REPLY_TO].body = NULL;
2814 if (hentry[H_CC].body != NULL) {
2815 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2816 g_free(hentry[H_CC].body);
2817 hentry[H_CC].body = NULL;
2819 if (hentry[H_REFERENCES].body != NULL) {
2820 if (compose->mode == COMPOSE_REEDIT)
2821 compose->references = hentry[H_REFERENCES].body;
2823 compose->references = compose_parse_references
2824 (hentry[H_REFERENCES].body, msginfo->msgid);
2825 g_free(hentry[H_REFERENCES].body);
2827 hentry[H_REFERENCES].body = NULL;
2829 if (hentry[H_BCC].body != NULL) {
2830 if (compose->mode == COMPOSE_REEDIT)
2832 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2833 g_free(hentry[H_BCC].body);
2834 hentry[H_BCC].body = NULL;
2836 if (hentry[H_NEWSGROUPS].body != NULL) {
2837 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2838 hentry[H_NEWSGROUPS].body = NULL;
2840 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2841 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2842 compose->followup_to =
2843 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2846 g_free(hentry[H_FOLLOWUP_TO].body);
2847 hentry[H_FOLLOWUP_TO].body = NULL;
2849 if (hentry[H_LIST_POST].body != NULL) {
2850 gchar *to = NULL, *start = NULL;
2852 extract_address(hentry[H_LIST_POST].body);
2853 if (hentry[H_LIST_POST].body[0] != '\0') {
2854 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2856 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2857 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2860 g_free(compose->ml_post);
2861 compose->ml_post = to;
2864 g_free(hentry[H_LIST_POST].body);
2865 hentry[H_LIST_POST].body = NULL;
2868 /* CLAWS - X-Priority */
2869 if (compose->mode == COMPOSE_REEDIT)
2870 if (hentry[H_X_PRIORITY].body != NULL) {
2873 priority = atoi(hentry[H_X_PRIORITY].body);
2874 g_free(hentry[H_X_PRIORITY].body);
2876 hentry[H_X_PRIORITY].body = NULL;
2878 if (priority < PRIORITY_HIGHEST ||
2879 priority > PRIORITY_LOWEST)
2880 priority = PRIORITY_NORMAL;
2882 compose->priority = priority;
2885 if (compose->mode == COMPOSE_REEDIT) {
2886 if (msginfo->inreplyto && *msginfo->inreplyto)
2887 compose->inreplyto = g_strdup(msginfo->inreplyto);
2891 if (msginfo->msgid && *msginfo->msgid)
2892 compose->inreplyto = g_strdup(msginfo->msgid);
2894 if (!compose->references) {
2895 if (msginfo->msgid && *msginfo->msgid) {
2896 if (msginfo->inreplyto && *msginfo->inreplyto)
2897 compose->references =
2898 g_strdup_printf("<%s>\n\t<%s>",
2902 compose->references =
2903 g_strconcat("<", msginfo->msgid, ">",
2905 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2906 compose->references =
2907 g_strconcat("<", msginfo->inreplyto, ">",
2915 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2920 cm_return_val_if_fail(msginfo != NULL, -1);
2922 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2923 procheader_get_header_fields(fp, entries);
2927 while (he != NULL && he->name != NULL) {
2929 GtkListStore *model = NULL;
2931 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
2932 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
2933 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
2934 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
2935 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
2942 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2944 GSList *ref_id_list, *cur;
2948 ref_id_list = references_list_append(NULL, ref);
2949 if (!ref_id_list) return NULL;
2950 if (msgid && *msgid)
2951 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2956 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2957 /* "<" + Message-ID + ">" + CR+LF+TAB */
2958 len += strlen((gchar *)cur->data) + 5;
2960 if (len > MAX_REFERENCES_LEN) {
2961 /* remove second message-ID */
2962 if (ref_id_list && ref_id_list->next &&
2963 ref_id_list->next->next) {
2964 g_free(ref_id_list->next->data);
2965 ref_id_list = g_slist_remove
2966 (ref_id_list, ref_id_list->next->data);
2968 slist_free_strings(ref_id_list);
2969 g_slist_free(ref_id_list);
2976 new_ref = g_string_new("");
2977 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2978 if (new_ref->len > 0)
2979 g_string_append(new_ref, "\n\t");
2980 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2983 slist_free_strings(ref_id_list);
2984 g_slist_free(ref_id_list);
2986 new_ref_str = new_ref->str;
2987 g_string_free(new_ref, FALSE);
2992 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2993 const gchar *fmt, const gchar *qmark,
2994 const gchar *body, gboolean rewrap,
2995 gboolean need_unescape,
2996 const gchar *err_msg)
2998 MsgInfo* dummyinfo = NULL;
2999 gchar *quote_str = NULL;
3001 gboolean prev_autowrap;
3002 const gchar *trimmed_body = body;
3003 gint cursor_pos = -1;
3004 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3005 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3010 SIGNAL_BLOCK(buffer);
3013 dummyinfo = compose_msginfo_new_from_compose(compose);
3014 msginfo = dummyinfo;
3017 if (qmark != NULL) {
3019 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3020 compose->gtkaspell);
3022 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3024 quote_fmt_scan_string(qmark);
3027 buf = quote_fmt_get_buffer();
3029 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3031 Xstrdup_a(quote_str, buf, goto error)
3034 if (fmt && *fmt != '\0') {
3037 while (*trimmed_body == '\n')
3041 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3042 compose->gtkaspell);
3044 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3046 if (need_unescape) {
3049 /* decode \-escape sequences in the internal representation of the quote format */
3050 tmp = g_malloc(strlen(fmt)+1);
3051 pref_get_unescaped_pref(tmp, fmt);
3052 quote_fmt_scan_string(tmp);
3056 quote_fmt_scan_string(fmt);
3060 buf = quote_fmt_get_buffer();
3062 gint line = quote_fmt_get_line();
3063 alertpanel_error(err_msg, line);
3069 prev_autowrap = compose->autowrap;
3070 compose->autowrap = FALSE;
3072 mark = gtk_text_buffer_get_insert(buffer);
3073 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3074 if (g_utf8_validate(buf, -1, NULL)) {
3075 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3077 gchar *tmpout = NULL;
3078 tmpout = conv_codeset_strdup
3079 (buf, conv_get_locale_charset_str_no_utf8(),
3081 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3083 tmpout = g_malloc(strlen(buf)*2+1);
3084 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3086 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3090 cursor_pos = quote_fmt_get_cursor_pos();
3091 if (cursor_pos == -1)
3092 cursor_pos = gtk_text_iter_get_offset(&iter);
3093 compose->set_cursor_pos = cursor_pos;
3095 gtk_text_buffer_get_start_iter(buffer, &iter);
3096 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3097 gtk_text_buffer_place_cursor(buffer, &iter);
3099 compose->autowrap = prev_autowrap;
3100 if (compose->autowrap && rewrap)
3101 compose_wrap_all(compose);
3108 SIGNAL_UNBLOCK(buffer);
3110 procmsg_msginfo_free( dummyinfo );
3115 /* if ml_post is of type addr@host and from is of type
3116 * addr-anything@host, return TRUE
3118 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3120 gchar *left_ml = NULL;
3121 gchar *right_ml = NULL;
3122 gchar *left_from = NULL;
3123 gchar *right_from = NULL;
3124 gboolean result = FALSE;
3126 if (!ml_post || !from)
3129 left_ml = g_strdup(ml_post);
3130 if (strstr(left_ml, "@")) {
3131 right_ml = strstr(left_ml, "@")+1;
3132 *(strstr(left_ml, "@")) = '\0';
3135 left_from = g_strdup(from);
3136 if (strstr(left_from, "@")) {
3137 right_from = strstr(left_from, "@")+1;
3138 *(strstr(left_from, "@")) = '\0';
3141 if (left_ml && left_from && right_ml && right_from
3142 && !strncmp(left_from, left_ml, strlen(left_ml))
3143 && !strcmp(right_from, right_ml)) {
3152 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3153 gboolean respect_default_to)
3157 if (!folder || !folder->prefs)
3160 if (respect_default_to && folder->prefs->enable_default_to) {
3161 compose_entry_append(compose, folder->prefs->default_to,
3162 COMPOSE_TO, PREF_FOLDER);
3163 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3165 if (folder->prefs->enable_default_cc)
3166 compose_entry_append(compose, folder->prefs->default_cc,
3167 COMPOSE_CC, PREF_FOLDER);
3168 if (folder->prefs->enable_default_bcc)
3169 compose_entry_append(compose, folder->prefs->default_bcc,
3170 COMPOSE_BCC, PREF_FOLDER);
3171 if (folder->prefs->enable_default_replyto)
3172 compose_entry_append(compose, folder->prefs->default_replyto,
3173 COMPOSE_REPLYTO, PREF_FOLDER);
3176 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3181 if (!compose || !msginfo)
3184 if (msginfo->subject && *msginfo->subject) {
3185 buf = p = g_strdup(msginfo->subject);
3186 p += subject_get_prefix_length(p);