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 #include "addressbook.h"
64 #include "folderview.h"
67 #include "stock_pixmap.h"
68 #include "send_message.h"
71 #include "customheader.h"
72 #include "prefs_common.h"
73 #include "prefs_account.h"
77 #include "procheader.h"
79 #include "statusbar.h"
82 #include "quoted-printable.h"
86 #include "gtkshruler.h"
88 #include "alertpanel.h"
89 #include "manage_window.h"
91 #include "addr_compl.h"
92 #include "quote_fmt.h"
94 #include "foldersel.h"
97 #include "message_search.h"
102 #include "autofaces.h"
103 #include "spell_entry.h"
116 #define N_ATTACH_COLS (N_COL_COLUMNS)
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
132 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
134 } ComposeCallAdvancedAction;
138 PRIORITY_HIGHEST = 1,
147 COMPOSE_INSERT_SUCCESS,
148 COMPOSE_INSERT_READ_ERROR,
149 COMPOSE_INSERT_INVALID_CHARACTER,
150 COMPOSE_INSERT_NO_FILE
151 } ComposeInsertResult;
155 COMPOSE_WRITE_FOR_SEND,
156 COMPOSE_WRITE_FOR_STORE
161 COMPOSE_QUOTE_FORCED,
168 SUBJECT_FIELD_PRESENT,
173 #define B64_LINE_SIZE 57
174 #define B64_BUFFSIZE 77
176 #define MAX_REFERENCES_LEN 999
178 static GList *compose_list = NULL;
180 static Compose *compose_generic_new (PrefsAccount *account,
183 GPtrArray *attach_files,
184 GList *listAddress );
186 static Compose *compose_create (PrefsAccount *account,
191 static void compose_entry_mark_default_to (Compose *compose,
192 const gchar *address);
193 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
194 ComposeQuoteMode quote_mode,
198 static Compose *compose_forward_multiple (PrefsAccount *account,
199 GSList *msginfo_list);
200 static Compose *compose_reply (MsgInfo *msginfo,
201 ComposeQuoteMode quote_mode,
206 static Compose *compose_reply_mode (ComposeMode mode,
207 GSList *msginfo_list,
209 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
210 static void compose_update_privacy_systems_menu(Compose *compose);
212 static GtkWidget *compose_account_option_menu_create
214 static void compose_set_out_encoding (Compose *compose);
215 static void compose_set_template_menu (Compose *compose);
216 static void compose_destroy (Compose *compose);
218 static MailField compose_entries_set (Compose *compose,
220 ComposeEntryType to_type);
221 static gint compose_parse_header (Compose *compose,
223 static gint compose_parse_manual_headers (Compose *compose,
225 HeaderEntry *entries);
226 static gchar *compose_parse_references (const gchar *ref,
229 static gchar *compose_quote_fmt (Compose *compose,
235 gboolean need_unescape,
236 const gchar *err_msg);
238 static void compose_reply_set_entry (Compose *compose,
244 followup_and_reply_to);
245 static void compose_reedit_set_entry (Compose *compose,
248 static void compose_insert_sig (Compose *compose,
250 static ComposeInsertResult compose_insert_file (Compose *compose,
253 static gboolean compose_attach_append (Compose *compose,
256 const gchar *content_type,
257 const gchar *charset);
258 static void compose_attach_parts (Compose *compose,
261 static gboolean compose_beautify_paragraph (Compose *compose,
262 GtkTextIter *par_iter,
264 static void compose_wrap_all (Compose *compose);
265 static void compose_wrap_all_full (Compose *compose,
268 static void compose_set_title (Compose *compose);
269 static void compose_select_account (Compose *compose,
270 PrefsAccount *account,
273 static PrefsAccount *compose_current_mail_account(void);
274 /* static gint compose_send (Compose *compose); */
275 static gboolean compose_check_for_valid_recipient
277 static gboolean compose_check_entries (Compose *compose,
278 gboolean check_everything);
279 static gint compose_write_to_file (Compose *compose,
282 gboolean attach_parts);
283 static gint compose_write_body_to_file (Compose *compose,
285 static gint compose_remove_reedit_target (Compose *compose,
287 static void compose_remove_draft (Compose *compose);
288 static gint compose_queue_sub (Compose *compose,
292 gboolean check_subject,
293 gboolean remove_reedit_target);
294 static int compose_add_attachments (Compose *compose,
296 static gchar *compose_get_header (Compose *compose);
297 static gchar *compose_get_manual_headers_info (Compose *compose);
299 static void compose_convert_header (Compose *compose,
304 gboolean addr_field);
306 static void compose_attach_info_free (AttachInfo *ainfo);
307 static void compose_attach_remove_selected (GtkAction *action,
310 static void compose_template_apply (Compose *compose,
313 static void compose_attach_property (GtkAction *action,
315 static void compose_attach_property_create (gboolean *cancelled);
316 static void attach_property_ok (GtkWidget *widget,
317 gboolean *cancelled);
318 static void attach_property_cancel (GtkWidget *widget,
319 gboolean *cancelled);
320 static gint attach_property_delete_event (GtkWidget *widget,
322 gboolean *cancelled);
323 static gboolean attach_property_key_pressed (GtkWidget *widget,
325 gboolean *cancelled);
327 static void compose_exec_ext_editor (Compose *compose);
329 static gint compose_exec_ext_editor_real (const gchar *file);
330 static gboolean compose_ext_editor_kill (Compose *compose);
331 static gboolean compose_input_cb (GIOChannel *source,
332 GIOCondition condition,
334 static void compose_set_ext_editor_sensitive (Compose *compose,
336 #endif /* G_OS_UNIX */
338 static void compose_undo_state_changed (UndoMain *undostruct,
343 static void compose_create_header_entry (Compose *compose);
344 static void compose_add_header_entry (Compose *compose, const gchar *header,
345 gchar *text, ComposePrefType pref_type);
346 static void compose_remove_header_entries(Compose *compose);
348 static void compose_update_priority_menu_item(Compose * compose);
350 static void compose_spell_menu_changed (void *data);
351 static void compose_dict_changed (void *data);
353 static void compose_add_field_list ( Compose *compose,
354 GList *listAddress );
356 /* callback functions */
358 static gboolean compose_edit_size_alloc (GtkEditable *widget,
359 GtkAllocation *allocation,
360 GtkSHRuler *shruler);
361 static void account_activated (GtkComboBox *optmenu,
363 static void attach_selected (GtkTreeView *tree_view,
364 GtkTreePath *tree_path,
365 GtkTreeViewColumn *column,
367 static gboolean attach_button_pressed (GtkWidget *widget,
368 GdkEventButton *event,
370 static gboolean attach_key_pressed (GtkWidget *widget,
373 static void compose_send_cb (GtkAction *action, gpointer data);
374 static void compose_send_later_cb (GtkAction *action, gpointer data);
376 static void compose_save_cb (GtkAction *action,
379 static void compose_attach_cb (GtkAction *action,
381 static void compose_insert_file_cb (GtkAction *action,
383 static void compose_insert_sig_cb (GtkAction *action,
386 static void compose_close_cb (GtkAction *action,
388 static void compose_print_cb (GtkAction *action,
391 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
393 static void compose_address_cb (GtkAction *action,
395 static void about_show_cb (GtkAction *action,
397 static void compose_template_activate_cb(GtkWidget *widget,
400 static void compose_ext_editor_cb (GtkAction *action,
403 static gint compose_delete_cb (GtkWidget *widget,
407 static void compose_undo_cb (GtkAction *action,
409 static void compose_redo_cb (GtkAction *action,
411 static void compose_cut_cb (GtkAction *action,
413 static void compose_copy_cb (GtkAction *action,
415 static void compose_paste_cb (GtkAction *action,
417 static void compose_paste_as_quote_cb (GtkAction *action,
419 static void compose_paste_no_wrap_cb (GtkAction *action,
421 static void compose_paste_wrap_cb (GtkAction *action,
423 static void compose_allsel_cb (GtkAction *action,
426 static void compose_advanced_action_cb (GtkAction *action,
429 static void compose_grab_focus_cb (GtkWidget *widget,
432 static void compose_changed_cb (GtkTextBuffer *textbuf,
435 static void compose_wrap_cb (GtkAction *action,
437 static void compose_wrap_all_cb (GtkAction *action,
439 static void compose_find_cb (GtkAction *action,
441 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
443 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
446 static void compose_toggle_ruler_cb (GtkToggleAction *action,
448 static void compose_toggle_sign_cb (GtkToggleAction *action,
450 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
452 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
453 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
454 static void activate_privacy_system (Compose *compose,
455 PrefsAccount *account,
457 static void compose_use_signing(Compose *compose, gboolean use_signing);
458 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
459 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
461 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
463 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
464 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
465 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
467 static void compose_attach_drag_received_cb (GtkWidget *widget,
468 GdkDragContext *drag_context,
471 GtkSelectionData *data,
475 static void compose_insert_drag_received_cb (GtkWidget *widget,
476 GdkDragContext *drag_context,
479 GtkSelectionData *data,
483 static void compose_header_drag_received_cb (GtkWidget *widget,
484 GdkDragContext *drag_context,
487 GtkSelectionData *data,
492 static gboolean compose_drag_drop (GtkWidget *widget,
493 GdkDragContext *drag_context,
495 guint time, gpointer user_data);
497 static void text_inserted (GtkTextBuffer *buffer,
502 static Compose *compose_generic_reply(MsgInfo *msginfo,
503 ComposeQuoteMode quote_mode,
507 gboolean followup_and_reply_to,
510 static void compose_headerentry_changed_cb (GtkWidget *entry,
511 ComposeHeaderEntry *headerentry);
512 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
514 ComposeHeaderEntry *headerentry);
515 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
516 ComposeHeaderEntry *headerentry);
518 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
520 static void compose_allow_user_actions (Compose *compose, gboolean allow);
522 static void compose_nothing_cb (GtkAction *action, gpointer data)
528 static void compose_check_all (GtkAction *action, gpointer data);
529 static void compose_highlight_all (GtkAction *action, gpointer data);
530 static void compose_check_backwards (GtkAction *action, gpointer data);
531 static void compose_check_forwards_go (GtkAction *action, gpointer data);
534 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
536 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
539 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
540 FolderItem *folder_item);
542 static void compose_attach_update_label(Compose *compose);
543 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
544 gboolean respect_default_to);
546 static GtkActionEntry compose_popup_entries[] =
548 {"Compose", NULL, "Compose" },
549 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
550 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
551 {"Compose/---", NULL, "---", NULL, NULL, NULL },
552 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
555 static GtkActionEntry compose_entries[] =
557 {"Menu", NULL, "Menu" },
559 {"Message", NULL, N_("_Message") },
560 {"Edit", NULL, N_("_Edit") },
562 {"Spelling", NULL, N_("_Spelling") },
564 {"Options", NULL, N_("_Options") },
565 {"Tools", NULL, N_("_Tools") },
566 {"Help", NULL, N_("_Help") },
568 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
569 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
570 {"Message/---", NULL, "---" },
572 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
573 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
574 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
575 /* {"Message/---", NULL, "---" }, */
576 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
577 /* {"Message/---", NULL, "---" }, */
578 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
579 /* {"Message/---", NULL, "---" }, */
580 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
583 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
584 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
585 {"Edit/---", NULL, "---" },
587 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
588 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
589 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
591 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
592 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
593 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
594 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
596 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
598 {"Edit/Advanced", NULL, N_("A_dvanced") },
599 {"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*/
600 {"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*/
601 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
602 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
603 {"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*/
604 {"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*/
605 {"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*/
606 {"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*/
607 {"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*/
608 {"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*/
609 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
610 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
611 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
612 {"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*/
614 /* {"Edit/---", NULL, "---" }, */
615 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
617 /* {"Edit/---", NULL, "---" }, */
618 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
619 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
620 /* {"Edit/---", NULL, "---" }, */
621 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
624 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
625 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
626 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
627 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
629 {"Spelling/---", NULL, "---" },
630 {"Spelling/Options", NULL, N_("_Options") },
635 {"Options/ReplyMode", NULL, N_("Reply _mode") },
636 {"Options/---", NULL, "---" },
637 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
638 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
640 /* {"Options/---", NULL, "---" }, */
642 {"Options/Priority", NULL, N_("_Priority") },
644 {"Options/Encoding", NULL, N_("Character _encoding") },
645 {"Options/Encoding/---", NULL, "---" },
646 #define ENC_ACTION(cs_char,c_char,string) \
647 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
649 {"Options/Encoding/Western", NULL, N_("Western European") },
650 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
651 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
652 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
653 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
654 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
655 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
656 {"Options/Encoding/Korean", NULL, N_("Korean") },
657 {"Options/Encoding/Thai", NULL, N_("Thai") },
660 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
662 {"Tools/Template", NULL, N_("_Template") },
663 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
664 {"Tools/Actions", NULL, N_("Actio_ns") },
665 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
668 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
671 static GtkToggleActionEntry compose_toggle_entries[] =
673 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
674 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
675 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
676 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
677 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
678 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
679 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
682 static GtkRadioActionEntry compose_radio_rm_entries[] =
684 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
685 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
686 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
687 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
690 static GtkRadioActionEntry compose_radio_prio_entries[] =
692 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
693 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
694 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
695 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
696 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
699 static GtkRadioActionEntry compose_radio_enc_entries[] =
701 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
729 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
730 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
731 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
732 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
735 static GtkTargetEntry compose_mime_types[] =
737 {"text/uri-list", 0, 0},
738 {"UTF8_STRING", 0, 0},
742 static gboolean compose_put_existing_to_front(MsgInfo *info)
744 GList *compose_list = compose_get_compose_list();
748 for (elem = compose_list; elem != NULL && elem->data != NULL;
750 Compose *c = (Compose*)elem->data;
752 if (!c->targetinfo || !c->targetinfo->msgid ||
756 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
757 gtkut_window_popup(c->window);
765 static GdkColor quote_color1 =
766 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
767 static GdkColor quote_color2 =
768 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
769 static GdkColor quote_color3 =
770 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
772 static GdkColor quote_bgcolor1 =
773 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
774 static GdkColor quote_bgcolor2 =
775 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
776 static GdkColor quote_bgcolor3 =
777 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
779 static GdkColor signature_color = {
786 static GdkColor uri_color = {
793 static void compose_create_tags(GtkTextView *text, Compose *compose)
795 GtkTextBuffer *buffer;
796 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
797 #if !GTK_CHECK_VERSION(2, 24, 0)
804 buffer = gtk_text_view_get_buffer(text);
806 if (prefs_common.enable_color) {
807 /* grab the quote colors, converting from an int to a GdkColor */
808 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
810 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
812 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
814 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
816 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
818 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
820 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
822 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
825 signature_color = quote_color1 = quote_color2 = quote_color3 =
826 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
829 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
830 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
831 "foreground-gdk", "e_color1,
832 "paragraph-background-gdk", "e_bgcolor1,
834 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
835 "foreground-gdk", "e_color2,
836 "paragraph-background-gdk", "e_bgcolor2,
838 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
839 "foreground-gdk", "e_color3,
840 "paragraph-background-gdk", "e_bgcolor3,
843 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
844 "foreground-gdk", "e_color1,
846 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
847 "foreground-gdk", "e_color2,
849 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
850 "foreground-gdk", "e_color3,
854 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
855 "foreground-gdk", &signature_color,
858 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
859 "foreground-gdk", &uri_color,
861 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
862 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
864 color[0] = quote_color1;
865 color[1] = quote_color2;
866 color[2] = quote_color3;
867 color[3] = quote_bgcolor1;
868 color[4] = quote_bgcolor2;
869 color[5] = quote_bgcolor3;
870 color[6] = signature_color;
871 color[7] = uri_color;
872 #if !GTK_CHECK_VERSION(2, 24, 0)
873 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
874 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
876 for (i = 0; i < 8; i++) {
877 if (success[i] == FALSE) {
880 g_warning("Compose: color allocation failed.\n");
881 style = gtk_widget_get_style(GTK_WIDGET(text));
882 quote_color1 = quote_color2 = quote_color3 =
883 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
884 signature_color = uri_color = black;
890 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
891 GPtrArray *attach_files)
893 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
896 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
898 return compose_generic_new(account, mailto, item, NULL, NULL);
901 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
903 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
906 #define SCROLL_TO_CURSOR(compose) { \
907 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
908 gtk_text_view_get_buffer( \
909 GTK_TEXT_VIEW(compose->text))); \
910 gtk_text_view_scroll_mark_onscreen( \
911 GTK_TEXT_VIEW(compose->text), \
915 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
918 if (folderidentifier) {
919 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
920 prefs_common.compose_save_to_history = add_history(
921 prefs_common.compose_save_to_history, folderidentifier);
922 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
923 prefs_common.compose_save_to_history);
926 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
927 if (folderidentifier)
928 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
930 gtk_entry_set_text(GTK_ENTRY(entry), "");
933 static gchar *compose_get_save_to(Compose *compose)
936 gchar *result = NULL;
937 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
938 result = gtk_editable_get_chars(entry, 0, -1);
941 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
942 prefs_common.compose_save_to_history = add_history(
943 prefs_common.compose_save_to_history, result);
944 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
945 prefs_common.compose_save_to_history);
950 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
951 GPtrArray *attach_files, GList *listAddress )
954 GtkTextView *textview;
955 GtkTextBuffer *textbuf;
957 const gchar *subject_format = NULL;
958 const gchar *body_format = NULL;
959 gchar *mailto_from = NULL;
960 PrefsAccount *mailto_account = NULL;
961 MsgInfo* dummyinfo = NULL;
962 gint cursor_pos = -1;
963 MailField mfield = NO_FIELD_PRESENT;
967 /* check if mailto defines a from */
968 if (mailto && *mailto != '\0') {
969 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
970 /* mailto defines a from, check if we can get account prefs from it,
971 if not, the account prefs will be guessed using other ways, but we'll keep
974 mailto_account = account_find_from_address(mailto_from, TRUE);
976 account = mailto_account;
979 /* if no account prefs set from mailto, set if from folder prefs (if any) */
980 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
981 account = account_find_from_id(item->prefs->default_account);
983 /* if no account prefs set, fallback to the current one */
984 if (!account) account = cur_account;
985 cm_return_val_if_fail(account != NULL, NULL);
987 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
989 /* override from name if mailto asked for it */
991 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
994 /* override from name according to folder properties */
995 if (item && item->prefs &&
996 item->prefs->compose_with_format &&
997 item->prefs->compose_override_from_format &&
998 *item->prefs->compose_override_from_format != '\0') {
1003 dummyinfo = compose_msginfo_new_from_compose(compose);
1005 /* decode \-escape sequences in the internal representation of the quote format */
1006 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1007 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1010 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1011 compose->gtkaspell);
1013 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1015 quote_fmt_scan_string(tmp);
1018 buf = quote_fmt_get_buffer();
1020 alertpanel_error(_("New message From format error."));
1022 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1023 quote_fmt_reset_vartable();
1028 compose->replyinfo = NULL;
1029 compose->fwdinfo = NULL;
1031 textview = GTK_TEXT_VIEW(compose->text);
1032 textbuf = gtk_text_view_get_buffer(textview);
1033 compose_create_tags(textview, compose);
1035 undo_block(compose->undostruct);
1037 compose_set_dictionaries_from_folder_prefs(compose, item);
1040 if (account->auto_sig)
1041 compose_insert_sig(compose, FALSE);
1042 gtk_text_buffer_get_start_iter(textbuf, &iter);
1043 gtk_text_buffer_place_cursor(textbuf, &iter);
1045 if (account->protocol != A_NNTP) {
1046 if (mailto && *mailto != '\0') {
1047 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1050 compose_set_folder_prefs(compose, item, TRUE);
1052 if (item && item->ret_rcpt) {
1053 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1056 if (mailto && *mailto != '\0') {
1057 if (!strchr(mailto, '@'))
1058 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1060 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1061 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1062 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1063 mfield = TO_FIELD_PRESENT;
1066 * CLAWS: just don't allow return receipt request, even if the user
1067 * may want to send an email. simple but foolproof.
1069 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1071 compose_add_field_list( compose, listAddress );
1073 if (item && item->prefs && item->prefs->compose_with_format) {
1074 subject_format = item->prefs->compose_subject_format;
1075 body_format = item->prefs->compose_body_format;
1076 } else if (account->compose_with_format) {
1077 subject_format = account->compose_subject_format;
1078 body_format = account->compose_body_format;
1079 } else if (prefs_common.compose_with_format) {
1080 subject_format = prefs_common.compose_subject_format;
1081 body_format = prefs_common.compose_body_format;
1084 if (subject_format || body_format) {
1087 && *subject_format != '\0' )
1089 gchar *subject = NULL;
1094 dummyinfo = compose_msginfo_new_from_compose(compose);
1096 /* decode \-escape sequences in the internal representation of the quote format */
1097 tmp = g_malloc(strlen(subject_format)+1);
1098 pref_get_unescaped_pref(tmp, subject_format);
1100 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1102 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1103 compose->gtkaspell);
1105 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1107 quote_fmt_scan_string(tmp);
1110 buf = quote_fmt_get_buffer();
1112 alertpanel_error(_("New message subject format error."));
1114 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1115 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1116 quote_fmt_reset_vartable();
1120 mfield = SUBJECT_FIELD_PRESENT;
1124 && *body_format != '\0' )
1127 GtkTextBuffer *buffer;
1128 GtkTextIter start, end;
1132 dummyinfo = compose_msginfo_new_from_compose(compose);
1134 text = GTK_TEXT_VIEW(compose->text);
1135 buffer = gtk_text_view_get_buffer(text);
1136 gtk_text_buffer_get_start_iter(buffer, &start);
1137 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1138 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1140 compose_quote_fmt(compose, dummyinfo,
1142 NULL, tmp, FALSE, TRUE,
1143 _("The body of the \"New message\" template has an error at line %d."));
1144 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1145 quote_fmt_reset_vartable();
1149 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1150 gtkaspell_highlight_all(compose->gtkaspell);
1152 mfield = BODY_FIELD_PRESENT;
1156 procmsg_msginfo_free( dummyinfo );
1162 for (i = 0; i < attach_files->len; i++) {
1163 file = g_ptr_array_index(attach_files, i);
1164 compose_attach_append(compose, file, file, NULL, NULL);
1168 compose_show_first_last_header(compose, TRUE);
1170 /* Set save folder */
1171 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1172 gchar *folderidentifier;
1174 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1175 folderidentifier = folder_item_get_identifier(item);
1176 compose_set_save_to(compose, folderidentifier);
1177 g_free(folderidentifier);
1180 /* Place cursor according to provided input (mfield) */
1182 case NO_FIELD_PRESENT:
1183 if (compose->header_last)
1184 gtk_widget_grab_focus(compose->header_last->entry);
1186 case TO_FIELD_PRESENT:
1187 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1189 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1192 gtk_widget_grab_focus(compose->subject_entry);
1194 case SUBJECT_FIELD_PRESENT:
1195 textview = GTK_TEXT_VIEW(compose->text);
1198 textbuf = gtk_text_view_get_buffer(textview);
1201 mark = gtk_text_buffer_get_insert(textbuf);
1202 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1203 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1205 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1206 * only defers where it comes to the variable body
1207 * is not null. If no body is present compose->text
1208 * will be null in which case you cannot place the
1209 * cursor inside the component so. An empty component
1210 * is therefore created before placing the cursor
1212 case BODY_FIELD_PRESENT:
1213 cursor_pos = quote_fmt_get_cursor_pos();
1214 if (cursor_pos == -1)
1215 gtk_widget_grab_focus(compose->header_last->entry);
1217 gtk_widget_grab_focus(compose->text);
1221 undo_unblock(compose->undostruct);
1223 if (prefs_common.auto_exteditor)
1224 compose_exec_ext_editor(compose);
1226 compose->draft_timeout_tag = -1;
1227 SCROLL_TO_CURSOR(compose);
1229 compose->modified = FALSE;
1230 compose_set_title(compose);
1232 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1237 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1238 gboolean override_pref, const gchar *system)
1240 const gchar *privacy = NULL;
1242 cm_return_if_fail(compose != NULL);
1243 cm_return_if_fail(account != NULL);
1245 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1250 else if (account->default_privacy_system
1251 && strlen(account->default_privacy_system)) {
1252 privacy = account->default_privacy_system;
1254 GSList *privacy_avail = privacy_get_system_ids();
1255 if (privacy_avail && g_slist_length(privacy_avail)) {
1256 privacy = (gchar *)(privacy_avail->data);
1259 if (privacy != NULL) {
1261 g_free(compose->privacy_system);
1262 compose->privacy_system = NULL;
1264 if (compose->privacy_system == NULL)
1265 compose->privacy_system = g_strdup(privacy);
1266 else if (*(compose->privacy_system) == '\0') {
1267 g_free(compose->privacy_system);
1268 compose->privacy_system = g_strdup(privacy);
1270 compose_update_privacy_system_menu_item(compose, FALSE);
1271 compose_use_encryption(compose, TRUE);
1275 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1277 const gchar *privacy = NULL;
1281 else if (account->default_privacy_system
1282 && strlen(account->default_privacy_system)) {
1283 privacy = account->default_privacy_system;
1285 GSList *privacy_avail = privacy_get_system_ids();
1286 if (privacy_avail && g_slist_length(privacy_avail)) {
1287 privacy = (gchar *)(privacy_avail->data);
1291 if (privacy != NULL) {
1293 g_free(compose->privacy_system);
1294 compose->privacy_system = NULL;
1296 if (compose->privacy_system == NULL)
1297 compose->privacy_system = g_strdup(privacy);
1298 compose_update_privacy_system_menu_item(compose, FALSE);
1299 compose_use_signing(compose, TRUE);
1303 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1307 Compose *compose = NULL;
1309 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1311 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1312 cm_return_val_if_fail(msginfo != NULL, NULL);
1314 list_len = g_slist_length(msginfo_list);
1318 case COMPOSE_REPLY_TO_ADDRESS:
1319 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1320 FALSE, prefs_common.default_reply_list, FALSE, body);
1322 case COMPOSE_REPLY_WITH_QUOTE:
1323 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1324 FALSE, prefs_common.default_reply_list, FALSE, body);
1326 case COMPOSE_REPLY_WITHOUT_QUOTE:
1327 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1328 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1330 case COMPOSE_REPLY_TO_SENDER:
1331 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1332 FALSE, FALSE, TRUE, body);
1334 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1335 compose = compose_followup_and_reply_to(msginfo,
1336 COMPOSE_QUOTE_CHECK,
1337 FALSE, FALSE, body);
1339 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1340 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1341 FALSE, FALSE, TRUE, body);
1343 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1344 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1345 FALSE, FALSE, TRUE, NULL);
1347 case COMPOSE_REPLY_TO_ALL:
1348 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1349 TRUE, FALSE, FALSE, body);
1351 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1352 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1353 TRUE, FALSE, FALSE, body);
1355 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1356 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1357 TRUE, FALSE, FALSE, NULL);
1359 case COMPOSE_REPLY_TO_LIST:
1360 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1361 FALSE, TRUE, FALSE, body);
1363 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1364 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1365 FALSE, TRUE, FALSE, body);
1367 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1368 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1369 FALSE, TRUE, FALSE, NULL);
1371 case COMPOSE_FORWARD:
1372 if (prefs_common.forward_as_attachment) {
1373 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1376 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1380 case COMPOSE_FORWARD_INLINE:
1381 /* check if we reply to more than one Message */
1382 if (list_len == 1) {
1383 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1386 /* more messages FALL THROUGH */
1387 case COMPOSE_FORWARD_AS_ATTACH:
1388 compose = compose_forward_multiple(NULL, msginfo_list);
1390 case COMPOSE_REDIRECT:
1391 compose = compose_redirect(NULL, msginfo, FALSE);
1394 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1397 if (compose == NULL) {
1398 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1402 compose->rmode = mode;
1403 switch (compose->rmode) {
1405 case COMPOSE_REPLY_WITH_QUOTE:
1406 case COMPOSE_REPLY_WITHOUT_QUOTE:
1407 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1408 debug_print("reply mode Normal\n");
1409 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1410 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1412 case COMPOSE_REPLY_TO_SENDER:
1413 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1414 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1415 debug_print("reply mode Sender\n");
1416 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1418 case COMPOSE_REPLY_TO_ALL:
1419 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1420 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1421 debug_print("reply mode All\n");
1422 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1424 case COMPOSE_REPLY_TO_LIST:
1425 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1426 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1427 debug_print("reply mode List\n");
1428 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1430 case COMPOSE_REPLY_TO_ADDRESS:
1431 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1439 static Compose *compose_reply(MsgInfo *msginfo,
1440 ComposeQuoteMode quote_mode,
1446 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1447 to_sender, FALSE, body);
1450 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1451 ComposeQuoteMode quote_mode,
1456 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1457 to_sender, TRUE, body);
1460 static void compose_extract_original_charset(Compose *compose)
1462 MsgInfo *info = NULL;
1463 if (compose->replyinfo) {
1464 info = compose->replyinfo;
1465 } else if (compose->fwdinfo) {
1466 info = compose->fwdinfo;
1467 } else if (compose->targetinfo) {
1468 info = compose->targetinfo;
1471 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1472 MimeInfo *partinfo = mimeinfo;
1473 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1474 partinfo = procmime_mimeinfo_next(partinfo);
1476 compose->orig_charset =
1477 g_strdup(procmime_mimeinfo_get_parameter(
1478 partinfo, "charset"));
1480 procmime_mimeinfo_free_all(mimeinfo);
1484 #define SIGNAL_BLOCK(buffer) { \
1485 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1486 G_CALLBACK(compose_changed_cb), \
1488 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1489 G_CALLBACK(text_inserted), \
1493 #define SIGNAL_UNBLOCK(buffer) { \
1494 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1495 G_CALLBACK(compose_changed_cb), \
1497 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1498 G_CALLBACK(text_inserted), \
1502 static Compose *compose_generic_reply(MsgInfo *msginfo,
1503 ComposeQuoteMode quote_mode,
1504 gboolean to_all, gboolean to_ml,
1506 gboolean followup_and_reply_to,
1510 PrefsAccount *account = NULL;
1511 GtkTextView *textview;
1512 GtkTextBuffer *textbuf;
1513 gboolean quote = FALSE;
1514 const gchar *qmark = NULL;
1515 const gchar *body_fmt = NULL;
1516 gchar *s_system = NULL;
1518 cm_return_val_if_fail(msginfo != NULL, NULL);
1519 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1521 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1523 cm_return_val_if_fail(account != NULL, NULL);
1525 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1527 compose->updating = TRUE;
1529 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1530 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1532 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1533 if (!compose->replyinfo)
1534 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1536 compose_extract_original_charset(compose);
1538 if (msginfo->folder && msginfo->folder->ret_rcpt)
1539 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1541 /* Set save folder */
1542 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1543 gchar *folderidentifier;
1545 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1546 folderidentifier = folder_item_get_identifier(msginfo->folder);
1547 compose_set_save_to(compose, folderidentifier);
1548 g_free(folderidentifier);
1551 if (compose_parse_header(compose, msginfo) < 0) {
1552 compose->updating = FALSE;
1553 compose_destroy(compose);
1557 /* override from name according to folder properties */
1558 if (msginfo->folder && msginfo->folder->prefs &&
1559 msginfo->folder->prefs->reply_with_format &&
1560 msginfo->folder->prefs->reply_override_from_format &&
1561 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1566 /* decode \-escape sequences in the internal representation of the quote format */
1567 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1568 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1571 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1572 compose->gtkaspell);
1574 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1576 quote_fmt_scan_string(tmp);
1579 buf = quote_fmt_get_buffer();
1581 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1583 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1584 quote_fmt_reset_vartable();
1589 textview = (GTK_TEXT_VIEW(compose->text));
1590 textbuf = gtk_text_view_get_buffer(textview);
1591 compose_create_tags(textview, compose);
1593 undo_block(compose->undostruct);
1595 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1596 gtkaspell_block_check(compose->gtkaspell);
1599 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1600 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1601 /* use the reply format of folder (if enabled), or the account's one
1602 (if enabled) or fallback to the global reply format, which is always
1603 enabled (even if empty), and use the relevant quotemark */
1605 if (msginfo->folder && msginfo->folder->prefs &&
1606 msginfo->folder->prefs->reply_with_format) {
1607 qmark = msginfo->folder->prefs->reply_quotemark;
1608 body_fmt = msginfo->folder->prefs->reply_body_format;
1610 } else if (account->reply_with_format) {
1611 qmark = account->reply_quotemark;
1612 body_fmt = account->reply_body_format;
1615 qmark = prefs_common.quotemark;
1616 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1617 body_fmt = gettext(prefs_common.quotefmt);
1624 /* empty quotemark is not allowed */
1625 if (qmark == NULL || *qmark == '\0')
1627 compose_quote_fmt(compose, compose->replyinfo,
1628 body_fmt, qmark, body, FALSE, TRUE,
1629 _("The body of the \"Reply\" template has an error at line %d."));
1630 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1631 quote_fmt_reset_vartable();
1634 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1635 compose_force_encryption(compose, account, FALSE, s_system);
1638 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1639 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1640 compose_force_signing(compose, account, s_system);
1644 SIGNAL_BLOCK(textbuf);
1646 if (account->auto_sig)
1647 compose_insert_sig(compose, FALSE);
1649 compose_wrap_all(compose);
1652 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1653 gtkaspell_highlight_all(compose->gtkaspell);
1654 gtkaspell_unblock_check(compose->gtkaspell);
1656 SIGNAL_UNBLOCK(textbuf);
1658 gtk_widget_grab_focus(compose->text);
1660 undo_unblock(compose->undostruct);
1662 if (prefs_common.auto_exteditor)
1663 compose_exec_ext_editor(compose);
1665 compose->modified = FALSE;
1666 compose_set_title(compose);
1668 compose->updating = FALSE;
1669 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1670 SCROLL_TO_CURSOR(compose);
1672 if (compose->deferred_destroy) {
1673 compose_destroy(compose);
1681 #define INSERT_FW_HEADER(var, hdr) \
1682 if (msginfo->var && *msginfo->var) { \
1683 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1684 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1685 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1688 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1689 gboolean as_attach, const gchar *body,
1690 gboolean no_extedit,
1694 GtkTextView *textview;
1695 GtkTextBuffer *textbuf;
1696 gint cursor_pos = -1;
1699 cm_return_val_if_fail(msginfo != NULL, NULL);
1700 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1703 !(account = compose_guess_forward_account_from_msginfo
1705 account = cur_account;
1707 if (!prefs_common.forward_as_attachment)
1708 mode = COMPOSE_FORWARD_INLINE;
1710 mode = COMPOSE_FORWARD;
1711 compose = compose_create(account, msginfo->folder, mode, batch);
1713 compose->updating = TRUE;
1714 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1715 if (!compose->fwdinfo)
1716 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1718 compose_extract_original_charset(compose);
1720 if (msginfo->subject && *msginfo->subject) {
1721 gchar *buf, *buf2, *p;
1723 buf = p = g_strdup(msginfo->subject);
1724 p += subject_get_prefix_length(p);
1725 memmove(buf, p, strlen(p) + 1);
1727 buf2 = g_strdup_printf("Fw: %s", buf);
1728 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1734 /* override from name according to folder properties */
1735 if (msginfo->folder && msginfo->folder->prefs &&
1736 msginfo->folder->prefs->forward_with_format &&
1737 msginfo->folder->prefs->forward_override_from_format &&
1738 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1742 MsgInfo *full_msginfo = NULL;
1745 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1747 full_msginfo = procmsg_msginfo_copy(msginfo);
1749 /* decode \-escape sequences in the internal representation of the quote format */
1750 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1751 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1754 gtkaspell_block_check(compose->gtkaspell);
1755 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1756 compose->gtkaspell);
1758 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1760 quote_fmt_scan_string(tmp);
1763 buf = quote_fmt_get_buffer();
1765 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1767 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1768 quote_fmt_reset_vartable();
1771 procmsg_msginfo_free(full_msginfo);
1774 textview = GTK_TEXT_VIEW(compose->text);
1775 textbuf = gtk_text_view_get_buffer(textview);
1776 compose_create_tags(textview, compose);
1778 undo_block(compose->undostruct);
1782 msgfile = procmsg_get_message_file(msginfo);
1783 if (!is_file_exist(msgfile))
1784 g_warning("%s: file not exist\n", msgfile);
1786 compose_attach_append(compose, msgfile, msgfile,
1787 "message/rfc822", NULL);
1791 const gchar *qmark = NULL;
1792 const gchar *body_fmt = NULL;
1793 MsgInfo *full_msginfo;
1795 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1796 body_fmt = gettext(prefs_common.fw_quotefmt);
1800 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1802 full_msginfo = procmsg_msginfo_copy(msginfo);
1804 /* use the forward format of folder (if enabled), or the account's one
1805 (if enabled) or fallback to the global forward format, which is always
1806 enabled (even if empty), and use the relevant quotemark */
1807 if (msginfo->folder && msginfo->folder->prefs &&
1808 msginfo->folder->prefs->forward_with_format) {
1809 qmark = msginfo->folder->prefs->forward_quotemark;
1810 body_fmt = msginfo->folder->prefs->forward_body_format;
1812 } else if (account->forward_with_format) {
1813 qmark = account->forward_quotemark;
1814 body_fmt = account->forward_body_format;
1817 qmark = prefs_common.fw_quotemark;
1818 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1819 body_fmt = gettext(prefs_common.fw_quotefmt);
1824 /* empty quotemark is not allowed */
1825 if (qmark == NULL || *qmark == '\0')
1828 compose_quote_fmt(compose, full_msginfo,
1829 body_fmt, qmark, body, FALSE, TRUE,
1830 _("The body of the \"Forward\" template has an error at line %d."));
1831 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1832 quote_fmt_reset_vartable();
1833 compose_attach_parts(compose, msginfo);
1835 procmsg_msginfo_free(full_msginfo);
1838 SIGNAL_BLOCK(textbuf);
1840 if (account->auto_sig)
1841 compose_insert_sig(compose, FALSE);
1843 compose_wrap_all(compose);
1846 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1847 gtkaspell_highlight_all(compose->gtkaspell);
1848 gtkaspell_unblock_check(compose->gtkaspell);
1850 SIGNAL_UNBLOCK(textbuf);
1852 cursor_pos = quote_fmt_get_cursor_pos();
1853 if (cursor_pos == -1)
1854 gtk_widget_grab_focus(compose->header_last->entry);
1856 gtk_widget_grab_focus(compose->text);
1858 if (!no_extedit && prefs_common.auto_exteditor)
1859 compose_exec_ext_editor(compose);
1862 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1863 gchar *folderidentifier;
1865 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1866 folderidentifier = folder_item_get_identifier(msginfo->folder);
1867 compose_set_save_to(compose, folderidentifier);
1868 g_free(folderidentifier);
1871 undo_unblock(compose->undostruct);
1873 compose->modified = FALSE;
1874 compose_set_title(compose);
1876 compose->updating = FALSE;
1877 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1878 SCROLL_TO_CURSOR(compose);
1880 if (compose->deferred_destroy) {
1881 compose_destroy(compose);
1885 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1890 #undef INSERT_FW_HEADER
1892 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1895 GtkTextView *textview;
1896 GtkTextBuffer *textbuf;
1900 gboolean single_mail = TRUE;
1902 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1904 if (g_slist_length(msginfo_list) > 1)
1905 single_mail = FALSE;
1907 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1908 if (((MsgInfo *)msginfo->data)->folder == NULL)
1911 /* guess account from first selected message */
1913 !(account = compose_guess_forward_account_from_msginfo
1914 (msginfo_list->data)))
1915 account = cur_account;
1917 cm_return_val_if_fail(account != NULL, NULL);
1919 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1920 if (msginfo->data) {
1921 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1922 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1926 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1927 g_warning("no msginfo_list");
1931 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1933 compose->updating = TRUE;
1935 /* override from name according to folder properties */
1936 if (msginfo_list->data) {
1937 MsgInfo *msginfo = msginfo_list->data;
1939 if (msginfo->folder && msginfo->folder->prefs &&
1940 msginfo->folder->prefs->forward_with_format &&
1941 msginfo->folder->prefs->forward_override_from_format &&
1942 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1947 /* decode \-escape sequences in the internal representation of the quote format */
1948 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1949 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1952 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1953 compose->gtkaspell);
1955 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1957 quote_fmt_scan_string(tmp);
1960 buf = quote_fmt_get_buffer();
1962 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1964 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1965 quote_fmt_reset_vartable();
1971 textview = GTK_TEXT_VIEW(compose->text);
1972 textbuf = gtk_text_view_get_buffer(textview);
1973 compose_create_tags(textview, compose);
1975 undo_block(compose->undostruct);
1976 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1977 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1979 if (!is_file_exist(msgfile))
1980 g_warning("%s: file not exist\n", msgfile);
1982 compose_attach_append(compose, msgfile, msgfile,
1983 "message/rfc822", NULL);
1988 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1989 if (info->subject && *info->subject) {
1990 gchar *buf, *buf2, *p;
1992 buf = p = g_strdup(info->subject);
1993 p += subject_get_prefix_length(p);
1994 memmove(buf, p, strlen(p) + 1);
1996 buf2 = g_strdup_printf("Fw: %s", buf);
1997 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2003 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2004 _("Fw: multiple emails"));
2007 SIGNAL_BLOCK(textbuf);
2009 if (account->auto_sig)
2010 compose_insert_sig(compose, FALSE);
2012 compose_wrap_all(compose);
2014 SIGNAL_UNBLOCK(textbuf);
2016 gtk_text_buffer_get_start_iter(textbuf, &iter);
2017 gtk_text_buffer_place_cursor(textbuf, &iter);
2019 gtk_widget_grab_focus(compose->header_last->entry);
2020 undo_unblock(compose->undostruct);
2021 compose->modified = FALSE;
2022 compose_set_title(compose);
2024 compose->updating = FALSE;
2025 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2026 SCROLL_TO_CURSOR(compose);
2028 if (compose->deferred_destroy) {
2029 compose_destroy(compose);
2033 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2038 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2040 GtkTextIter start = *iter;
2041 GtkTextIter end_iter;
2042 int start_pos = gtk_text_iter_get_offset(&start);
2044 if (!compose->account->sig_sep)
2047 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2048 start_pos+strlen(compose->account->sig_sep));
2050 /* check sig separator */
2051 str = gtk_text_iter_get_text(&start, &end_iter);
2052 if (!strcmp(str, compose->account->sig_sep)) {
2054 /* check end of line (\n) */
2055 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2056 start_pos+strlen(compose->account->sig_sep));
2057 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2058 start_pos+strlen(compose->account->sig_sep)+1);
2059 tmp = gtk_text_iter_get_text(&start, &end_iter);
2060 if (!strcmp(tmp,"\n")) {
2072 static void compose_colorize_signature(Compose *compose)
2074 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2076 GtkTextIter end_iter;
2077 gtk_text_buffer_get_start_iter(buffer, &iter);
2078 while (gtk_text_iter_forward_line(&iter))
2079 if (compose_is_sig_separator(compose, buffer, &iter)) {
2080 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2081 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2085 #define BLOCK_WRAP() { \
2086 prev_autowrap = compose->autowrap; \
2087 buffer = gtk_text_view_get_buffer( \
2088 GTK_TEXT_VIEW(compose->text)); \
2089 compose->autowrap = FALSE; \
2091 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2092 G_CALLBACK(compose_changed_cb), \
2094 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2095 G_CALLBACK(text_inserted), \
2098 #define UNBLOCK_WRAP() { \
2099 compose->autowrap = prev_autowrap; \
2100 if (compose->autowrap) { \
2101 gint old = compose->draft_timeout_tag; \
2102 compose->draft_timeout_tag = -2; \
2103 compose_wrap_all(compose); \
2104 compose->draft_timeout_tag = old; \
2107 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2108 G_CALLBACK(compose_changed_cb), \
2110 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2111 G_CALLBACK(text_inserted), \
2115 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2117 Compose *compose = NULL;
2118 PrefsAccount *account = NULL;
2119 GtkTextView *textview;
2120 GtkTextBuffer *textbuf;
2124 gchar buf[BUFFSIZE];
2125 gboolean use_signing = FALSE;
2126 gboolean use_encryption = FALSE;
2127 gchar *privacy_system = NULL;
2128 int priority = PRIORITY_NORMAL;
2129 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2130 gboolean autowrap = prefs_common.autowrap;
2131 gboolean autoindent = prefs_common.auto_indent;
2132 HeaderEntry *manual_headers = NULL;
2134 cm_return_val_if_fail(msginfo != NULL, NULL);
2135 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2137 if (compose_put_existing_to_front(msginfo)) {
2141 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2142 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2143 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2144 gchar queueheader_buf[BUFFSIZE];
2147 /* Select Account from queue headers */
2148 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2149 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2150 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2151 account = account_find_from_id(id);
2153 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2154 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2155 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2156 account = account_find_from_id(id);
2158 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2159 sizeof(queueheader_buf), "NAID:")) {
2160 id = atoi(&queueheader_buf[strlen("NAID:")]);
2161 account = account_find_from_id(id);
2163 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2164 sizeof(queueheader_buf), "MAID:")) {
2165 id = atoi(&queueheader_buf[strlen("MAID:")]);
2166 account = account_find_from_id(id);
2168 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2169 sizeof(queueheader_buf), "S:")) {
2170 account = account_find_from_address(queueheader_buf, FALSE);
2172 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2173 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2174 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2175 use_signing = param;
2178 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2179 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2180 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2181 use_signing = param;
2184 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2185 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2186 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2187 use_encryption = param;
2189 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2190 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2191 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2192 use_encryption = param;
2194 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2195 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2196 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2199 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2200 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2201 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2204 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2205 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2206 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2208 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2209 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2210 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2212 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2213 sizeof(queueheader_buf), "X-Priority: ")) {
2214 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2217 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2218 sizeof(queueheader_buf), "RMID:")) {
2219 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2220 if (tokens[0] && tokens[1] && tokens[2]) {
2221 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2222 if (orig_item != NULL) {
2223 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2228 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2229 sizeof(queueheader_buf), "FMID:")) {
2230 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2231 if (tokens[0] && tokens[1] && tokens[2]) {
2232 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2233 if (orig_item != NULL) {
2234 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2239 /* Get manual headers */
2240 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2241 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2242 if (*listmh != '\0') {
2243 debug_print("Got manual headers: %s\n", listmh);
2244 manual_headers = procheader_entries_from_str(listmh);
2249 account = msginfo->folder->folder->account;
2252 if (!account && prefs_common.reedit_account_autosel) {
2253 gchar from[BUFFSIZE];
2254 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2255 extract_address(from);
2256 account = account_find_from_address(from, FALSE);
2260 account = cur_account;
2262 cm_return_val_if_fail(account != NULL, NULL);
2264 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2266 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2267 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2268 compose->autowrap = autowrap;
2269 compose->replyinfo = replyinfo;
2270 compose->fwdinfo = fwdinfo;
2272 compose->updating = TRUE;
2273 compose->priority = priority;
2275 if (privacy_system != NULL) {
2276 compose->privacy_system = privacy_system;
2277 compose_use_signing(compose, use_signing);
2278 compose_use_encryption(compose, use_encryption);
2279 compose_update_privacy_system_menu_item(compose, FALSE);
2281 activate_privacy_system(compose, account, FALSE);
2284 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2286 compose_extract_original_charset(compose);
2288 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2289 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2290 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2291 gchar queueheader_buf[BUFFSIZE];
2293 /* Set message save folder */
2294 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2295 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2296 compose_set_save_to(compose, &queueheader_buf[4]);
2298 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2299 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2301 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2306 if (compose_parse_header(compose, msginfo) < 0) {
2307 compose->updating = FALSE;
2308 compose_destroy(compose);
2311 compose_reedit_set_entry(compose, msginfo);
2313 textview = GTK_TEXT_VIEW(compose->text);
2314 textbuf = gtk_text_view_get_buffer(textview);
2315 compose_create_tags(textview, compose);
2317 mark = gtk_text_buffer_get_insert(textbuf);
2318 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2320 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2321 G_CALLBACK(compose_changed_cb),
2324 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2325 fp = procmime_get_first_encrypted_text_content(msginfo);
2327 compose_force_encryption(compose, account, TRUE, NULL);
2330 fp = procmime_get_first_text_content(msginfo);
2333 g_warning("Can't get text part\n");
2337 gboolean prev_autowrap = compose->autowrap;
2338 GtkTextBuffer *buffer = textbuf;
2340 while (fgets(buf, sizeof(buf), fp) != NULL) {
2342 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2348 compose_attach_parts(compose, msginfo);
2350 compose_colorize_signature(compose);
2352 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2353 G_CALLBACK(compose_changed_cb),
2356 if (manual_headers != NULL) {
2357 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2358 procheader_entries_free(manual_headers);
2359 compose->updating = FALSE;
2360 compose_destroy(compose);
2363 procheader_entries_free(manual_headers);
2366 gtk_widget_grab_focus(compose->text);
2368 if (prefs_common.auto_exteditor) {
2369 compose_exec_ext_editor(compose);
2371 compose->modified = FALSE;
2372 compose_set_title(compose);
2374 compose->updating = FALSE;
2375 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2376 SCROLL_TO_CURSOR(compose);
2378 if (compose->deferred_destroy) {
2379 compose_destroy(compose);
2383 compose->sig_str = account_get_signature_str(compose->account);
2385 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2390 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2397 cm_return_val_if_fail(msginfo != NULL, NULL);
2400 account = account_get_reply_account(msginfo,
2401 prefs_common.reply_account_autosel);
2402 cm_return_val_if_fail(account != NULL, NULL);
2404 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2406 compose->updating = TRUE;
2408 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2409 compose->replyinfo = NULL;
2410 compose->fwdinfo = NULL;
2412 compose_show_first_last_header(compose, TRUE);
2414 gtk_widget_grab_focus(compose->header_last->entry);
2416 filename = procmsg_get_message_file(msginfo);
2418 if (filename == NULL) {
2419 compose->updating = FALSE;
2420 compose_destroy(compose);
2425 compose->redirect_filename = filename;
2427 /* Set save folder */
2428 item = msginfo->folder;
2429 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2430 gchar *folderidentifier;
2432 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2433 folderidentifier = folder_item_get_identifier(item);
2434 compose_set_save_to(compose, folderidentifier);
2435 g_free(folderidentifier);
2438 compose_attach_parts(compose, msginfo);
2440 if (msginfo->subject)
2441 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2443 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2445 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2446 _("The body of the \"Redirect\" template has an error at line %d."));
2447 quote_fmt_reset_vartable();
2448 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2450 compose_colorize_signature(compose);
2453 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2454 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2455 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2457 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2458 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2459 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2460 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2461 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2462 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2463 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2464 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2466 if (compose->toolbar->draft_btn)
2467 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2468 if (compose->toolbar->insert_btn)
2469 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2470 if (compose->toolbar->attach_btn)
2471 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2472 if (compose->toolbar->sig_btn)
2473 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2474 if (compose->toolbar->exteditor_btn)
2475 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2476 if (compose->toolbar->linewrap_current_btn)
2477 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2478 if (compose->toolbar->linewrap_all_btn)
2479 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2481 compose->modified = FALSE;
2482 compose_set_title(compose);
2483 compose->updating = FALSE;
2484 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2485 SCROLL_TO_CURSOR(compose);
2487 if (compose->deferred_destroy) {
2488 compose_destroy(compose);
2492 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2497 GList *compose_get_compose_list(void)
2499 return compose_list;
2502 void compose_entry_append(Compose *compose, const gchar *address,
2503 ComposeEntryType type, ComposePrefType pref_type)
2505 const gchar *header;
2507 gboolean in_quote = FALSE;
2508 if (!address || *address == '\0') return;
2515 header = N_("Bcc:");
2517 case COMPOSE_REPLYTO:
2518 header = N_("Reply-To:");
2520 case COMPOSE_NEWSGROUPS:
2521 header = N_("Newsgroups:");
2523 case COMPOSE_FOLLOWUPTO:
2524 header = N_( "Followup-To:");
2526 case COMPOSE_INREPLYTO:
2527 header = N_( "In-Reply-To:");
2534 header = prefs_common_translated_header_name(header);
2536 cur = begin = (gchar *)address;
2538 /* we separate the line by commas, but not if we're inside a quoted
2540 while (*cur != '\0') {
2542 in_quote = !in_quote;
2543 if (*cur == ',' && !in_quote) {
2544 gchar *tmp = g_strdup(begin);
2546 tmp[cur-begin]='\0';
2549 while (*tmp == ' ' || *tmp == '\t')
2551 compose_add_header_entry(compose, header, tmp, pref_type);
2558 gchar *tmp = g_strdup(begin);
2560 tmp[cur-begin]='\0';
2563 while (*tmp == ' ' || *tmp == '\t')
2565 compose_add_header_entry(compose, header, tmp, pref_type);
2570 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2572 #if !GTK_CHECK_VERSION(3, 0, 0)
2573 static GdkColor yellow;
2574 static GdkColor black;
2575 static gboolean yellow_initialised = FALSE;
2577 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2578 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2583 #if !GTK_CHECK_VERSION(3, 0, 0)
2584 if (!yellow_initialised) {
2585 gdk_color_parse("#f5f6be", &yellow);
2586 gdk_color_parse("#000000", &black);
2587 yellow_initialised = gdk_colormap_alloc_color(
2588 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2589 yellow_initialised &= gdk_colormap_alloc_color(
2590 gdk_colormap_get_system(), &black, FALSE, TRUE);
2594 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2595 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2596 if (gtk_entry_get_text(entry) &&
2597 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2598 #if !GTK_CHECK_VERSION(3, 0, 0)
2599 if (yellow_initialised) {
2601 gtk_widget_modify_base(
2602 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2603 GTK_STATE_NORMAL, &yellow);
2604 gtk_widget_modify_text(
2605 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2606 GTK_STATE_NORMAL, &black);
2607 #if !GTK_CHECK_VERSION(3, 0, 0)
2614 void compose_toolbar_cb(gint action, gpointer data)
2616 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2617 Compose *compose = (Compose*)toolbar_item->parent;
2619 cm_return_if_fail(compose != NULL);
2623 compose_send_cb(NULL, compose);
2626 compose_send_later_cb(NULL, compose);
2629 compose_draft(compose, COMPOSE_QUIT_EDITING);
2632 compose_insert_file_cb(NULL, compose);
2635 compose_attach_cb(NULL, compose);
2638 compose_insert_sig(compose, FALSE);
2641 compose_ext_editor_cb(NULL, compose);
2643 case A_LINEWRAP_CURRENT:
2644 compose_beautify_paragraph(compose, NULL, TRUE);
2646 case A_LINEWRAP_ALL:
2647 compose_wrap_all_full(compose, TRUE);
2650 compose_address_cb(NULL, compose);
2653 case A_CHECK_SPELLING:
2654 compose_check_all(NULL, compose);
2662 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2667 gchar *subject = NULL;
2671 gchar **attach = NULL;
2672 gchar *inreplyto = NULL;
2673 MailField mfield = NO_FIELD_PRESENT;
2675 /* get mailto parts but skip from */
2676 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2679 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2680 mfield = TO_FIELD_PRESENT;
2683 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2685 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2687 if (!g_utf8_validate (subject, -1, NULL)) {
2688 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2689 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2692 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2694 mfield = SUBJECT_FIELD_PRESENT;
2697 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2698 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2701 gboolean prev_autowrap = compose->autowrap;
2703 compose->autowrap = FALSE;
2705 mark = gtk_text_buffer_get_insert(buffer);
2706 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2708 if (!g_utf8_validate (body, -1, NULL)) {
2709 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2710 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2713 gtk_text_buffer_insert(buffer, &iter, body, -1);
2715 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2717 compose->autowrap = prev_autowrap;
2718 if (compose->autowrap)
2719 compose_wrap_all(compose);
2720 mfield = BODY_FIELD_PRESENT;
2724 gint i = 0, att = 0;
2725 gchar *warn_files = NULL;
2726 while (attach[i] != NULL) {
2727 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2728 if (utf8_filename) {
2729 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2730 gchar *tmp = g_strdup_printf("%s%s\n",
2731 warn_files?warn_files:"",
2737 g_free(utf8_filename);
2739 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2744 alertpanel_notice(ngettext(
2745 "The following file has been attached: \n%s",
2746 "The following files have been attached: \n%s", att), warn_files);
2751 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2764 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2766 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2767 {"Cc:", NULL, TRUE},
2768 {"References:", NULL, FALSE},
2769 {"Bcc:", NULL, TRUE},
2770 {"Newsgroups:", NULL, TRUE},
2771 {"Followup-To:", NULL, TRUE},
2772 {"List-Post:", NULL, FALSE},
2773 {"X-Priority:", NULL, FALSE},
2774 {NULL, NULL, FALSE}};
2790 cm_return_val_if_fail(msginfo != NULL, -1);
2792 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2793 procheader_get_header_fields(fp, hentry);
2796 if (hentry[H_REPLY_TO].body != NULL) {
2797 if (hentry[H_REPLY_TO].body[0] != '\0') {
2799 conv_unmime_header(hentry[H_REPLY_TO].body,
2802 g_free(hentry[H_REPLY_TO].body);
2803 hentry[H_REPLY_TO].body = NULL;
2805 if (hentry[H_CC].body != NULL) {
2806 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2807 g_free(hentry[H_CC].body);
2808 hentry[H_CC].body = NULL;
2810 if (hentry[H_REFERENCES].body != NULL) {
2811 if (compose->mode == COMPOSE_REEDIT)
2812 compose->references = hentry[H_REFERENCES].body;
2814 compose->references = compose_parse_references
2815 (hentry[H_REFERENCES].body, msginfo->msgid);
2816 g_free(hentry[H_REFERENCES].body);
2818 hentry[H_REFERENCES].body = NULL;
2820 if (hentry[H_BCC].body != NULL) {
2821 if (compose->mode == COMPOSE_REEDIT)
2823 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2824 g_free(hentry[H_BCC].body);
2825 hentry[H_BCC].body = NULL;
2827 if (hentry[H_NEWSGROUPS].body != NULL) {
2828 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2829 hentry[H_NEWSGROUPS].body = NULL;
2831 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2832 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2833 compose->followup_to =
2834 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2837 g_free(hentry[H_FOLLOWUP_TO].body);
2838 hentry[H_FOLLOWUP_TO].body = NULL;
2840 if (hentry[H_LIST_POST].body != NULL) {
2841 gchar *to = NULL, *start = NULL;
2843 extract_address(hentry[H_LIST_POST].body);
2844 if (hentry[H_LIST_POST].body[0] != '\0') {
2845 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2847 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2848 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2851 g_free(compose->ml_post);
2852 compose->ml_post = to;
2855 g_free(hentry[H_LIST_POST].body);
2856 hentry[H_LIST_POST].body = NULL;
2859 /* CLAWS - X-Priority */
2860 if (compose->mode == COMPOSE_REEDIT)
2861 if (hentry[H_X_PRIORITY].body != NULL) {
2864 priority = atoi(hentry[H_X_PRIORITY].body);
2865 g_free(hentry[H_X_PRIORITY].body);
2867 hentry[H_X_PRIORITY].body = NULL;
2869 if (priority < PRIORITY_HIGHEST ||
2870 priority > PRIORITY_LOWEST)
2871 priority = PRIORITY_NORMAL;
2873 compose->priority = priority;
2876 if (compose->mode == COMPOSE_REEDIT) {
2877 if (msginfo->inreplyto && *msginfo->inreplyto)
2878 compose->inreplyto = g_strdup(msginfo->inreplyto);
2882 if (msginfo->msgid && *msginfo->msgid)
2883 compose->inreplyto = g_strdup(msginfo->msgid);
2885 if (!compose->references) {
2886 if (msginfo->msgid && *msginfo->msgid) {
2887 if (msginfo->inreplyto && *msginfo->inreplyto)
2888 compose->references =
2889 g_strdup_printf("<%s>\n\t<%s>",
2893 compose->references =
2894 g_strconcat("<", msginfo->msgid, ">",
2896 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2897 compose->references =
2898 g_strconcat("<", msginfo->inreplyto, ">",
2906 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2911 cm_return_val_if_fail(msginfo != NULL, -1);
2913 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2914 procheader_get_header_fields(fp, entries);
2918 while (he != NULL && he->name != NULL) {
2920 GtkListStore *model = NULL;
2922 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
2923 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
2924 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
2925 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
2926 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
2933 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2935 GSList *ref_id_list, *cur;
2939 ref_id_list = references_list_append(NULL, ref);
2940 if (!ref_id_list) return NULL;
2941 if (msgid && *msgid)
2942 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2947 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2948 /* "<" + Message-ID + ">" + CR+LF+TAB */
2949 len += strlen((gchar *)cur->data) + 5;
2951 if (len > MAX_REFERENCES_LEN) {
2952 /* remove second message-ID */
2953 if (ref_id_list && ref_id_list->next &&
2954 ref_id_list->next->next) {
2955 g_free(ref_id_list->next->data);
2956 ref_id_list = g_slist_remove
2957 (ref_id_list, ref_id_list->next->data);
2959 slist_free_strings(ref_id_list);
2960 g_slist_free(ref_id_list);
2967 new_ref = g_string_new("");
2968 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2969 if (new_ref->len > 0)
2970 g_string_append(new_ref, "\n\t");
2971 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2974 slist_free_strings(ref_id_list);
2975 g_slist_free(ref_id_list);
2977 new_ref_str = new_ref->str;
2978 g_string_free(new_ref, FALSE);
2983 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2984 const gchar *fmt, const gchar *qmark,
2985 const gchar *body, gboolean rewrap,
2986 gboolean need_unescape,
2987 const gchar *err_msg)
2989 MsgInfo* dummyinfo = NULL;
2990 gchar *quote_str = NULL;
2992 gboolean prev_autowrap;
2993 const gchar *trimmed_body = body;
2994 gint cursor_pos = -1;
2995 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2996 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3001 SIGNAL_BLOCK(buffer);
3004 dummyinfo = compose_msginfo_new_from_compose(compose);
3005 msginfo = dummyinfo;
3008 if (qmark != NULL) {
3010 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3011 compose->gtkaspell);
3013 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3015 quote_fmt_scan_string(qmark);
3018 buf = quote_fmt_get_buffer();
3020 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3022 Xstrdup_a(quote_str, buf, goto error)
3025 if (fmt && *fmt != '\0') {
3028 while (*trimmed_body == '\n')
3032 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3033 compose->gtkaspell);
3035 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3037 if (need_unescape) {
3040 /* decode \-escape sequences in the internal representation of the quote format */
3041 tmp = g_malloc(strlen(fmt)+1);
3042 pref_get_unescaped_pref(tmp, fmt);
3043 quote_fmt_scan_string(tmp);
3047 quote_fmt_scan_string(fmt);
3051 buf = quote_fmt_get_buffer();
3053 gint line = quote_fmt_get_line();
3054 alertpanel_error(err_msg, line);
3060 prev_autowrap = compose->autowrap;
3061 compose->autowrap = FALSE;
3063 mark = gtk_text_buffer_get_insert(buffer);
3064 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3065 if (g_utf8_validate(buf, -1, NULL)) {
3066 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3068 gchar *tmpout = NULL;
3069 tmpout = conv_codeset_strdup
3070 (buf, conv_get_locale_charset_str_no_utf8(),
3072 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3074 tmpout = g_malloc(strlen(buf)*2+1);
3075 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3077 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3081 cursor_pos = quote_fmt_get_cursor_pos();
3082 if (cursor_pos == -1)
3083 cursor_pos = gtk_text_iter_get_offset(&iter);
3084 compose->set_cursor_pos = cursor_pos;
3086 gtk_text_buffer_get_start_iter(buffer, &iter);
3087 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3088 gtk_text_buffer_place_cursor(buffer, &iter);
3090 compose->autowrap = prev_autowrap;
3091 if (compose->autowrap && rewrap)
3092 compose_wrap_all(compose);
3099 SIGNAL_UNBLOCK(buffer);
3101 procmsg_msginfo_free( dummyinfo );
3106 /* if ml_post is of type addr@host and from is of type
3107 * addr-anything@host, return TRUE
3109 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3111 gchar *left_ml = NULL;
3112 gchar *right_ml = NULL;
3113 gchar *left_from = NULL;
3114 gchar *right_from = NULL;
3115 gboolean result = FALSE;
3117 if (!ml_post || !from)
3120 left_ml = g_strdup(ml_post);
3121 if (strstr(left_ml, "@")) {
3122 right_ml = strstr(left_ml, "@")+1;
3123 *(strstr(left_ml, "@")) = '\0';
3126 left_from = g_strdup(from);
3127 if (strstr(left_from, "@")) {
3128 right_from = strstr(left_from, "@")+1;
3129 *(strstr(left_from, "@")) = '\0';
3132 if (left_ml && left_from && right_ml && right_from
3133 && !strncmp(left_from, left_ml, strlen(left_ml))
3134 && !strcmp(right_from, right_ml)) {
3143 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3144 gboolean respect_default_to)
3148 if (!folder || !folder->prefs)
3151 if (respect_default_to && folder->prefs->enable_default_to) {
3152 compose_entry_append(compose, folder->prefs->default_to,
3153 COMPOSE_TO, PREF_FOLDER);
3154 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3156 if (folder->prefs->enable_default_cc)
3157 compose_entry_append(compose, folder->prefs->default_cc,
3158 COMPOSE_CC, PREF_FOLDER);
3159 if (folder->prefs->enable_default_bcc)
3160 compose_entry_append(compose, folder->prefs->default_bcc,
3161 COMPOSE_BCC, PREF_FOLDER);
3162 if (folder->prefs->enable_default_replyto)
3163 compose_entry_append(compose, folder->prefs->default_replyto,
3164 COMPOSE_REPLYTO, PREF_FOLDER);
3167 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3172 if (!compose || !msginfo)
3175 if (msginfo->subject && *msginfo->subject) {
3176 buf = p = g_strdup(msginfo->subject);
3177 p += subject_get_prefix_length(p);
3178 memmove(buf, p, strlen(p) + 1);
3180 buf2 = g_strdup_printf("Re: %s", buf);
3181 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3186 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");