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"
87 #include "alertpanel.h"
88 #include "manage_window.h"
89 #include "gtkshruler.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 gchar *compose_parse_references (const gchar *ref,
226 static gchar *compose_quote_fmt (Compose *compose,
232 gboolean need_unescape,
233 const gchar *err_msg);
235 static void compose_reply_set_entry (Compose *compose,
241 followup_and_reply_to);
242 static void compose_reedit_set_entry (Compose *compose,
245 static void compose_insert_sig (Compose *compose,
247 static ComposeInsertResult compose_insert_file (Compose *compose,
250 static gboolean compose_attach_append (Compose *compose,
253 const gchar *content_type,
254 const gchar *charset);
255 static void compose_attach_parts (Compose *compose,
258 static gboolean compose_beautify_paragraph (Compose *compose,
259 GtkTextIter *par_iter,
261 static void compose_wrap_all (Compose *compose);
262 static void compose_wrap_all_full (Compose *compose,
265 static void compose_set_title (Compose *compose);
266 static void compose_select_account (Compose *compose,
267 PrefsAccount *account,
270 static PrefsAccount *compose_current_mail_account(void);
271 /* static gint compose_send (Compose *compose); */
272 static gboolean compose_check_for_valid_recipient
274 static gboolean compose_check_entries (Compose *compose,
275 gboolean check_everything);
276 static gint compose_write_to_file (Compose *compose,
279 gboolean attach_parts);
280 static gint compose_write_body_to_file (Compose *compose,
282 static gint compose_remove_reedit_target (Compose *compose,
284 static void compose_remove_draft (Compose *compose);
285 static gint compose_queue_sub (Compose *compose,
289 gboolean check_subject,
290 gboolean remove_reedit_target);
291 static int compose_add_attachments (Compose *compose,
293 static gchar *compose_get_header (Compose *compose);
295 static void compose_convert_header (Compose *compose,
300 gboolean addr_field);
302 static void compose_attach_info_free (AttachInfo *ainfo);
303 static void compose_attach_remove_selected (GtkAction *action,
306 static void compose_template_apply (Compose *compose,
309 static void compose_attach_property (GtkAction *action,
311 static void compose_attach_property_create (gboolean *cancelled);
312 static void attach_property_ok (GtkWidget *widget,
313 gboolean *cancelled);
314 static void attach_property_cancel (GtkWidget *widget,
315 gboolean *cancelled);
316 static gint attach_property_delete_event (GtkWidget *widget,
318 gboolean *cancelled);
319 static gboolean attach_property_key_pressed (GtkWidget *widget,
321 gboolean *cancelled);
323 static void compose_exec_ext_editor (Compose *compose);
325 static gint compose_exec_ext_editor_real (const gchar *file);
326 static gboolean compose_ext_editor_kill (Compose *compose);
327 static gboolean compose_input_cb (GIOChannel *source,
328 GIOCondition condition,
330 static void compose_set_ext_editor_sensitive (Compose *compose,
332 #endif /* G_OS_UNIX */
334 static void compose_undo_state_changed (UndoMain *undostruct,
339 static void compose_create_header_entry (Compose *compose);
340 static void compose_add_header_entry (Compose *compose, const gchar *header,
341 gchar *text, ComposePrefType pref_type);
342 static void compose_remove_header_entries(Compose *compose);
344 static void compose_update_priority_menu_item(Compose * compose);
346 static void compose_spell_menu_changed (void *data);
347 static void compose_dict_changed (void *data);
349 static void compose_add_field_list ( Compose *compose,
350 GList *listAddress );
352 /* callback functions */
354 static gboolean compose_edit_size_alloc (GtkEditable *widget,
355 GtkAllocation *allocation,
356 GtkSHRuler *shruler);
357 static void account_activated (GtkComboBox *optmenu,
359 static void attach_selected (GtkTreeView *tree_view,
360 GtkTreePath *tree_path,
361 GtkTreeViewColumn *column,
363 static gboolean attach_button_pressed (GtkWidget *widget,
364 GdkEventButton *event,
366 static gboolean attach_key_pressed (GtkWidget *widget,
369 static void compose_send_cb (GtkAction *action, gpointer data);
370 static void compose_send_later_cb (GtkAction *action, gpointer data);
372 static void compose_save_cb (GtkAction *action,
375 static void compose_attach_cb (GtkAction *action,
377 static void compose_insert_file_cb (GtkAction *action,
379 static void compose_insert_sig_cb (GtkAction *action,
382 static void compose_close_cb (GtkAction *action,
385 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
387 static void compose_address_cb (GtkAction *action,
389 static void about_show_cb (GtkAction *action,
391 static void compose_template_activate_cb(GtkWidget *widget,
394 static void compose_ext_editor_cb (GtkAction *action,
397 static gint compose_delete_cb (GtkWidget *widget,
401 static void compose_undo_cb (GtkAction *action,
403 static void compose_redo_cb (GtkAction *action,
405 static void compose_cut_cb (GtkAction *action,
407 static void compose_copy_cb (GtkAction *action,
409 static void compose_paste_cb (GtkAction *action,
411 static void compose_paste_as_quote_cb (GtkAction *action,
413 static void compose_paste_no_wrap_cb (GtkAction *action,
415 static void compose_paste_wrap_cb (GtkAction *action,
417 static void compose_allsel_cb (GtkAction *action,
420 static void compose_advanced_action_cb (GtkAction *action,
423 static void compose_grab_focus_cb (GtkWidget *widget,
426 static void compose_changed_cb (GtkTextBuffer *textbuf,
429 static void compose_wrap_cb (GtkAction *action,
431 static void compose_wrap_all_cb (GtkAction *action,
433 static void compose_find_cb (GtkAction *action,
435 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
437 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
440 static void compose_toggle_ruler_cb (GtkToggleAction *action,
442 static void compose_toggle_sign_cb (GtkToggleAction *action,
444 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
446 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
447 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
448 static void activate_privacy_system (Compose *compose,
449 PrefsAccount *account,
451 static void compose_use_signing(Compose *compose, gboolean use_signing);
452 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
453 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
455 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
457 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
458 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
459 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
461 static void compose_attach_drag_received_cb (GtkWidget *widget,
462 GdkDragContext *drag_context,
465 GtkSelectionData *data,
469 static void compose_insert_drag_received_cb (GtkWidget *widget,
470 GdkDragContext *drag_context,
473 GtkSelectionData *data,
477 static void compose_header_drag_received_cb (GtkWidget *widget,
478 GdkDragContext *drag_context,
481 GtkSelectionData *data,
486 static gboolean compose_drag_drop (GtkWidget *widget,
487 GdkDragContext *drag_context,
489 guint time, gpointer user_data);
491 static void text_inserted (GtkTextBuffer *buffer,
496 static Compose *compose_generic_reply(MsgInfo *msginfo,
497 ComposeQuoteMode quote_mode,
501 gboolean followup_and_reply_to,
504 static void compose_headerentry_changed_cb (GtkWidget *entry,
505 ComposeHeaderEntry *headerentry);
506 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
508 ComposeHeaderEntry *headerentry);
509 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
510 ComposeHeaderEntry *headerentry);
512 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
514 static void compose_allow_user_actions (Compose *compose, gboolean allow);
516 static void compose_nothing_cb (GtkAction *action, gpointer data)
522 static void compose_check_all (GtkAction *action, gpointer data);
523 static void compose_highlight_all (GtkAction *action, gpointer data);
524 static void compose_check_backwards (GtkAction *action, gpointer data);
525 static void compose_check_forwards_go (GtkAction *action, gpointer data);
528 static gint compose_defer_auto_save_draft (Compose *compose);
529 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
531 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
534 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
535 FolderItem *folder_item);
537 static void compose_attach_update_label(Compose *compose);
538 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
539 gboolean respect_default_to);
541 static GtkActionEntry compose_popup_entries[] =
543 {"Compose", NULL, "Compose" },
544 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
545 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
546 {"Compose/---", NULL, "---", NULL, NULL, NULL },
547 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
550 static GtkActionEntry compose_entries[] =
552 {"Menu", NULL, "Menu" },
554 {"Message", NULL, N_("_Message") },
555 {"Edit", NULL, N_("_Edit") },
557 {"Spelling", NULL, N_("_Spelling") },
559 {"Options", NULL, N_("_Options") },
560 {"Tools", NULL, N_("_Tools") },
561 {"Help", NULL, N_("_Help") },
563 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
564 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
565 {"Message/---", NULL, "---" },
567 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
568 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
569 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
570 /* {"Message/---", NULL, "---" }, */
571 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
572 /* {"Message/---", NULL, "---" }, */
573 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
576 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
577 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
578 {"Edit/---", NULL, "---" },
580 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
581 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
582 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
584 {"Edit/SpecialPaste", NULL, N_("Special paste") },
585 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
586 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
587 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
589 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
591 {"Edit/Advanced", NULL, N_("A_dvanced") },
592 {"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*/
593 {"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*/
594 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
595 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
596 {"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*/
597 {"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*/
598 {"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*/
599 {"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*/
600 {"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*/
601 {"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*/
602 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
603 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
604 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
605 {"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*/
607 /* {"Edit/---", NULL, "---" }, */
608 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
610 /* {"Edit/---", NULL, "---" }, */
611 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
612 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
613 /* {"Edit/---", NULL, "---" }, */
614 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
617 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
618 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
619 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
620 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
622 {"Spelling/---", NULL, "---" },
623 {"Spelling/Options", NULL, N_("_Options") },
628 {"Options/ReplyMode", NULL, N_("Reply _mode") },
629 {"Options/---", NULL, "---" },
630 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
631 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
633 /* {"Options/---", NULL, "---" }, */
635 {"Options/Priority", NULL, N_("_Priority") },
637 {"Options/Encoding", NULL, N_("Character _encoding") },
638 {"Options/Encoding/---", NULL, "---" },
639 #define ENC_ACTION(cs_char,c_char,string) \
640 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
642 {"Options/Encoding/Western", NULL, N_("Western European") },
643 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
644 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
645 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
646 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
647 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
648 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
649 {"Options/Encoding/Korean", NULL, N_("Korean") },
650 {"Options/Encoding/Thai", NULL, N_("Thai") },
653 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
655 {"Tools/Template", NULL, N_("_Template") },
656 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
657 {"Tools/Actions", NULL, N_("Actio_ns") },
658 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
661 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
664 static GtkToggleActionEntry compose_toggle_entries[] =
666 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
667 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
668 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
669 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
670 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
671 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
672 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
675 static GtkRadioActionEntry compose_radio_rm_entries[] =
677 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
678 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
679 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
680 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
683 static GtkRadioActionEntry compose_radio_prio_entries[] =
685 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
686 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
687 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
688 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
689 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
692 static GtkRadioActionEntry compose_radio_enc_entries[] =
694 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
728 static GtkTargetEntry compose_mime_types[] =
730 {"text/uri-list", 0, 0},
731 {"UTF8_STRING", 0, 0},
735 static gboolean compose_put_existing_to_front(MsgInfo *info)
737 GList *compose_list = compose_get_compose_list();
741 for (elem = compose_list; elem != NULL && elem->data != NULL;
743 Compose *c = (Compose*)elem->data;
745 if (!c->targetinfo || !c->targetinfo->msgid ||
749 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
750 gtkut_window_popup(c->window);
758 static GdkColor quote_color1 =
759 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
760 static GdkColor quote_color2 =
761 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
762 static GdkColor quote_color3 =
763 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
765 static GdkColor quote_bgcolor1 =
766 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
767 static GdkColor quote_bgcolor2 =
768 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
769 static GdkColor quote_bgcolor3 =
770 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
772 static GdkColor signature_color = {
779 static GdkColor uri_color = {
786 static void compose_create_tags(GtkTextView *text, Compose *compose)
788 GtkTextBuffer *buffer;
789 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
795 buffer = gtk_text_view_get_buffer(text);
797 if (prefs_common.enable_color) {
798 /* grab the quote colors, converting from an int to a GdkColor */
799 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
801 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
803 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
805 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
807 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
809 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
811 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
813 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
816 signature_color = quote_color1 = quote_color2 = quote_color3 =
817 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
820 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
821 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
822 "foreground-gdk", "e_color1,
823 "paragraph-background-gdk", "e_bgcolor1,
825 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
826 "foreground-gdk", "e_color2,
827 "paragraph-background-gdk", "e_bgcolor2,
829 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
830 "foreground-gdk", "e_color3,
831 "paragraph-background-gdk", "e_bgcolor3,
834 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
835 "foreground-gdk", "e_color1,
837 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
838 "foreground-gdk", "e_color2,
840 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
841 "foreground-gdk", "e_color3,
845 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
846 "foreground-gdk", &signature_color,
849 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
850 "foreground-gdk", &uri_color,
852 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
853 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
855 color[0] = quote_color1;
856 color[1] = quote_color2;
857 color[2] = quote_color3;
858 color[3] = quote_bgcolor1;
859 color[4] = quote_bgcolor2;
860 color[5] = quote_bgcolor3;
861 color[6] = signature_color;
862 color[7] = uri_color;
863 cmap = gdk_drawable_get_colormap(compose->window->window);
864 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
866 for (i = 0; i < 8; i++) {
867 if (success[i] == FALSE) {
870 g_warning("Compose: color allocation failed.\n");
871 style = gtk_widget_get_style(GTK_WIDGET(text));
872 quote_color1 = quote_color2 = quote_color3 =
873 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
874 signature_color = uri_color = black;
879 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
880 GPtrArray *attach_files)
882 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
885 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
887 return compose_generic_new(account, mailto, item, NULL, NULL);
890 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
892 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
895 #define SCROLL_TO_CURSOR(compose) { \
896 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
897 gtk_text_view_get_buffer( \
898 GTK_TEXT_VIEW(compose->text))); \
899 gtk_text_view_scroll_mark_onscreen( \
900 GTK_TEXT_VIEW(compose->text), \
904 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
907 if (folderidentifier) {
908 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
909 prefs_common.compose_save_to_history = add_history(
910 prefs_common.compose_save_to_history, folderidentifier);
911 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
912 prefs_common.compose_save_to_history);
915 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
916 if (folderidentifier)
917 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
919 gtk_entry_set_text(GTK_ENTRY(entry), "");
922 static gchar *compose_get_save_to(Compose *compose)
925 gchar *result = NULL;
926 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
927 result = gtk_editable_get_chars(entry, 0, -1);
930 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
931 prefs_common.compose_save_to_history = add_history(
932 prefs_common.compose_save_to_history, result);
933 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
934 prefs_common.compose_save_to_history);
939 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
940 GPtrArray *attach_files, GList *listAddress )
943 GtkTextView *textview;
944 GtkTextBuffer *textbuf;
946 const gchar *subject_format = NULL;
947 const gchar *body_format = NULL;
948 gchar *mailto_from = NULL;
949 PrefsAccount *mailto_account = NULL;
950 MsgInfo* dummyinfo = NULL;
951 MailField mfield = NO_FIELD_PRESENT;
955 /* check if mailto defines a from */
956 if (mailto && *mailto != '\0') {
957 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
958 /* mailto defines a from, check if we can get account prefs from it,
959 if not, the account prefs will be guessed using other ways, but we'll keep
962 mailto_account = account_find_from_address(mailto_from, TRUE);
964 account = mailto_account;
967 /* if no account prefs set from mailto, set if from folder prefs (if any) */
968 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
969 account = account_find_from_id(item->prefs->default_account);
971 /* if no account prefs set, fallback to the current one */
972 if (!account) account = cur_account;
973 cm_return_val_if_fail(account != NULL, NULL);
975 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
977 /* override from name if mailto asked for it */
979 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
982 /* override from name according to folder properties */
983 if (item && item->prefs &&
984 item->prefs->compose_with_format &&
985 item->prefs->compose_override_from_format &&
986 *item->prefs->compose_override_from_format != '\0') {
991 dummyinfo = compose_msginfo_new_from_compose(compose);
993 /* decode \-escape sequences in the internal representation of the quote format */
994 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
995 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
998 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1001 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1003 quote_fmt_scan_string(tmp);
1006 buf = quote_fmt_get_buffer();
1008 alertpanel_error(_("New message From format error."));
1010 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1011 quote_fmt_reset_vartable();
1016 compose->replyinfo = NULL;
1017 compose->fwdinfo = NULL;
1019 textview = GTK_TEXT_VIEW(compose->text);
1020 textbuf = gtk_text_view_get_buffer(textview);
1021 compose_create_tags(textview, compose);
1023 undo_block(compose->undostruct);
1025 compose_set_dictionaries_from_folder_prefs(compose, item);
1028 if (account->auto_sig)
1029 compose_insert_sig(compose, FALSE);
1030 gtk_text_buffer_get_start_iter(textbuf, &iter);
1031 gtk_text_buffer_place_cursor(textbuf, &iter);
1033 if (account->protocol != A_NNTP) {
1034 if (mailto && *mailto != '\0') {
1035 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1038 compose_set_folder_prefs(compose, item, TRUE);
1040 if (item && item->ret_rcpt) {
1041 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1044 if (mailto && *mailto != '\0') {
1045 if (!strchr(mailto, '@'))
1046 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1048 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1049 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1050 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1051 mfield = TO_FIELD_PRESENT;
1054 * CLAWS: just don't allow return receipt request, even if the user
1055 * may want to send an email. simple but foolproof.
1057 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1059 compose_add_field_list( compose, listAddress );
1061 if (item && item->prefs && item->prefs->compose_with_format) {
1062 subject_format = item->prefs->compose_subject_format;
1063 body_format = item->prefs->compose_body_format;
1064 } else if (account->compose_with_format) {
1065 subject_format = account->compose_subject_format;
1066 body_format = account->compose_body_format;
1067 } else if (prefs_common.compose_with_format) {
1068 subject_format = prefs_common.compose_subject_format;
1069 body_format = prefs_common.compose_body_format;
1072 if (subject_format || body_format) {
1075 && *subject_format != '\0' )
1077 gchar *subject = NULL;
1082 dummyinfo = compose_msginfo_new_from_compose(compose);
1084 /* decode \-escape sequences in the internal representation of the quote format */
1085 tmp = g_malloc(strlen(subject_format)+1);
1086 pref_get_unescaped_pref(tmp, subject_format);
1088 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1090 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1091 compose->gtkaspell);
1093 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1095 quote_fmt_scan_string(tmp);
1098 buf = quote_fmt_get_buffer();
1100 alertpanel_error(_("New message subject format error."));
1102 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1103 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1104 quote_fmt_reset_vartable();
1108 mfield = SUBJECT_FIELD_PRESENT;
1112 && *body_format != '\0' )
1115 GtkTextBuffer *buffer;
1116 GtkTextIter start, end;
1120 dummyinfo = compose_msginfo_new_from_compose(compose);
1122 text = GTK_TEXT_VIEW(compose->text);
1123 buffer = gtk_text_view_get_buffer(text);
1124 gtk_text_buffer_get_start_iter(buffer, &start);
1125 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1126 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1128 compose_quote_fmt(compose, dummyinfo,
1130 NULL, tmp, FALSE, TRUE,
1131 _("The body of the \"New message\" template has an error at line %d."));
1132 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1133 quote_fmt_reset_vartable();
1137 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1138 gtkaspell_highlight_all(compose->gtkaspell);
1140 mfield = BODY_FIELD_PRESENT;
1144 procmsg_msginfo_free( dummyinfo );
1150 for (i = 0; i < attach_files->len; i++) {
1151 file = g_ptr_array_index(attach_files, i);
1152 compose_attach_append(compose, file, file, NULL, NULL);
1156 compose_show_first_last_header(compose, TRUE);
1158 /* Set save folder */
1159 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1160 gchar *folderidentifier;
1162 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1163 folderidentifier = folder_item_get_identifier(item);
1164 compose_set_save_to(compose, folderidentifier);
1165 g_free(folderidentifier);
1168 /* Place cursor according to provided input (mfield) */
1170 case NO_FIELD_PRESENT:
1171 if (compose->header_last)
1172 gtk_widget_grab_focus(compose->header_last->entry);
1174 case TO_FIELD_PRESENT:
1175 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1177 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1180 gtk_widget_grab_focus(compose->subject_entry);
1182 case SUBJECT_FIELD_PRESENT:
1183 textview = GTK_TEXT_VIEW(compose->text);
1186 textbuf = gtk_text_view_get_buffer(textview);
1189 mark = gtk_text_buffer_get_insert(textbuf);
1190 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1191 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1193 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1194 * only defers where it comes to the variable body
1195 * is not null. If no body is present compose->text
1196 * will be null in which case you cannot place the
1197 * cursor inside the component so. An empty component
1198 * is therefore created before placing the cursor
1200 case BODY_FIELD_PRESENT:
1201 gtk_widget_grab_focus(compose->text);
1205 undo_unblock(compose->undostruct);
1207 if (prefs_common.auto_exteditor)
1208 compose_exec_ext_editor(compose);
1210 compose->draft_timeout_tag = -1;
1211 SCROLL_TO_CURSOR(compose);
1213 compose->modified = FALSE;
1214 compose_set_title(compose);
1216 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1221 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1222 gboolean override_pref, const gchar *system)
1224 const gchar *privacy = NULL;
1226 cm_return_if_fail(compose != NULL);
1227 cm_return_if_fail(account != NULL);
1229 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1234 else if (account->default_privacy_system
1235 && strlen(account->default_privacy_system)) {
1236 privacy = account->default_privacy_system;
1238 GSList *privacy_avail = privacy_get_system_ids();
1239 if (privacy_avail && g_slist_length(privacy_avail)) {
1240 privacy = (gchar *)(privacy_avail->data);
1243 if (privacy != NULL) {
1245 g_free(compose->privacy_system);
1246 compose->privacy_system = NULL;
1248 if (compose->privacy_system == NULL)
1249 compose->privacy_system = g_strdup(privacy);
1250 else if (*(compose->privacy_system) == '\0') {
1251 g_free(compose->privacy_system);
1252 compose->privacy_system = g_strdup(privacy);
1254 compose_update_privacy_system_menu_item(compose, FALSE);
1255 compose_use_encryption(compose, TRUE);
1259 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1261 const gchar *privacy = NULL;
1265 else if (account->default_privacy_system
1266 && strlen(account->default_privacy_system)) {
1267 privacy = account->default_privacy_system;
1269 GSList *privacy_avail = privacy_get_system_ids();
1270 if (privacy_avail && g_slist_length(privacy_avail)) {
1271 privacy = (gchar *)(privacy_avail->data);
1275 if (privacy != NULL) {
1277 g_free(compose->privacy_system);
1278 compose->privacy_system = NULL;
1280 if (compose->privacy_system == NULL)
1281 compose->privacy_system = g_strdup(privacy);
1282 compose_update_privacy_system_menu_item(compose, FALSE);
1283 compose_use_signing(compose, TRUE);
1287 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1291 Compose *compose = NULL;
1293 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1295 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1296 cm_return_val_if_fail(msginfo != NULL, NULL);
1298 list_len = g_slist_length(msginfo_list);
1302 case COMPOSE_REPLY_TO_ADDRESS:
1303 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1304 FALSE, prefs_common.default_reply_list, FALSE, body);
1306 case COMPOSE_REPLY_WITH_QUOTE:
1307 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1308 FALSE, prefs_common.default_reply_list, FALSE, body);
1310 case COMPOSE_REPLY_WITHOUT_QUOTE:
1311 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1312 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1314 case COMPOSE_REPLY_TO_SENDER:
1315 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1316 FALSE, FALSE, TRUE, body);
1318 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1319 compose = compose_followup_and_reply_to(msginfo,
1320 COMPOSE_QUOTE_CHECK,
1321 FALSE, FALSE, body);
1323 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1324 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1325 FALSE, FALSE, TRUE, body);
1327 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1328 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1329 FALSE, FALSE, TRUE, NULL);
1331 case COMPOSE_REPLY_TO_ALL:
1332 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1333 TRUE, FALSE, FALSE, body);
1335 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1336 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1337 TRUE, FALSE, FALSE, body);
1339 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1340 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1341 TRUE, FALSE, FALSE, NULL);
1343 case COMPOSE_REPLY_TO_LIST:
1344 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1345 FALSE, TRUE, FALSE, body);
1347 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1348 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1349 FALSE, TRUE, FALSE, body);
1351 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1352 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1353 FALSE, TRUE, FALSE, NULL);
1355 case COMPOSE_FORWARD:
1356 if (prefs_common.forward_as_attachment) {
1357 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1360 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1364 case COMPOSE_FORWARD_INLINE:
1365 /* check if we reply to more than one Message */
1366 if (list_len == 1) {
1367 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1370 /* more messages FALL THROUGH */
1371 case COMPOSE_FORWARD_AS_ATTACH:
1372 compose = compose_forward_multiple(NULL, msginfo_list);
1374 case COMPOSE_REDIRECT:
1375 compose = compose_redirect(NULL, msginfo, FALSE);
1378 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1381 if (compose == NULL) {
1382 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1386 compose->rmode = mode;
1387 switch (compose->rmode) {
1389 case COMPOSE_REPLY_WITH_QUOTE:
1390 case COMPOSE_REPLY_WITHOUT_QUOTE:
1391 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1392 debug_print("reply mode Normal\n");
1393 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1394 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1396 case COMPOSE_REPLY_TO_SENDER:
1397 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1398 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1399 debug_print("reply mode Sender\n");
1400 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1402 case COMPOSE_REPLY_TO_ALL:
1403 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1404 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1405 debug_print("reply mode All\n");
1406 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1408 case COMPOSE_REPLY_TO_LIST:
1409 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1410 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1411 debug_print("reply mode List\n");
1412 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1414 case COMPOSE_REPLY_TO_ADDRESS:
1415 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1423 static Compose *compose_reply(MsgInfo *msginfo,
1424 ComposeQuoteMode quote_mode,
1430 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1431 to_sender, FALSE, body);
1434 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1435 ComposeQuoteMode quote_mode,
1440 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1441 to_sender, TRUE, body);
1444 static void compose_extract_original_charset(Compose *compose)
1446 MsgInfo *info = NULL;
1447 if (compose->replyinfo) {
1448 info = compose->replyinfo;
1449 } else if (compose->fwdinfo) {
1450 info = compose->fwdinfo;
1451 } else if (compose->targetinfo) {
1452 info = compose->targetinfo;
1455 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1456 MimeInfo *partinfo = mimeinfo;
1457 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1458 partinfo = procmime_mimeinfo_next(partinfo);
1460 compose->orig_charset =
1461 g_strdup(procmime_mimeinfo_get_parameter(
1462 partinfo, "charset"));
1464 procmime_mimeinfo_free_all(mimeinfo);
1468 #define SIGNAL_BLOCK(buffer) { \
1469 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1470 G_CALLBACK(compose_changed_cb), \
1472 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1473 G_CALLBACK(text_inserted), \
1477 #define SIGNAL_UNBLOCK(buffer) { \
1478 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1479 G_CALLBACK(compose_changed_cb), \
1481 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1482 G_CALLBACK(text_inserted), \
1486 static Compose *compose_generic_reply(MsgInfo *msginfo,
1487 ComposeQuoteMode quote_mode,
1488 gboolean to_all, gboolean to_ml,
1490 gboolean followup_and_reply_to,
1494 PrefsAccount *account = NULL;
1495 GtkTextView *textview;
1496 GtkTextBuffer *textbuf;
1497 gboolean quote = FALSE;
1498 const gchar *qmark = NULL;
1499 const gchar *body_fmt = NULL;
1500 gchar *s_system = NULL;
1502 cm_return_val_if_fail(msginfo != NULL, NULL);
1503 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1505 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1507 cm_return_val_if_fail(account != NULL, NULL);
1509 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1511 compose->updating = TRUE;
1513 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1514 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1516 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1517 if (!compose->replyinfo)
1518 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1520 compose_extract_original_charset(compose);
1522 if (msginfo->folder && msginfo->folder->ret_rcpt)
1523 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1525 /* Set save folder */
1526 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1527 gchar *folderidentifier;
1529 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1530 folderidentifier = folder_item_get_identifier(msginfo->folder);
1531 compose_set_save_to(compose, folderidentifier);
1532 g_free(folderidentifier);
1535 if (compose_parse_header(compose, msginfo) < 0) {
1536 compose->updating = FALSE;
1537 compose_destroy(compose);
1541 /* override from name according to folder properties */
1542 if (msginfo->folder && msginfo->folder->prefs &&
1543 msginfo->folder->prefs->reply_with_format &&
1544 msginfo->folder->prefs->reply_override_from_format &&
1545 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1550 /* decode \-escape sequences in the internal representation of the quote format */
1551 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1552 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1555 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1556 compose->gtkaspell);
1558 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1560 quote_fmt_scan_string(tmp);
1563 buf = quote_fmt_get_buffer();
1565 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1567 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1568 quote_fmt_reset_vartable();
1573 textview = (GTK_TEXT_VIEW(compose->text));
1574 textbuf = gtk_text_view_get_buffer(textview);
1575 compose_create_tags(textview, compose);
1577 undo_block(compose->undostruct);
1579 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1582 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1583 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1584 /* use the reply format of folder (if enabled), or the account's one
1585 (if enabled) or fallback to the global reply format, which is always
1586 enabled (even if empty), and use the relevant quotemark */
1588 if (msginfo->folder && msginfo->folder->prefs &&
1589 msginfo->folder->prefs->reply_with_format) {
1590 qmark = msginfo->folder->prefs->reply_quotemark;
1591 body_fmt = msginfo->folder->prefs->reply_body_format;
1593 } else if (account->reply_with_format) {
1594 qmark = account->reply_quotemark;
1595 body_fmt = account->reply_body_format;
1598 qmark = prefs_common.quotemark;
1599 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1600 body_fmt = gettext(prefs_common.quotefmt);
1607 /* empty quotemark is not allowed */
1608 if (qmark == NULL || *qmark == '\0')
1610 compose_quote_fmt(compose, compose->replyinfo,
1611 body_fmt, qmark, body, FALSE, TRUE,
1612 _("The body of the \"Reply\" template has an error at line %d."));
1613 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1614 quote_fmt_reset_vartable();
1616 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1617 gtkaspell_highlight_all(compose->gtkaspell);
1621 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1622 compose_force_encryption(compose, account, FALSE, s_system);
1625 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1626 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1627 compose_force_signing(compose, account, s_system);
1631 SIGNAL_BLOCK(textbuf);
1633 if (account->auto_sig)
1634 compose_insert_sig(compose, FALSE);
1636 compose_wrap_all(compose);
1638 SIGNAL_UNBLOCK(textbuf);
1640 gtk_widget_grab_focus(compose->text);
1642 undo_unblock(compose->undostruct);
1644 if (prefs_common.auto_exteditor)
1645 compose_exec_ext_editor(compose);
1647 compose->modified = FALSE;
1648 compose_set_title(compose);
1650 compose->updating = FALSE;
1651 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1652 SCROLL_TO_CURSOR(compose);
1654 if (compose->deferred_destroy) {
1655 compose_destroy(compose);
1663 #define INSERT_FW_HEADER(var, hdr) \
1664 if (msginfo->var && *msginfo->var) { \
1665 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1666 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1667 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1670 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1671 gboolean as_attach, const gchar *body,
1672 gboolean no_extedit,
1676 GtkTextView *textview;
1677 GtkTextBuffer *textbuf;
1681 cm_return_val_if_fail(msginfo != NULL, NULL);
1682 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1685 !(account = compose_guess_forward_account_from_msginfo
1687 account = cur_account;
1689 if (!prefs_common.forward_as_attachment)
1690 mode = COMPOSE_FORWARD_INLINE;
1692 mode = COMPOSE_FORWARD;
1693 compose = compose_create(account, msginfo->folder, mode, batch);
1695 compose->updating = TRUE;
1696 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1697 if (!compose->fwdinfo)
1698 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1700 compose_extract_original_charset(compose);
1702 if (msginfo->subject && *msginfo->subject) {
1703 gchar *buf, *buf2, *p;
1705 buf = p = g_strdup(msginfo->subject);
1706 p += subject_get_prefix_length(p);
1707 memmove(buf, p, strlen(p) + 1);
1709 buf2 = g_strdup_printf("Fw: %s", buf);
1710 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1716 /* override from name according to folder properties */
1717 if (msginfo->folder && msginfo->folder->prefs &&
1718 msginfo->folder->prefs->forward_with_format &&
1719 msginfo->folder->prefs->forward_override_from_format &&
1720 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1724 MsgInfo *full_msginfo = NULL;
1727 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1729 full_msginfo = procmsg_msginfo_copy(msginfo);
1731 /* decode \-escape sequences in the internal representation of the quote format */
1732 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1733 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1736 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1737 compose->gtkaspell);
1739 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1741 quote_fmt_scan_string(tmp);
1744 buf = quote_fmt_get_buffer();
1746 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1748 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1749 quote_fmt_reset_vartable();
1752 procmsg_msginfo_free(full_msginfo);
1755 textview = GTK_TEXT_VIEW(compose->text);
1756 textbuf = gtk_text_view_get_buffer(textview);
1757 compose_create_tags(textview, compose);
1759 undo_block(compose->undostruct);
1763 msgfile = procmsg_get_message_file(msginfo);
1764 if (!is_file_exist(msgfile))
1765 g_warning("%s: file not exist\n", msgfile);
1767 compose_attach_append(compose, msgfile, msgfile,
1768 "message/rfc822", NULL);
1772 const gchar *qmark = NULL;
1773 const gchar *body_fmt = NULL;
1774 MsgInfo *full_msginfo;
1776 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1777 body_fmt = gettext(prefs_common.fw_quotefmt);
1781 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1783 full_msginfo = procmsg_msginfo_copy(msginfo);
1785 /* use the forward format of folder (if enabled), or the account's one
1786 (if enabled) or fallback to the global forward format, which is always
1787 enabled (even if empty), and use the relevant quotemark */
1788 if (msginfo->folder && msginfo->folder->prefs &&
1789 msginfo->folder->prefs->forward_with_format) {
1790 qmark = msginfo->folder->prefs->forward_quotemark;
1791 body_fmt = msginfo->folder->prefs->forward_body_format;
1793 } else if (account->forward_with_format) {
1794 qmark = account->forward_quotemark;
1795 body_fmt = account->forward_body_format;
1798 qmark = prefs_common.fw_quotemark;
1799 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1800 body_fmt = gettext(prefs_common.fw_quotefmt);
1805 /* empty quotemark is not allowed */
1806 if (qmark == NULL || *qmark == '\0')
1809 compose_quote_fmt(compose, full_msginfo,
1810 body_fmt, qmark, body, FALSE, TRUE,
1811 _("The body of the \"Forward\" template has an error at line %d."));
1812 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1813 quote_fmt_reset_vartable();
1814 compose_attach_parts(compose, msginfo);
1816 procmsg_msginfo_free(full_msginfo);
1818 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1819 gtkaspell_highlight_all(compose->gtkaspell);
1823 SIGNAL_BLOCK(textbuf);
1825 if (account->auto_sig)
1826 compose_insert_sig(compose, FALSE);
1828 compose_wrap_all(compose);
1830 SIGNAL_UNBLOCK(textbuf);
1832 gtk_text_buffer_get_start_iter(textbuf, &iter);
1833 gtk_text_buffer_place_cursor(textbuf, &iter);
1835 gtk_widget_grab_focus(compose->header_last->entry);
1837 if (!no_extedit && prefs_common.auto_exteditor)
1838 compose_exec_ext_editor(compose);
1841 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1842 gchar *folderidentifier;
1844 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1845 folderidentifier = folder_item_get_identifier(msginfo->folder);
1846 compose_set_save_to(compose, folderidentifier);
1847 g_free(folderidentifier);
1850 undo_unblock(compose->undostruct);
1852 compose->modified = FALSE;
1853 compose_set_title(compose);
1855 compose->updating = FALSE;
1856 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1857 SCROLL_TO_CURSOR(compose);
1859 if (compose->deferred_destroy) {
1860 compose_destroy(compose);
1864 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1869 #undef INSERT_FW_HEADER
1871 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1874 GtkTextView *textview;
1875 GtkTextBuffer *textbuf;
1879 gboolean single_mail = TRUE;
1881 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1883 if (g_slist_length(msginfo_list) > 1)
1884 single_mail = FALSE;
1886 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1887 if (((MsgInfo *)msginfo->data)->folder == NULL)
1890 /* guess account from first selected message */
1892 !(account = compose_guess_forward_account_from_msginfo
1893 (msginfo_list->data)))
1894 account = cur_account;
1896 cm_return_val_if_fail(account != NULL, NULL);
1898 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1899 if (msginfo->data) {
1900 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1901 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1905 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1906 g_warning("no msginfo_list");
1910 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1912 compose->updating = TRUE;
1914 /* override from name according to folder properties */
1915 if (msginfo_list->data) {
1916 MsgInfo *msginfo = msginfo_list->data;
1918 if (msginfo->folder && msginfo->folder->prefs &&
1919 msginfo->folder->prefs->forward_with_format &&
1920 msginfo->folder->prefs->forward_override_from_format &&
1921 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1926 /* decode \-escape sequences in the internal representation of the quote format */
1927 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1928 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1931 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1932 compose->gtkaspell);
1934 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1936 quote_fmt_scan_string(tmp);
1939 buf = quote_fmt_get_buffer();
1941 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1943 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1944 quote_fmt_reset_vartable();
1950 textview = GTK_TEXT_VIEW(compose->text);
1951 textbuf = gtk_text_view_get_buffer(textview);
1952 compose_create_tags(textview, compose);
1954 undo_block(compose->undostruct);
1955 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1956 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1958 if (!is_file_exist(msgfile))
1959 g_warning("%s: file not exist\n", msgfile);
1961 compose_attach_append(compose, msgfile, msgfile,
1962 "message/rfc822", NULL);
1967 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1968 if (info->subject && *info->subject) {
1969 gchar *buf, *buf2, *p;
1971 buf = p = g_strdup(info->subject);
1972 p += subject_get_prefix_length(p);
1973 memmove(buf, p, strlen(p) + 1);
1975 buf2 = g_strdup_printf("Fw: %s", buf);
1976 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1982 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1983 _("Fw: multiple emails"));
1986 SIGNAL_BLOCK(textbuf);
1988 if (account->auto_sig)
1989 compose_insert_sig(compose, FALSE);
1991 compose_wrap_all(compose);
1993 SIGNAL_UNBLOCK(textbuf);
1995 gtk_text_buffer_get_start_iter(textbuf, &iter);
1996 gtk_text_buffer_place_cursor(textbuf, &iter);
1998 gtk_widget_grab_focus(compose->header_last->entry);
1999 undo_unblock(compose->undostruct);
2000 compose->modified = FALSE;
2001 compose_set_title(compose);
2003 compose->updating = FALSE;
2004 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2005 SCROLL_TO_CURSOR(compose);
2007 if (compose->deferred_destroy) {
2008 compose_destroy(compose);
2012 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2017 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2019 GtkTextIter start = *iter;
2020 GtkTextIter end_iter;
2021 int start_pos = gtk_text_iter_get_offset(&start);
2023 if (!compose->account->sig_sep)
2026 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2027 start_pos+strlen(compose->account->sig_sep));
2029 /* check sig separator */
2030 str = gtk_text_iter_get_text(&start, &end_iter);
2031 if (!strcmp(str, compose->account->sig_sep)) {
2033 /* check end of line (\n) */
2034 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2035 start_pos+strlen(compose->account->sig_sep));
2036 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2037 start_pos+strlen(compose->account->sig_sep)+1);
2038 tmp = gtk_text_iter_get_text(&start, &end_iter);
2039 if (!strcmp(tmp,"\n")) {
2051 static void compose_colorize_signature(Compose *compose)
2053 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2055 GtkTextIter end_iter;
2056 gtk_text_buffer_get_start_iter(buffer, &iter);
2057 while (gtk_text_iter_forward_line(&iter))
2058 if (compose_is_sig_separator(compose, buffer, &iter)) {
2059 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2060 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2064 #define BLOCK_WRAP() { \
2065 prev_autowrap = compose->autowrap; \
2066 buffer = gtk_text_view_get_buffer( \
2067 GTK_TEXT_VIEW(compose->text)); \
2068 compose->autowrap = FALSE; \
2070 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2071 G_CALLBACK(compose_changed_cb), \
2073 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2074 G_CALLBACK(text_inserted), \
2077 #define UNBLOCK_WRAP() { \
2078 compose->autowrap = prev_autowrap; \
2079 if (compose->autowrap) { \
2080 gint old = compose->draft_timeout_tag; \
2081 compose->draft_timeout_tag = -2; \
2082 compose_wrap_all(compose); \
2083 compose->draft_timeout_tag = old; \
2086 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2087 G_CALLBACK(compose_changed_cb), \
2089 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2090 G_CALLBACK(text_inserted), \
2094 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2096 Compose *compose = NULL;
2097 PrefsAccount *account = NULL;
2098 GtkTextView *textview;
2099 GtkTextBuffer *textbuf;
2103 gchar buf[BUFFSIZE];
2104 gboolean use_signing = FALSE;
2105 gboolean use_encryption = FALSE;
2106 gchar *privacy_system = NULL;
2107 int priority = PRIORITY_NORMAL;
2108 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2109 gboolean autowrap = prefs_common.autowrap;
2110 gboolean autoindent = prefs_common.auto_indent;
2112 cm_return_val_if_fail(msginfo != NULL, NULL);
2113 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2115 if (compose_put_existing_to_front(msginfo)) {
2119 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2120 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2121 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2122 gchar queueheader_buf[BUFFSIZE];
2125 /* Select Account from queue headers */
2126 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2127 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2128 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2129 account = account_find_from_id(id);
2131 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2132 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2133 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2134 account = account_find_from_id(id);
2136 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2137 sizeof(queueheader_buf), "NAID:")) {
2138 id = atoi(&queueheader_buf[strlen("NAID:")]);
2139 account = account_find_from_id(id);
2141 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2142 sizeof(queueheader_buf), "MAID:")) {
2143 id = atoi(&queueheader_buf[strlen("MAID:")]);
2144 account = account_find_from_id(id);
2146 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2147 sizeof(queueheader_buf), "S:")) {
2148 account = account_find_from_address(queueheader_buf, FALSE);
2150 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2151 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2152 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2153 use_signing = param;
2156 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2157 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2158 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2159 use_signing = param;
2162 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2163 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2164 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2165 use_encryption = param;
2167 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2168 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2169 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2170 use_encryption = param;
2172 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2173 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2174 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2177 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2178 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2179 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2182 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2183 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2184 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2186 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2187 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2188 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2190 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2191 sizeof(queueheader_buf), "X-Priority: ")) {
2192 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2195 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2196 sizeof(queueheader_buf), "RMID:")) {
2197 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2198 if (tokens[0] && tokens[1] && tokens[2]) {
2199 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2200 if (orig_item != NULL) {
2201 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2206 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2207 sizeof(queueheader_buf), "FMID:")) {
2208 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2209 if (tokens[0] && tokens[1] && tokens[2]) {
2210 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2211 if (orig_item != NULL) {
2212 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2218 account = msginfo->folder->folder->account;
2221 if (!account && prefs_common.reedit_account_autosel) {
2222 gchar from[BUFFSIZE];
2223 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2224 extract_address(from);
2225 account = account_find_from_address(from, FALSE);
2229 account = cur_account;
2231 cm_return_val_if_fail(account != NULL, NULL);
2233 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2235 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2236 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2237 compose->autowrap = autowrap;
2238 compose->replyinfo = replyinfo;
2239 compose->fwdinfo = fwdinfo;
2241 compose->updating = TRUE;
2242 compose->priority = priority;
2244 if (privacy_system != NULL) {
2245 compose->privacy_system = privacy_system;
2246 compose_use_signing(compose, use_signing);
2247 compose_use_encryption(compose, use_encryption);
2248 compose_update_privacy_system_menu_item(compose, FALSE);
2250 activate_privacy_system(compose, account, FALSE);
2253 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2255 compose_extract_original_charset(compose);
2257 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2258 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2259 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2260 gchar queueheader_buf[BUFFSIZE];
2262 /* Set message save folder */
2263 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2264 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2265 compose_set_save_to(compose, &queueheader_buf[4]);
2267 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2268 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2270 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2275 if (compose_parse_header(compose, msginfo) < 0) {
2276 compose->updating = FALSE;
2277 compose_destroy(compose);
2280 compose_reedit_set_entry(compose, msginfo);
2282 textview = GTK_TEXT_VIEW(compose->text);
2283 textbuf = gtk_text_view_get_buffer(textview);
2284 compose_create_tags(textview, compose);
2286 mark = gtk_text_buffer_get_insert(textbuf);
2287 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2289 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2290 G_CALLBACK(compose_changed_cb),
2293 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2294 fp = procmime_get_first_encrypted_text_content(msginfo);
2296 compose_force_encryption(compose, account, TRUE, NULL);
2299 fp = procmime_get_first_text_content(msginfo);
2302 g_warning("Can't get text part\n");
2306 gboolean prev_autowrap = compose->autowrap;
2307 GtkTextBuffer *buffer = textbuf;
2309 while (fgets(buf, sizeof(buf), fp) != NULL) {
2311 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2317 compose_attach_parts(compose, msginfo);
2319 compose_colorize_signature(compose);
2321 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2322 G_CALLBACK(compose_changed_cb),
2325 gtk_widget_grab_focus(compose->text);
2327 if (prefs_common.auto_exteditor) {
2328 compose_exec_ext_editor(compose);
2330 compose->modified = FALSE;
2331 compose_set_title(compose);
2333 compose->updating = FALSE;
2334 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2335 SCROLL_TO_CURSOR(compose);
2337 if (compose->deferred_destroy) {
2338 compose_destroy(compose);
2342 compose->sig_str = account_get_signature_str(compose->account);
2344 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2349 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2356 cm_return_val_if_fail(msginfo != NULL, NULL);
2359 account = account_get_reply_account(msginfo,
2360 prefs_common.reply_account_autosel);
2361 cm_return_val_if_fail(account != NULL, NULL);
2363 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2365 compose->updating = TRUE;
2367 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2368 compose->replyinfo = NULL;
2369 compose->fwdinfo = NULL;
2371 compose_show_first_last_header(compose, TRUE);
2373 gtk_widget_grab_focus(compose->header_last->entry);
2375 filename = procmsg_get_message_file(msginfo);
2377 if (filename == NULL) {
2378 compose->updating = FALSE;
2379 compose_destroy(compose);
2384 compose->redirect_filename = filename;
2386 /* Set save folder */
2387 item = msginfo->folder;
2388 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2389 gchar *folderidentifier;
2391 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2392 folderidentifier = folder_item_get_identifier(item);
2393 compose_set_save_to(compose, folderidentifier);
2394 g_free(folderidentifier);
2397 compose_attach_parts(compose, msginfo);
2399 if (msginfo->subject)
2400 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2402 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2404 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2405 _("The body of the \"Redirect\" template has an error at line %d."));
2406 quote_fmt_reset_vartable();
2407 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2409 compose_colorize_signature(compose);
2412 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2413 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2414 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2416 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2417 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2418 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2419 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2420 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2421 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2422 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2423 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2425 if (compose->toolbar->draft_btn)
2426 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2427 if (compose->toolbar->insert_btn)
2428 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2429 if (compose->toolbar->attach_btn)
2430 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2431 if (compose->toolbar->sig_btn)
2432 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2433 if (compose->toolbar->exteditor_btn)
2434 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2435 if (compose->toolbar->linewrap_current_btn)
2436 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2437 if (compose->toolbar->linewrap_all_btn)
2438 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2440 compose->modified = FALSE;
2441 compose_set_title(compose);
2442 compose->updating = FALSE;
2443 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2444 SCROLL_TO_CURSOR(compose);
2446 if (compose->deferred_destroy) {
2447 compose_destroy(compose);
2451 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2456 GList *compose_get_compose_list(void)
2458 return compose_list;
2461 void compose_entry_append(Compose *compose, const gchar *address,
2462 ComposeEntryType type, ComposePrefType pref_type)
2464 const gchar *header;
2466 gboolean in_quote = FALSE;
2467 if (!address || *address == '\0') return;
2474 header = N_("Bcc:");
2476 case COMPOSE_REPLYTO:
2477 header = N_("Reply-To:");
2479 case COMPOSE_NEWSGROUPS:
2480 header = N_("Newsgroups:");
2482 case COMPOSE_FOLLOWUPTO:
2483 header = N_( "Followup-To:");
2485 case COMPOSE_INREPLYTO:
2486 header = N_( "In-Reply-To:");
2493 header = prefs_common_translated_header_name(header);
2495 cur = begin = (gchar *)address;
2497 /* we separate the line by commas, but not if we're inside a quoted
2499 while (*cur != '\0') {
2501 in_quote = !in_quote;
2502 if (*cur == ',' && !in_quote) {
2503 gchar *tmp = g_strdup(begin);
2505 tmp[cur-begin]='\0';
2508 while (*tmp == ' ' || *tmp == '\t')
2510 compose_add_header_entry(compose, header, tmp, pref_type);
2517 gchar *tmp = g_strdup(begin);
2519 tmp[cur-begin]='\0';
2522 while (*tmp == ' ' || *tmp == '\t')
2524 compose_add_header_entry(compose, header, tmp, pref_type);
2529 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2531 static GdkColor yellow;
2532 static GdkColor black;
2533 static gboolean yellow_initialised = FALSE;
2537 if (!yellow_initialised) {
2538 gdk_color_parse("#f5f6be", &yellow);
2539 gdk_color_parse("#000000", &black);
2540 yellow_initialised = gdk_colormap_alloc_color(
2541 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2542 yellow_initialised &= gdk_colormap_alloc_color(
2543 gdk_colormap_get_system(), &black, FALSE, TRUE);
2546 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2547 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2548 if (gtk_entry_get_text(entry) &&
2549 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2550 if (yellow_initialised) {
2551 gtk_widget_modify_base(
2552 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2553 GTK_STATE_NORMAL, &yellow);
2554 gtk_widget_modify_text(
2555 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2556 GTK_STATE_NORMAL, &black);
2562 void compose_toolbar_cb(gint action, gpointer data)
2564 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2565 Compose *compose = (Compose*)toolbar_item->parent;
2567 cm_return_if_fail(compose != NULL);
2571 compose_send_cb(NULL, compose);
2574 compose_send_later_cb(NULL, compose);
2577 compose_draft(compose, COMPOSE_QUIT_EDITING);
2580 compose_insert_file_cb(NULL, compose);
2583 compose_attach_cb(NULL, compose);
2586 compose_insert_sig(compose, FALSE);
2589 compose_ext_editor_cb(NULL, compose);
2591 case A_LINEWRAP_CURRENT:
2592 compose_beautify_paragraph(compose, NULL, TRUE);
2594 case A_LINEWRAP_ALL:
2595 compose_wrap_all_full(compose, TRUE);
2598 compose_address_cb(NULL, compose);
2601 case A_CHECK_SPELLING:
2602 compose_check_all(NULL, compose);
2610 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2615 gchar *subject = NULL;
2619 gchar **attach = NULL;
2620 gchar *inreplyto = NULL;
2621 MailField mfield = NO_FIELD_PRESENT;
2623 /* get mailto parts but skip from */
2624 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2627 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2628 mfield = TO_FIELD_PRESENT;
2631 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2633 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2635 if (!g_utf8_validate (subject, -1, NULL)) {
2636 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2637 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2640 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2642 mfield = SUBJECT_FIELD_PRESENT;
2645 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2646 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2649 gboolean prev_autowrap = compose->autowrap;
2651 compose->autowrap = FALSE;
2653 mark = gtk_text_buffer_get_insert(buffer);
2654 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2656 if (!g_utf8_validate (body, -1, NULL)) {
2657 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2658 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2661 gtk_text_buffer_insert(buffer, &iter, body, -1);
2663 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2665 compose->autowrap = prev_autowrap;
2666 if (compose->autowrap)
2667 compose_wrap_all(compose);
2668 mfield = BODY_FIELD_PRESENT;
2672 gint i = 0, att = 0;
2673 gchar *warn_files = NULL;
2674 while (attach[i] != NULL) {
2675 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2676 if (utf8_filename) {
2677 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2678 gchar *tmp = g_strdup_printf("%s%s\n",
2679 warn_files?warn_files:"",
2685 g_free(utf8_filename);
2687 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2692 alertpanel_notice(ngettext(
2693 "The following file has been attached: \n%s",
2694 "The following files have been attached: \n%s", att), warn_files);
2699 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2712 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2714 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2715 {"Cc:", NULL, TRUE},
2716 {"References:", NULL, FALSE},
2717 {"Bcc:", NULL, TRUE},
2718 {"Newsgroups:", NULL, TRUE},
2719 {"Followup-To:", NULL, TRUE},
2720 {"List-Post:", NULL, FALSE},
2721 {"X-Priority:", NULL, FALSE},
2722 {NULL, NULL, FALSE}};
2738 cm_return_val_if_fail(msginfo != NULL, -1);
2740 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2741 procheader_get_header_fields(fp, hentry);
2744 if (hentry[H_REPLY_TO].body != NULL) {
2745 if (hentry[H_REPLY_TO].body[0] != '\0') {
2747 conv_unmime_header(hentry[H_REPLY_TO].body,
2750 g_free(hentry[H_REPLY_TO].body);
2751 hentry[H_REPLY_TO].body = NULL;
2753 if (hentry[H_CC].body != NULL) {
2754 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2755 g_free(hentry[H_CC].body);
2756 hentry[H_CC].body = NULL;
2758 if (hentry[H_REFERENCES].body != NULL) {
2759 if (compose->mode == COMPOSE_REEDIT)
2760 compose->references = hentry[H_REFERENCES].body;
2762 compose->references = compose_parse_references
2763 (hentry[H_REFERENCES].body, msginfo->msgid);
2764 g_free(hentry[H_REFERENCES].body);
2766 hentry[H_REFERENCES].body = NULL;
2768 if (hentry[H_BCC].body != NULL) {
2769 if (compose->mode == COMPOSE_REEDIT)
2771 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2772 g_free(hentry[H_BCC].body);
2773 hentry[H_BCC].body = NULL;
2775 if (hentry[H_NEWSGROUPS].body != NULL) {
2776 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2777 hentry[H_NEWSGROUPS].body = NULL;
2779 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2780 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2781 compose->followup_to =
2782 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2785 g_free(hentry[H_FOLLOWUP_TO].body);
2786 hentry[H_FOLLOWUP_TO].body = NULL;
2788 if (hentry[H_LIST_POST].body != NULL) {
2789 gchar *to = NULL, *start = NULL;
2791 extract_address(hentry[H_LIST_POST].body);
2792 if (hentry[H_LIST_POST].body[0] != '\0') {
2793 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2795 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2796 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2799 g_free(compose->ml_post);
2800 compose->ml_post = to;
2803 g_free(hentry[H_LIST_POST].body);
2804 hentry[H_LIST_POST].body = NULL;
2807 /* CLAWS - X-Priority */
2808 if (compose->mode == COMPOSE_REEDIT)
2809 if (hentry[H_X_PRIORITY].body != NULL) {
2812 priority = atoi(hentry[H_X_PRIORITY].body);
2813 g_free(hentry[H_X_PRIORITY].body);
2815 hentry[H_X_PRIORITY].body = NULL;
2817 if (priority < PRIORITY_HIGHEST ||
2818 priority > PRIORITY_LOWEST)
2819 priority = PRIORITY_NORMAL;
2821 compose->priority = priority;
2824 if (compose->mode == COMPOSE_REEDIT) {
2825 if (msginfo->inreplyto && *msginfo->inreplyto)
2826 compose->inreplyto = g_strdup(msginfo->inreplyto);
2830 if (msginfo->msgid && *msginfo->msgid)
2831 compose->inreplyto = g_strdup(msginfo->msgid);
2833 if (!compose->references) {
2834 if (msginfo->msgid && *msginfo->msgid) {
2835 if (msginfo->inreplyto && *msginfo->inreplyto)
2836 compose->references =
2837 g_strdup_printf("<%s>\n\t<%s>",
2841 compose->references =
2842 g_strconcat("<", msginfo->msgid, ">",
2844 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2845 compose->references =
2846 g_strconcat("<", msginfo->inreplyto, ">",
2854 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2856 GSList *ref_id_list, *cur;
2860 ref_id_list = references_list_append(NULL, ref);
2861 if (!ref_id_list) return NULL;
2862 if (msgid && *msgid)
2863 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2868 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2869 /* "<" + Message-ID + ">" + CR+LF+TAB */
2870 len += strlen((gchar *)cur->data) + 5;
2872 if (len > MAX_REFERENCES_LEN) {
2873 /* remove second message-ID */
2874 if (ref_id_list && ref_id_list->next &&
2875 ref_id_list->next->next) {
2876 g_free(ref_id_list->next->data);
2877 ref_id_list = g_slist_remove
2878 (ref_id_list, ref_id_list->next->data);
2880 slist_free_strings(ref_id_list);
2881 g_slist_free(ref_id_list);
2888 new_ref = g_string_new("");
2889 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2890 if (new_ref->len > 0)
2891 g_string_append(new_ref, "\n\t");
2892 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2895 slist_free_strings(ref_id_list);
2896 g_slist_free(ref_id_list);
2898 new_ref_str = new_ref->str;
2899 g_string_free(new_ref, FALSE);
2904 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2905 const gchar *fmt, const gchar *qmark,
2906 const gchar *body, gboolean rewrap,
2907 gboolean need_unescape,
2908 const gchar *err_msg)
2910 MsgInfo* dummyinfo = NULL;
2911 gchar *quote_str = NULL;
2913 gboolean prev_autowrap;
2914 const gchar *trimmed_body = body;
2915 gint cursor_pos = -1;
2916 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2917 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2922 SIGNAL_BLOCK(buffer);
2925 dummyinfo = compose_msginfo_new_from_compose(compose);
2926 msginfo = dummyinfo;
2929 if (qmark != NULL) {
2931 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2932 compose->gtkaspell);
2934 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2936 quote_fmt_scan_string(qmark);
2939 buf = quote_fmt_get_buffer();
2941 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2943 Xstrdup_a(quote_str, buf, goto error)
2946 if (fmt && *fmt != '\0') {
2949 while (*trimmed_body == '\n')
2953 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2954 compose->gtkaspell);
2956 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2958 if (need_unescape) {
2961 /* decode \-escape sequences in the internal representation of the quote format */
2962 tmp = g_malloc(strlen(fmt)+1);
2963 pref_get_unescaped_pref(tmp, fmt);
2964 quote_fmt_scan_string(tmp);
2968 quote_fmt_scan_string(fmt);
2972 buf = quote_fmt_get_buffer();
2974 gint line = quote_fmt_get_line();
2975 alertpanel_error(err_msg, line);
2981 prev_autowrap = compose->autowrap;
2982 compose->autowrap = FALSE;
2984 mark = gtk_text_buffer_get_insert(buffer);
2985 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2986 if (g_utf8_validate(buf, -1, NULL)) {
2987 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2989 gchar *tmpout = NULL;
2990 tmpout = conv_codeset_strdup
2991 (buf, conv_get_locale_charset_str_no_utf8(),
2993 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2995 tmpout = g_malloc(strlen(buf)*2+1);
2996 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2998 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3002 cursor_pos = quote_fmt_get_cursor_pos();
3003 if (cursor_pos == -1)
3004 cursor_pos = gtk_text_iter_get_offset(&iter);
3005 compose->set_cursor_pos = cursor_pos;
3007 gtk_text_buffer_get_start_iter(buffer, &iter);
3008 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3009 gtk_text_buffer_place_cursor(buffer, &iter);
3011 compose->autowrap = prev_autowrap;
3012 if (compose->autowrap && rewrap)
3013 compose_wrap_all(compose);
3020 SIGNAL_UNBLOCK(buffer);
3022 procmsg_msginfo_free( dummyinfo );
3027 /* if ml_post is of type addr@host and from is of type
3028 * addr-anything@host, return TRUE
3030 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3032 gchar *left_ml = NULL;
3033 gchar *right_ml = NULL;
3034 gchar *left_from = NULL;
3035 gchar *right_from = NULL;
3036 gboolean result = FALSE;
3038 if (!ml_post || !from)
3041 left_ml = g_strdup(ml_post);
3042 if (strstr(left_ml, "@")) {
3043 right_ml = strstr(left_ml, "@")+1;
3044 *(strstr(left_ml, "@")) = '\0';
3047 left_from = g_strdup(from);
3048 if (strstr(left_from, "@")) {
3049 right_from = strstr(left_from, "@")+1;
3050 *(strstr(left_from, "@")) = '\0';
3053 if (left_ml && left_from && right_ml && right_from
3054 && !strncmp(left_from, left_ml, strlen(left_ml))
3055 && !strcmp(right_from, right_ml)) {
3064 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3065 gboolean respect_default_to)
3069 if (!folder || !folder->prefs)
3072 if (respect_default_to && folder->prefs->enable_default_to) {
3073 compose_entry_append(compose, folder->prefs->default_to,
3074 COMPOSE_TO, PREF_FOLDER);
3075 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3077 if (folder->prefs->enable_default_cc)
3078 compose_entry_append(compose, folder->prefs->default_cc,
3079 COMPOSE_CC, PREF_FOLDER);
3080 if (folder->prefs->enable_default_bcc)
3081 compose_entry_append(compose, folder->prefs->default_bcc,
3082 COMPOSE_BCC, PREF_FOLDER);
3083 if (folder->prefs->enable_default_replyto)
3084 compose_entry_append(compose, folder->prefs->default_replyto,
3085 COMPOSE_REPLYTO, PREF_FOLDER);
3088 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3093 if (!compose || !msginfo)
3096 if (msginfo->subject && *msginfo->subject) {
3097 buf = p = g_strdup(msginfo->subject);
3098 p += subject_get_prefix_length(p);
3099 memmove(buf, p, strlen(p) + 1);
3101 buf2 = g_strdup_printf("Re: %s", buf);
3102 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3107 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3110 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3111 gboolean to_all, gboolean to_ml,
3113 gboolean followup_and_reply_to)
3115 GSList *cc_list = NULL;
3118 gchar *replyto = NULL;
3119 gchar *ac_email = NULL;
3121 gboolean reply_to_ml = FALSE;
3122 gboolean default_reply_to = FALSE;
3124 cm_return_if_fail(compose->account != NULL);
3125 cm_return_if_fail(msginfo != NULL);
3127 reply_to_ml = to_ml && compose->ml_post;
3129 default_reply_to = msginfo->folder &&
3130 msginfo->folder->prefs->enable_default_reply_to;
3132 if (compose->account->protocol != A_NNTP) {
3133 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3135 if (reply_to_ml && !default_reply_to) {
3137 gboolean is_subscr = is_subscription(compose->ml_post,
3140 /* normal answer to ml post with a reply-to */
3141 compose_entry_append(compose,
3143 COMPOSE_TO, PREF_ML);
3144 if (compose->replyto)
3145 compose_entry_append(compose,
3147 COMPOSE_CC, PREF_ML);
3149 /* answer to subscription confirmation */
3150 if (compose->replyto)
3151 compose_entry_append(compose,
3153 COMPOSE_TO, PREF_ML);
3154 else if (msginfo->from)
3155 compose_entry_append(compose,
3157 COMPOSE_TO, PREF_ML);
3160 else if (!(to_all || to_sender) && default_reply_to) {
3161 compose_entry_append(compose,
3162 msginfo->folder->prefs->default_reply_to,
3163 COMPOSE_TO, PREF_FOLDER);
3164 compose_entry_mark_default_to(compose,
3165 msginfo->folder->prefs->default_reply_to);
3170 Xstrdup_a(tmp1, msginfo->from, return);
3171 extract_address(tmp1);
3172 if (to_all || to_sender ||
3173 !account_find_from_address(tmp1, FALSE))
3174 compose_entry_append(compose,
3175 (compose->replyto && !to_sender)