2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 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 2 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, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <gtk/gtkmain.h>
34 #include <gtk/gtkmenu.h>
35 #include <gtk/gtkmenuitem.h>
36 #include <gtk/gtkitemfactory.h>
37 #include <gtk/gtkcheckmenuitem.h>
38 #include <gtk/gtkoptionmenu.h>
39 #include <gtk/gtkwidget.h>
40 #include <gtk/gtkvpaned.h>
41 #include <gtk/gtkentry.h>
42 #include <gtk/gtkeditable.h>
43 #include <gtk/gtkwindow.h>
44 #include <gtk/gtksignal.h>
45 #include <gtk/gtkvbox.h>
46 #include <gtk/gtkcontainer.h>
47 #include <gtk/gtkhandlebox.h>
48 #include <gtk/gtktoolbar.h>
49 #include <gtk/gtktable.h>
50 #include <gtk/gtkhbox.h>
51 #include <gtk/gtklabel.h>
52 #include <gtk/gtkscrolledwindow.h>
53 #include <gtk/gtktreeview.h>
54 #include <gtk/gtkliststore.h>
55 #include <gtk/gtktreeselection.h>
56 #include <gtk/gtktreemodel.h>
58 #include <gtk/gtkdnd.h>
59 #include <gtk/gtkclipboard.h>
60 #include <pango/pango-break.h>
65 #include <sys/types.h>
71 # include <sys/wait.h>
75 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
79 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
86 #include "mainwindow.h"
88 #include "addressbook.h"
89 #include "folderview.h"
92 #include "stock_pixmap.h"
93 #include "send_message.h"
96 #include "customheader.h"
97 #include "prefs_common.h"
98 #include "prefs_account.h"
102 #include "procheader.h"
103 #include "procmime.h"
104 #include "statusbar.h"
107 #include "quoted-printable.h"
108 #include "codeconv.h"
110 #include "gtkutils.h"
112 #include "alertpanel.h"
113 #include "manage_window.h"
114 #include "gtkshruler.h"
116 #include "addr_compl.h"
117 #include "quote_fmt.h"
119 #include "foldersel.h"
122 #include "message_search.h"
123 #include "combobox.h"
137 #define N_ATTACH_COLS (N_COL_COLUMNS)
141 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
142 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
143 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
144 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
145 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
146 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
147 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
148 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
149 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
150 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
151 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
152 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
153 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
154 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE_N,
155 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
156 } ComposeCallAdvancedAction;
160 PRIORITY_HIGHEST = 1,
169 COMPOSE_INSERT_SUCCESS,
170 COMPOSE_INSERT_READ_ERROR,
171 COMPOSE_INSERT_INVALID_CHARACTER,
172 COMPOSE_INSERT_NO_FILE
173 } ComposeInsertResult;
177 COMPOSE_WRITE_FOR_SEND,
178 COMPOSE_WRITE_FOR_STORE
183 COMPOSE_QUOTE_FORCED,
188 #define B64_LINE_SIZE 57
189 #define B64_BUFFSIZE 77
191 #define MAX_REFERENCES_LEN 999
193 static GList *compose_list = NULL;
195 static Compose *compose_generic_new (PrefsAccount *account,
198 GPtrArray *attach_files,
199 GList *listAddress );
201 static Compose *compose_create (PrefsAccount *account,
205 static void compose_entry_mark_default_to (Compose *compose,
206 const gchar *address);
207 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
208 ComposeQuoteMode quote_mode,
212 static Compose *compose_forward_multiple (PrefsAccount *account,
213 GSList *msginfo_list);
214 static Compose *compose_reply (MsgInfo *msginfo,
215 ComposeQuoteMode quote_mode,
220 static Compose *compose_reply_mode (ComposeMode mode,
221 GSList *msginfo_list,
223 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
224 static void compose_update_privacy_systems_menu(Compose *compose);
226 static GtkWidget *compose_account_option_menu_create
228 static void compose_set_out_encoding (Compose *compose);
229 static void compose_set_template_menu (Compose *compose);
230 static void compose_template_apply (Compose *compose,
233 static void compose_destroy (Compose *compose);
235 static void compose_entries_set (Compose *compose,
236 const gchar *mailto);
237 static gint compose_parse_header (Compose *compose,
239 static gchar *compose_parse_references (const gchar *ref,
242 static gchar *compose_quote_fmt (Compose *compose,
248 gboolean need_unescape,
249 const gchar *err_msg);
251 static void compose_reply_set_entry (Compose *compose,
257 followup_and_reply_to);
258 static void compose_reedit_set_entry (Compose *compose,
261 static void compose_insert_sig (Compose *compose,
263 static gchar *compose_get_signature_str (Compose *compose);
264 static ComposeInsertResult compose_insert_file (Compose *compose,
267 static gboolean compose_attach_append (Compose *compose,
270 const gchar *content_type);
271 static void compose_attach_parts (Compose *compose,
274 static void compose_beautify_paragraph (Compose *compose,
275 GtkTextIter *par_iter,
277 static void compose_wrap_all (Compose *compose);
278 static void compose_wrap_all_full (Compose *compose,
281 static void compose_set_title (Compose *compose);
282 static void compose_select_account (Compose *compose,
283 PrefsAccount *account,
286 static PrefsAccount *compose_current_mail_account(void);
287 /* static gint compose_send (Compose *compose); */
288 static gboolean compose_check_for_valid_recipient
290 static gboolean compose_check_entries (Compose *compose,
291 gboolean check_everything);
292 static gint compose_write_to_file (Compose *compose,
295 gboolean attach_parts);
296 static gint compose_write_body_to_file (Compose *compose,
298 static gint compose_remove_reedit_target (Compose *compose,
300 static void compose_remove_draft (Compose *compose);
301 static gint compose_queue_sub (Compose *compose,
305 gboolean check_subject,
306 gboolean remove_reedit_target);
307 static void compose_add_attachments (Compose *compose,
309 static gchar *compose_get_header (Compose *compose);
311 static void compose_convert_header (Compose *compose,
316 gboolean addr_field);
318 static void compose_attach_info_free (AttachInfo *ainfo);
319 static void compose_attach_remove_selected (Compose *compose);
321 static void compose_attach_property (Compose *compose);
322 static void compose_attach_property_create (gboolean *cancelled);
323 static void attach_property_ok (GtkWidget *widget,
324 gboolean *cancelled);
325 static void attach_property_cancel (GtkWidget *widget,
326 gboolean *cancelled);
327 static gint attach_property_delete_event (GtkWidget *widget,
329 gboolean *cancelled);
330 static gboolean attach_property_key_pressed (GtkWidget *widget,
332 gboolean *cancelled);
334 static void compose_exec_ext_editor (Compose *compose);
336 static gint compose_exec_ext_editor_real (const gchar *file);
337 static gboolean compose_ext_editor_kill (Compose *compose);
338 static gboolean compose_input_cb (GIOChannel *source,
339 GIOCondition condition,
341 static void compose_set_ext_editor_sensitive (Compose *compose,
343 #endif /* G_OS_UNIX */
345 static void compose_undo_state_changed (UndoMain *undostruct,
350 static void compose_create_header_entry (Compose *compose);
351 static void compose_add_header_entry (Compose *compose, const gchar *header, gchar *text);
352 static void compose_remove_header_entries(Compose *compose);
354 static void compose_update_priority_menu_item(Compose * compose);
356 static void compose_spell_menu_changed (void *data);
358 static void compose_add_field_list ( Compose *compose,
359 GList *listAddress );
361 /* callback functions */
363 static gboolean compose_edit_size_alloc (GtkEditable *widget,
364 GtkAllocation *allocation,
365 GtkSHRuler *shruler);
366 static void account_activated (GtkComboBox *optmenu,
368 static void attach_selected (GtkTreeView *tree_view,
369 GtkTreePath *tree_path,
370 GtkTreeViewColumn *column,
372 static gboolean attach_button_pressed (GtkWidget *widget,
373 GdkEventButton *event,
375 static gboolean attach_key_pressed (GtkWidget *widget,
378 static void compose_send_cb (gpointer data,
381 static void compose_send_later_cb (gpointer data,
385 static void compose_draft_cb (gpointer data,
389 static void compose_attach_cb (gpointer data,
392 static void compose_insert_file_cb (gpointer data,
395 static void compose_insert_sig_cb (gpointer data,
399 static void compose_close_cb (gpointer data,
403 static void compose_set_encoding_cb (gpointer data,
407 static void compose_address_cb (gpointer data,
410 static void compose_template_activate_cb(GtkWidget *widget,
413 static void compose_ext_editor_cb (gpointer data,
417 static gint compose_delete_cb (GtkWidget *widget,
421 static void compose_undo_cb (Compose *compose);
422 static void compose_redo_cb (Compose *compose);
423 static void compose_cut_cb (Compose *compose);
424 static void compose_copy_cb (Compose *compose);
425 static void compose_paste_cb (Compose *compose);
426 static void compose_paste_as_quote_cb (Compose *compose);
427 static void compose_paste_no_wrap_cb (Compose *compose);
428 static void compose_paste_wrap_cb (Compose *compose);
429 static void compose_allsel_cb (Compose *compose);
431 static void compose_advanced_action_cb (Compose *compose,
432 ComposeCallAdvancedAction action);
434 static void compose_grab_focus_cb (GtkWidget *widget,
437 static void compose_changed_cb (GtkTextBuffer *textbuf,
440 static void compose_wrap_cb (gpointer data,
443 static void compose_find_cb (gpointer data,
446 static void compose_toggle_autowrap_cb (gpointer data,
450 static void compose_toggle_ruler_cb (gpointer data,
453 static void compose_toggle_sign_cb (gpointer data,
456 static void compose_toggle_encrypt_cb (gpointer data,
459 static void compose_set_privacy_system_cb(GtkWidget *widget,
461 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
462 static void activate_privacy_system (Compose *compose,
463 PrefsAccount *account,
465 static void compose_use_signing(Compose *compose, gboolean use_signing);
466 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
467 static void compose_toggle_return_receipt_cb(gpointer data, guint action,
469 static void compose_toggle_remove_refs_cb(gpointer data, guint action,
471 static void compose_set_priority_cb (gpointer data,
474 static void compose_reply_change_mode (gpointer data,
478 static void compose_attach_drag_received_cb (GtkWidget *widget,
479 GdkDragContext *drag_context,
482 GtkSelectionData *data,
486 static void compose_insert_drag_received_cb (GtkWidget *widget,
487 GdkDragContext *drag_context,
490 GtkSelectionData *data,
494 static void compose_header_drag_received_cb (GtkWidget *widget,
495 GdkDragContext *drag_context,
498 GtkSelectionData *data,
503 static gboolean compose_drag_drop (GtkWidget *widget,
504 GdkDragContext *drag_context,
506 guint time, gpointer user_data);
508 static void text_inserted (GtkTextBuffer *buffer,
513 static Compose *compose_generic_reply(MsgInfo *msginfo,
514 ComposeQuoteMode quote_mode,
518 gboolean followup_and_reply_to,
521 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
522 ComposeHeaderEntry *headerentry);
523 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
525 ComposeHeaderEntry *headerentry);
527 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
529 static void compose_allow_user_actions (Compose *compose, gboolean allow);
532 static void compose_check_all (Compose *compose);
533 static void compose_highlight_all (Compose *compose);
534 static void compose_check_backwards (Compose *compose);
535 static void compose_check_forwards_go (Compose *compose);
538 static gint compose_defer_auto_save_draft (Compose *compose);
539 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
541 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
544 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
545 FolderItem *folder_item);
548 static GtkItemFactoryEntry compose_popup_entries[] =
550 {N_("/_Add..."), NULL, compose_attach_cb, 0, NULL},
551 {N_("/_Remove"), NULL, compose_attach_remove_selected, 0, NULL},
552 {"/---", NULL, NULL, 0, "<Separator>"},
553 {N_("/_Properties..."), NULL, compose_attach_property, 0, NULL}
556 static GtkItemFactoryEntry compose_entries[] =
558 {N_("/_Message"), NULL, NULL, 0, "<Branch>"},
559 {N_("/_Message/S_end"), "<control>Return",
560 compose_send_cb, 0, NULL},
561 {N_("/_Message/Send _later"), "<shift><control>S",
562 compose_send_later_cb, 0, NULL},
563 {N_("/_Message/---"), NULL, NULL, 0, "<Separator>"},
564 {N_("/_Message/_Attach file"), "<control>M", compose_attach_cb, 0, NULL},
565 {N_("/_Message/_Insert file"), "<control>I", compose_insert_file_cb, 0, NULL},
566 {N_("/_Message/Insert si_gnature"), "<control>G", compose_insert_sig_cb, 0, NULL},
567 {N_("/_Message/---"), NULL, NULL, 0, "<Separator>"},
568 {N_("/_Message/_Save"),
569 "<control>S", compose_draft_cb, COMPOSE_KEEP_EDITING, NULL},
570 {N_("/_Message/---"), NULL, NULL, 0, "<Separator>"},
571 {N_("/_Message/_Close"), "<control>W", compose_close_cb, 0, NULL},
573 {N_("/_Edit"), NULL, NULL, 0, "<Branch>"},
574 {N_("/_Edit/_Undo"), "<control>Z", compose_undo_cb, 0, NULL},
575 {N_("/_Edit/_Redo"), "<control>Y", compose_redo_cb, 0, NULL},
576 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
577 {N_("/_Edit/Cu_t"), "<control>X", compose_cut_cb, 0, NULL},
578 {N_("/_Edit/_Copy"), "<control>C", compose_copy_cb, 0, NULL},
579 {N_("/_Edit/_Paste"), "<control>V", compose_paste_cb, 0, NULL},
580 {N_("/_Edit/Special paste"), NULL, NULL, 0, "<Branch>"},
581 {N_("/_Edit/Special paste/as _quotation"),
582 NULL, compose_paste_as_quote_cb, 0, NULL},
583 {N_("/_Edit/Special paste/_wrapped"),
584 NULL, compose_paste_wrap_cb, 0, NULL},
585 {N_("/_Edit/Special paste/_unwrapped"),
586 NULL, compose_paste_no_wrap_cb, 0, NULL},
587 {N_("/_Edit/Select _all"), "<control>A", compose_allsel_cb, 0, NULL},
588 {N_("/_Edit/A_dvanced"), NULL, NULL, 0, "<Branch>"},
589 {N_("/_Edit/A_dvanced/Move a character backward"),
591 compose_advanced_action_cb,
592 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
594 {N_("/_Edit/A_dvanced/Move a character forward"),
596 compose_advanced_action_cb,
597 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
599 {N_("/_Edit/A_dvanced/Move a word backward"),
601 compose_advanced_action_cb,
602 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
604 {N_("/_Edit/A_dvanced/Move a word forward"),
606 compose_advanced_action_cb,
607 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
609 {N_("/_Edit/A_dvanced/Move to beginning of line"),
610 NULL, /* "<control>A" */
611 compose_advanced_action_cb,
612 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
614 {N_("/_Edit/A_dvanced/Move to end of line"),
616 compose_advanced_action_cb,
617 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
619 {N_("/_Edit/A_dvanced/Move to previous line"),
621 compose_advanced_action_cb,
622 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
624 {N_("/_Edit/A_dvanced/Move to next line"),
626 compose_advanced_action_cb,
627 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
629 {N_("/_Edit/A_dvanced/Delete a character backward"),
631 compose_advanced_action_cb,
632 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
634 {N_("/_Edit/A_dvanced/Delete a character forward"),
636 compose_advanced_action_cb,
637 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
639 {N_("/_Edit/A_dvanced/Delete a word backward"),
640 NULL, /* "<control>W" */
641 compose_advanced_action_cb,
642 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
644 {N_("/_Edit/A_dvanced/Delete a word forward"),
645 NULL, /* "<alt>D", */
646 compose_advanced_action_cb,
647 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
649 {N_("/_Edit/A_dvanced/Delete line"),
651 compose_advanced_action_cb,
652 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
654 {N_("/_Edit/A_dvanced/Delete entire line"),
656 compose_advanced_action_cb,
657 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE_N,
659 {N_("/_Edit/A_dvanced/Delete to end of line"),
661 compose_advanced_action_cb,
662 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END,
664 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
666 "<control>F", compose_find_cb, 0, NULL},
667 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
668 {N_("/_Edit/_Wrap current paragraph"),
669 "<control>L", compose_wrap_cb, 0, NULL},
670 {N_("/_Edit/Wrap all long _lines"),
671 "<control><alt>L", compose_wrap_cb, 1, NULL},
672 {N_("/_Edit/Aut_o wrapping"), "<shift><control>L", compose_toggle_autowrap_cb, 0, "<ToggleItem>"},
673 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
674 {N_("/_Edit/Edit with e_xternal editor"),
675 "<shift><control>X", compose_ext_editor_cb, 0, NULL},
677 {N_("/_Spelling"), NULL, NULL, 0, "<Branch>"},
678 {N_("/_Spelling/_Check all or check selection"),
679 NULL, compose_check_all, 0, NULL},
680 {N_("/_Spelling/_Highlight all misspelled words"),
681 NULL, compose_highlight_all, 0, NULL},
682 {N_("/_Spelling/Check _backwards misspelled word"),
683 NULL, compose_check_backwards , 0, NULL},
684 {N_("/_Spelling/_Forward to next misspelled word"),
685 NULL, compose_check_forwards_go, 0, NULL},
686 {N_("/_Spelling/---"), NULL, NULL, 0, "<Separator>"},
687 {N_("/_Spelling/Options"),
688 NULL, NULL, 0, "<Branch>"},
690 {N_("/_Options"), NULL, NULL, 0, "<Branch>"},
691 {N_("/_Options/Reply _mode"), NULL, NULL, 0, "<Branch>"},
692 {N_("/_Options/Reply _mode/_Normal"), NULL, compose_reply_change_mode, COMPOSE_REPLY, "<RadioItem>"},
693 {N_("/_Options/Reply _mode/_All"), NULL, compose_reply_change_mode, COMPOSE_REPLY_TO_ALL, "/Options/Reply mode/Normal"},
694 {N_("/_Options/Reply _mode/_Sender"), NULL, compose_reply_change_mode, COMPOSE_REPLY_TO_SENDER, "/Options/Reply mode/Normal"},
695 {N_("/_Options/Reply _mode/_Mailing-list"), NULL, compose_reply_change_mode, COMPOSE_REPLY_TO_LIST, "/Options/Reply mode/Normal"},
696 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
697 {N_("/_Options/Privacy _System"), NULL, NULL, 0, "<Branch>"},
698 {N_("/_Options/Privacy _System/None"), NULL, NULL, 0, "<RadioItem>"},
699 {N_("/_Options/Si_gn"), NULL, compose_toggle_sign_cb , 0, "<ToggleItem>"},
700 {N_("/_Options/_Encrypt"), NULL, compose_toggle_encrypt_cb, 0, "<ToggleItem>"},
701 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
702 {N_("/_Options/_Priority"), NULL, NULL, 0, "<Branch>"},
703 {N_("/_Options/Priority/_Highest"), NULL, compose_set_priority_cb, PRIORITY_HIGHEST, "<RadioItem>"},
704 {N_("/_Options/Priority/Hi_gh"), NULL, compose_set_priority_cb, PRIORITY_HIGH, "/Options/Priority/Highest"},
705 {N_("/_Options/Priority/_Normal"), NULL, compose_set_priority_cb, PRIORITY_NORMAL, "/Options/Priority/Highest"},
706 {N_("/_Options/Priority/Lo_w"), NULL, compose_set_priority_cb, PRIORITY_LOW, "/Options/Priority/Highest"},
707 {N_("/_Options/Priority/_Lowest"), NULL, compose_set_priority_cb, PRIORITY_LOWEST, "/Options/Priority/Highest"},
708 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
709 {N_("/_Options/_Request Return Receipt"), NULL, compose_toggle_return_receipt_cb, 0, "<ToggleItem>"},
710 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
711 {N_("/_Options/Remo_ve references"), NULL, compose_toggle_remove_refs_cb, 0, "<ToggleItem>"},
712 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
714 #define ENC_ACTION(action) \
715 NULL, compose_set_encoding_cb, action, \
716 "/Options/Character encoding/Automatic"
718 {N_("/_Options/Character _encoding"), NULL, NULL, 0, "<Branch>"},
719 {N_("/_Options/Character _encoding/_Automatic"),
720 NULL, compose_set_encoding_cb, C_AUTO, "<RadioItem>"},
721 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
723 {N_("/_Options/Character _encoding/7bit ascii (US-ASC_II)"),
724 ENC_ACTION(C_US_ASCII)},
725 {N_("/_Options/Character _encoding/Unicode (_UTF-8)"),
726 ENC_ACTION(C_UTF_8)},
727 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
729 {N_("/_Options/Character _encoding/Western European (ISO-8859-_1)"),
730 ENC_ACTION(C_ISO_8859_1)},
731 {N_("/_Options/Character _encoding/Western European (ISO-8859-15)"),
732 ENC_ACTION(C_ISO_8859_15)},
733 {N_("/_Options/Character _encoding/Western European (Windows-1252)"),
734 ENC_ACTION(C_WINDOWS_1252)},
735 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
737 {N_("/_Options/Character _encoding/Central European (ISO-8859-_2)"),
738 ENC_ACTION(C_ISO_8859_2)},
739 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
741 {N_("/_Options/Character _encoding/_Baltic (ISO-8859-13)"),
742 ENC_ACTION(C_ISO_8859_13)},
743 {N_("/_Options/Character _encoding/Baltic (ISO-8859-_4)"),
744 ENC_ACTION(C_ISO_8859_4)},
745 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
747 {N_("/_Options/Character _encoding/Greek (ISO-8859-_7)"),
748 ENC_ACTION(C_ISO_8859_7)},
749 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
751 {N_("/_Options/Character _encoding/Hebrew (ISO-8859-_8)"),
752 ENC_ACTION(C_ISO_8859_8)},
753 {N_("/_Options/Character _encoding/Hebrew (Windows-1255)"),
754 ENC_ACTION(C_WINDOWS_1255)},
755 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
757 {N_("/_Options/Character _encoding/Arabic (ISO-8859-_6)"),
758 ENC_ACTION(C_ISO_8859_6)},
759 {N_("/_Options/Character _encoding/Arabic (Windows-1256)"),
760 ENC_ACTION(C_CP1256)},
761 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
763 {N_("/_Options/Character _encoding/Turkish (ISO-8859-_9)"),
764 ENC_ACTION(C_ISO_8859_9)},
765 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
767 {N_("/_Options/Character _encoding/Cyrillic (ISO-8859-_5)"),
768 ENC_ACTION(C_ISO_8859_5)},
769 {N_("/_Options/Character _encoding/Cyrillic (KOI8-_R)"),
770 ENC_ACTION(C_KOI8_R)},
771 {N_("/_Options/Character _encoding/Cyrillic (KOI8-U)"),
772 ENC_ACTION(C_KOI8_U)},
773 {N_("/_Options/Character _encoding/Cyrillic (Windows-1251)"),
774 ENC_ACTION(C_WINDOWS_1251)},
775 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
777 {N_("/_Options/Character _encoding/Japanese (ISO-2022-_JP)"),
778 ENC_ACTION(C_ISO_2022_JP)},
779 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
781 {N_("/_Options/Character _encoding/Simplified Chinese (_GB2312)"),
782 ENC_ACTION(C_GB2312)},
783 {N_("/_Options/Character _encoding/Simplified Chinese (GBK)"),
785 {N_("/_Options/Character _encoding/Traditional Chinese (_Big5)"),
787 {N_("/_Options/Character _encoding/Traditional Chinese (EUC-_TW)"),
788 ENC_ACTION(C_EUC_TW)},
789 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
791 {N_("/_Options/Character _encoding/Korean (EUC-_KR)"),
792 ENC_ACTION(C_EUC_KR)},
793 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
795 {N_("/_Options/Character _encoding/Thai (TIS-620)"),
796 ENC_ACTION(C_TIS_620)},
797 {N_("/_Options/Character _encoding/Thai (Windows-874)"),
798 ENC_ACTION(C_WINDOWS_874)},
800 {N_("/_Tools"), NULL, NULL, 0, "<Branch>"},
801 {N_("/_Tools/Show _ruler"), NULL, compose_toggle_ruler_cb, 0, "<ToggleItem>"},
802 {N_("/_Tools/_Address book"), "<shift><control>A", compose_address_cb , 0, NULL},
803 {N_("/_Tools/_Template"), NULL, NULL, 0, "<Branch>"},
804 {N_("/_Tools/Actio_ns"), NULL, NULL, 0, "<Branch>"},
805 {N_("/_Help"), NULL, NULL, 0, "<Branch>"},
806 {N_("/_Help/_About"), NULL, about_show, 0, NULL}
809 static GtkTargetEntry compose_mime_types[] =
811 {"text/uri-list", 0, 0},
812 {"UTF8_STRING", 0, 0},
816 static gboolean compose_put_existing_to_front(MsgInfo *info)
818 GList *compose_list = compose_get_compose_list();
822 for (elem = compose_list; elem != NULL && elem->data != NULL;
824 Compose *c = (Compose*)elem->data;
826 if (!c->targetinfo || !c->targetinfo->msgid ||
830 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
831 gtkut_window_popup(c->window);
839 static GdkColor quote_color1 =
840 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
841 static GdkColor quote_color2 =
842 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
843 static GdkColor quote_color3 =
844 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
846 static GdkColor quote_bgcolor1 =
847 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
848 static GdkColor quote_bgcolor2 =
849 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
850 static GdkColor quote_bgcolor3 =
851 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
853 static GdkColor signature_color = {
860 static GdkColor uri_color = {
867 static void compose_create_tags(GtkTextView *text, Compose *compose)
869 GtkTextBuffer *buffer;
870 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
876 buffer = gtk_text_view_get_buffer(text);
878 if (prefs_common.enable_color) {
879 /* grab the quote colors, converting from an int to a GdkColor */
880 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
882 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
884 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
886 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
888 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
890 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
892 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
894 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
897 signature_color = quote_color1 = quote_color2 = quote_color3 =
898 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
901 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
902 gtk_text_buffer_create_tag(buffer, "quote0",
903 "foreground-gdk", "e_color1,
904 "paragraph-background-gdk", "e_bgcolor1,
906 gtk_text_buffer_create_tag(buffer, "quote1",
907 "foreground-gdk", "e_color2,
908 "paragraph-background-gdk", "e_bgcolor2,
910 gtk_text_buffer_create_tag(buffer, "quote2",
911 "foreground-gdk", "e_color3,
912 "paragraph-background-gdk", "e_bgcolor3,
915 gtk_text_buffer_create_tag(buffer, "quote0",
916 "foreground-gdk", "e_color1,
918 gtk_text_buffer_create_tag(buffer, "quote1",
919 "foreground-gdk", "e_color2,
921 gtk_text_buffer_create_tag(buffer, "quote2",
922 "foreground-gdk", "e_color3,
926 gtk_text_buffer_create_tag(buffer, "signature",
927 "foreground-gdk", &signature_color,
929 gtk_text_buffer_create_tag(buffer, "link",
930 "foreground-gdk", &uri_color,
932 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
933 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
935 color[0] = quote_color1;
936 color[1] = quote_color2;
937 color[2] = quote_color3;
938 color[3] = quote_bgcolor1;
939 color[4] = quote_bgcolor2;
940 color[5] = quote_bgcolor3;
941 color[6] = signature_color;
942 color[7] = uri_color;
943 cmap = gdk_drawable_get_colormap(compose->window->window);
944 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
946 for (i = 0; i < 8; i++) {
947 if (success[i] == FALSE) {
950 g_warning("Compose: color allocation failed.\n");
951 style = gtk_widget_get_style(GTK_WIDGET(text));
952 quote_color1 = quote_color2 = quote_color3 =
953 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
954 signature_color = uri_color = black;
959 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
960 GPtrArray *attach_files)
962 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
965 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
967 return compose_generic_new(account, mailto, item, NULL, NULL);
970 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
972 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
975 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
976 GPtrArray *attach_files, GList *listAddress )
979 GtkTextView *textview;
980 GtkTextBuffer *textbuf;
982 GtkItemFactory *ifactory;
983 gchar *subject_format = NULL;
984 gchar *body_format = NULL;
986 if (item && item->prefs && item->prefs->enable_default_account)
987 account = account_find_from_id(item->prefs->default_account);
989 if (!account) account = cur_account;
990 g_return_val_if_fail(account != NULL, NULL);
992 compose = compose_create(account, COMPOSE_NEW, FALSE);
994 ifactory = gtk_item_factory_from_widget(compose->menubar);
996 compose->replyinfo = NULL;
997 compose->fwdinfo = NULL;
999 textview = GTK_TEXT_VIEW(compose->text);
1000 textbuf = gtk_text_view_get_buffer(textview);
1001 compose_create_tags(textview, compose);
1003 undo_block(compose->undostruct);
1005 compose_set_dictionaries_from_folder_prefs(compose, item);
1008 if (account->auto_sig)
1009 compose_insert_sig(compose, FALSE);
1010 gtk_text_buffer_get_start_iter(textbuf, &iter);
1011 gtk_text_buffer_place_cursor(textbuf, &iter);
1013 if (account->protocol != A_NNTP) {
1014 if (mailto && *mailto != '\0') {
1015 compose_entries_set(compose, mailto);
1017 } else if (item && item->prefs->enable_default_to) {
1018 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
1019 compose_entry_mark_default_to(compose, item->prefs->default_to);
1021 if (item && item->ret_rcpt) {
1022 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
1026 compose_entry_append(compose, mailto, COMPOSE_NEWSGROUPS);
1027 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1028 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1031 * CLAWS: just don't allow return receipt request, even if the user
1032 * may want to send an email. simple but foolproof.
1034 menu_set_sensitive(ifactory, "/Options/Request Return Receipt", FALSE);
1036 compose_add_field_list( compose, listAddress );
1038 if (item && item->prefs && item->prefs->compose_with_format) {
1039 subject_format = item->prefs->compose_subject_format;
1040 body_format = item->prefs->compose_body_format;
1041 } else if (account->compose_with_format) {
1042 subject_format = account->compose_subject_format;
1043 body_format = account->compose_body_format;
1044 } else if (prefs_common.compose_with_format) {
1045 subject_format = prefs_common.compose_subject_format;
1046 body_format = prefs_common.compose_body_format;
1049 if (subject_format || body_format) {
1050 MsgInfo* dummyinfo = NULL;
1053 && *subject_format != '\0' )
1055 gchar *subject = NULL;
1059 dummyinfo = compose_msginfo_new_from_compose(compose);
1061 /* decode \-escape sequences in the internal representation of the quote format */
1062 tmp = malloc(strlen(subject_format)+1);
1063 pref_get_unescaped_pref(tmp, subject_format);
1065 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1067 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account,
1068 compose->gtkaspell);
1070 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account);
1072 quote_fmt_scan_string(tmp);
1075 buf = quote_fmt_get_buffer();
1077 alertpanel_error(_("New message subject format error."));
1079 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1080 quote_fmt_reset_vartable();
1087 && *body_format != '\0' )
1090 GtkTextBuffer *buffer;
1091 GtkTextIter start, end;
1094 if ( dummyinfo == NULL )
1095 dummyinfo = compose_msginfo_new_from_compose(compose);
1097 text = GTK_TEXT_VIEW(compose->text);
1098 buffer = gtk_text_view_get_buffer(text);
1099 gtk_text_buffer_get_start_iter(buffer, &start);
1100 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1101 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1103 compose_quote_fmt(compose, dummyinfo,
1105 NULL, tmp, FALSE, TRUE,
1106 _("New message body format error at line %d."));
1107 quote_fmt_reset_vartable();
1112 procmsg_msginfo_free( dummyinfo );
1119 for (i = 0; i < attach_files->len; i++) {
1120 file = g_ptr_array_index(attach_files, i);
1121 compose_attach_append(compose, file, file, NULL);
1125 compose_show_first_last_header(compose, TRUE);
1127 /* Set save folder */
1128 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1129 gchar *folderidentifier;
1131 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1132 folderidentifier = folder_item_get_identifier(item);
1133 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1134 g_free(folderidentifier);
1137 gtk_widget_grab_focus(compose->header_last->entry);
1139 undo_unblock(compose->undostruct);
1141 if (prefs_common.auto_exteditor)
1142 compose_exec_ext_editor(compose);
1144 compose->modified = FALSE;
1145 compose_set_title(compose);
1149 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1150 gboolean override_pref)
1152 gchar *privacy = NULL;
1154 g_return_if_fail(compose != NULL);
1155 g_return_if_fail(account != NULL);
1157 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1160 if (account->default_privacy_system
1161 && strlen(account->default_privacy_system)) {
1162 privacy = account->default_privacy_system;
1164 GSList *privacy_avail = privacy_get_system_ids();
1165 if (privacy_avail && g_slist_length(privacy_avail)) {
1166 privacy = (gchar *)(privacy_avail->data);
1169 if (privacy != NULL) {
1170 if (compose->privacy_system == NULL)
1171 compose->privacy_system = g_strdup(privacy);
1172 compose_update_privacy_system_menu_item(compose, FALSE);
1173 compose_use_encryption(compose, TRUE);
1177 static void compose_force_signing(Compose *compose, PrefsAccount *account)
1179 gchar *privacy = NULL;
1181 if (account->default_privacy_system
1182 && strlen(account->default_privacy_system)) {
1183 privacy = account->default_privacy_system;
1185 GSList *privacy_avail = privacy_get_system_ids();
1186 if (privacy_avail && g_slist_length(privacy_avail)) {
1187 privacy = (gchar *)(privacy_avail->data);
1190 if (privacy != NULL) {
1191 if (compose->privacy_system == NULL)
1192 compose->privacy_system = g_strdup(privacy);
1193 compose_update_privacy_system_menu_item(compose, FALSE);
1194 compose_use_signing(compose, TRUE);
1198 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1202 Compose *compose = NULL;
1203 GtkItemFactory *ifactory = NULL;
1205 g_return_val_if_fail(msginfo_list != NULL, NULL);
1207 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1208 g_return_val_if_fail(msginfo != NULL, NULL);
1210 list_len = g_slist_length(msginfo_list);
1214 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1215 FALSE, prefs_common.default_reply_list, FALSE, body);
1217 case COMPOSE_REPLY_WITH_QUOTE:
1218 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1219 FALSE, prefs_common.default_reply_list, FALSE, body);
1221 case COMPOSE_REPLY_WITHOUT_QUOTE:
1222 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1223 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1225 case COMPOSE_REPLY_TO_SENDER:
1226 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1227 FALSE, FALSE, TRUE, body);
1229 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1230 compose = compose_followup_and_reply_to(msginfo,
1231 COMPOSE_QUOTE_CHECK,
1232 FALSE, FALSE, body);
1234 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1235 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1236 FALSE, FALSE, TRUE, body);
1238 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1239 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1240 FALSE, FALSE, TRUE, NULL);
1242 case COMPOSE_REPLY_TO_ALL:
1243 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1244 TRUE, FALSE, FALSE, body);
1246 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1247 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1248 TRUE, FALSE, FALSE, body);
1250 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1251 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1252 TRUE, FALSE, FALSE, NULL);
1254 case COMPOSE_REPLY_TO_LIST:
1255 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1256 FALSE, TRUE, FALSE, body);
1258 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1259 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1260 FALSE, TRUE, FALSE, body);
1262 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1263 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1264 FALSE, TRUE, FALSE, NULL);
1266 case COMPOSE_FORWARD:
1267 if (prefs_common.forward_as_attachment) {
1268 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1271 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1275 case COMPOSE_FORWARD_INLINE:
1276 /* check if we reply to more than one Message */
1277 if (list_len == 1) {
1278 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1281 /* more messages FALL THROUGH */
1282 case COMPOSE_FORWARD_AS_ATTACH:
1283 compose = compose_forward_multiple(NULL, msginfo_list);
1285 case COMPOSE_REDIRECT:
1286 compose = compose_redirect(NULL, msginfo, FALSE);
1289 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1292 ifactory = gtk_item_factory_from_widget(compose->menubar);
1294 compose->rmode = mode;
1295 switch (compose->rmode) {
1297 case COMPOSE_REPLY_WITH_QUOTE:
1298 case COMPOSE_REPLY_WITHOUT_QUOTE:
1299 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1300 debug_print("reply mode Normal\n");
1301 menu_set_active(ifactory, "/Options/Reply mode/Normal", TRUE);
1302 compose_reply_change_mode(compose, COMPOSE_REPLY, NULL); /* force update */
1304 case COMPOSE_REPLY_TO_SENDER:
1305 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1306 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1307 debug_print("reply mode Sender\n");
1308 menu_set_active(ifactory, "/Options/Reply mode/Sender", TRUE);
1310 case COMPOSE_REPLY_TO_ALL:
1311 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1312 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1313 debug_print("reply mode All\n");
1314 menu_set_active(ifactory, "/Options/Reply mode/All", TRUE);
1316 case COMPOSE_REPLY_TO_LIST:
1317 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1318 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1319 debug_print("reply mode List\n");
1320 menu_set_active(ifactory, "/Options/Reply mode/Mailing-list", TRUE);
1328 static Compose *compose_reply(MsgInfo *msginfo,
1329 ComposeQuoteMode quote_mode,
1335 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1336 to_sender, FALSE, body);
1339 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1340 ComposeQuoteMode quote_mode,
1345 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1346 to_sender, TRUE, body);
1349 static void compose_extract_original_charset(Compose *compose)
1351 MsgInfo *info = NULL;
1352 if (compose->replyinfo) {
1353 info = compose->replyinfo;
1354 } else if (compose->fwdinfo) {
1355 info = compose->fwdinfo;
1356 } else if (compose->targetinfo) {
1357 info = compose->targetinfo;
1360 MimeInfo *mimeinfo = procmime_scan_message(info);
1361 MimeInfo *partinfo = mimeinfo;
1362 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1363 partinfo = procmime_mimeinfo_next(partinfo);
1365 compose->orig_charset =
1366 g_strdup(procmime_mimeinfo_get_parameter(
1367 partinfo, "charset"));
1369 procmime_mimeinfo_free_all(mimeinfo);
1373 #define SIGNAL_BLOCK(buffer) { \
1374 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1375 G_CALLBACK(compose_changed_cb), \
1377 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1378 G_CALLBACK(text_inserted), \
1382 #define SIGNAL_UNBLOCK(buffer) { \
1383 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1384 G_CALLBACK(compose_changed_cb), \
1386 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1387 G_CALLBACK(text_inserted), \
1391 static Compose *compose_generic_reply(MsgInfo *msginfo,
1392 ComposeQuoteMode quote_mode,
1393 gboolean to_all, gboolean to_ml,
1395 gboolean followup_and_reply_to,
1398 GtkItemFactory *ifactory;
1400 PrefsAccount *account = NULL;
1401 GtkTextView *textview;
1402 GtkTextBuffer *textbuf;
1403 gboolean quote = FALSE;
1404 gchar *qmark = NULL;
1405 gchar *body_fmt = NULL;
1407 g_return_val_if_fail(msginfo != NULL, NULL);
1408 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1410 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1412 g_return_val_if_fail(account != NULL, NULL);
1414 compose = compose_create(account, COMPOSE_REPLY, FALSE);
1416 compose->updating = TRUE;
1418 ifactory = gtk_item_factory_from_widget(compose->menubar);
1420 menu_set_active(ifactory, "/Options/Remove references", FALSE);
1421 menu_set_sensitive(ifactory, "/Options/Remove references", TRUE);
1423 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1424 if (!compose->replyinfo)
1425 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1427 compose_extract_original_charset(compose);
1429 if (msginfo->folder && msginfo->folder->ret_rcpt)
1430 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
1432 /* Set save folder */
1433 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1434 gchar *folderidentifier;
1436 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1437 folderidentifier = folder_item_get_identifier(msginfo->folder);
1438 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1439 g_free(folderidentifier);
1442 if (compose_parse_header(compose, msginfo) < 0) return NULL;
1444 textview = (GTK_TEXT_VIEW(compose->text));
1445 textbuf = gtk_text_view_get_buffer(textview);
1446 compose_create_tags(textview, compose);
1448 undo_block(compose->undostruct);
1450 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1453 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1454 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1455 /* use the reply format of folder (if enabled), or the account's one
1456 (if enabled) or fallback to the global reply format, which is always
1457 enabled (even if empty), and use the relevant quotemark */
1459 if (msginfo->folder && msginfo->folder->prefs &&
1460 msginfo->folder->prefs->reply_with_format) {
1461 qmark = msginfo->folder->prefs->reply_quotemark;
1462 body_fmt = msginfo->folder->prefs->reply_body_format;
1464 } else if (account->reply_with_format) {
1465 qmark = account->reply_quotemark;
1466 body_fmt = account->reply_body_format;
1469 qmark = prefs_common.quotemark;
1470 body_fmt = prefs_common.quotefmt;
1475 /* empty quotemark is not allowed */
1476 if (qmark == NULL || *qmark == '\0')
1478 compose_quote_fmt(compose, compose->replyinfo,
1479 body_fmt, qmark, body, FALSE, TRUE,
1480 _("Message reply format error at line %d."));
1481 quote_fmt_reset_vartable();
1483 if (procmime_msginfo_is_encrypted(compose->replyinfo)) {
1484 compose_force_encryption(compose, account, FALSE);
1487 SIGNAL_BLOCK(textbuf);
1489 if (account->auto_sig)
1490 compose_insert_sig(compose, FALSE);
1492 compose_wrap_all(compose);
1494 SIGNAL_UNBLOCK(textbuf);
1496 gtk_widget_grab_focus(compose->text);
1498 undo_unblock(compose->undostruct);
1500 if (prefs_common.auto_exteditor)
1501 compose_exec_ext_editor(compose);
1503 compose->modified = FALSE;
1504 compose_set_title(compose);
1506 compose->updating = FALSE;
1508 if (compose->deferred_destroy) {
1509 compose_destroy(compose);
1516 #define INSERT_FW_HEADER(var, hdr) \
1517 if (msginfo->var && *msginfo->var) { \
1518 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1519 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1520 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1523 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1524 gboolean as_attach, const gchar *body,
1525 gboolean no_extedit,
1529 GtkTextView *textview;
1530 GtkTextBuffer *textbuf;
1533 g_return_val_if_fail(msginfo != NULL, NULL);
1534 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1537 !(account = compose_guess_forward_account_from_msginfo
1539 account = cur_account;
1541 compose = compose_create(account, COMPOSE_FORWARD, batch);
1543 compose->updating = TRUE;
1544 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1545 if (!compose->fwdinfo)
1546 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1548 compose_extract_original_charset(compose);
1550 if (msginfo->subject && *msginfo->subject) {
1551 gchar *buf, *buf2, *p;
1553 buf = p = g_strdup(msginfo->subject);
1554 p += subject_get_prefix_length(p);
1555 memmove(buf, p, strlen(p) + 1);
1557 buf2 = g_strdup_printf("Fw: %s", buf);
1558 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1564 textview = GTK_TEXT_VIEW(compose->text);
1565 textbuf = gtk_text_view_get_buffer(textview);
1566 compose_create_tags(textview, compose);
1568 undo_block(compose->undostruct);
1572 msgfile = procmsg_get_message_file(msginfo);
1573 if (!is_file_exist(msgfile))
1574 g_warning("%s: file not exist\n", msgfile);
1576 compose_attach_append(compose, msgfile, msgfile,
1581 gchar *qmark = NULL;
1582 gchar *body_fmt = prefs_common.fw_quotefmt;
1583 MsgInfo *full_msginfo;
1585 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1587 full_msginfo = procmsg_msginfo_copy(msginfo);
1589 /* use the forward format of folder (if enabled), or the account's one
1590 (if enabled) or fallback to the global forward format, which is always
1591 enabled (even if empty), and use the relevant quotemark */
1592 if (msginfo->folder && msginfo->folder->prefs &&
1593 msginfo->folder->prefs->forward_with_format) {
1594 qmark = msginfo->folder->prefs->forward_quotemark;
1595 body_fmt = msginfo->folder->prefs->forward_body_format;
1597 } else if (account->forward_with_format) {
1598 qmark = account->forward_quotemark;
1599 body_fmt = account->forward_body_format;
1602 qmark = prefs_common.fw_quotemark;
1603 body_fmt = prefs_common.fw_quotefmt;
1606 /* empty quotemark is not allowed */
1607 if (qmark == NULL || *qmark == '\0')
1610 compose_quote_fmt(compose, full_msginfo,
1611 body_fmt, qmark, body, FALSE, TRUE,
1612 _("Message forward format error at line %d."));
1613 quote_fmt_reset_vartable();
1614 compose_attach_parts(compose, msginfo);
1616 procmsg_msginfo_free(full_msginfo);
1619 SIGNAL_BLOCK(textbuf);
1621 if (account->auto_sig)
1622 compose_insert_sig(compose, FALSE);
1624 compose_wrap_all(compose);
1626 SIGNAL_UNBLOCK(textbuf);
1628 gtk_text_buffer_get_start_iter(textbuf, &iter);
1629 gtk_text_buffer_place_cursor(textbuf, &iter);
1631 gtk_widget_grab_focus(compose->header_last->entry);
1633 if (!no_extedit && prefs_common.auto_exteditor)
1634 compose_exec_ext_editor(compose);
1637 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1638 gchar *folderidentifier;
1640 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1641 folderidentifier = folder_item_get_identifier(msginfo->folder);
1642 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1643 g_free(folderidentifier);
1646 undo_unblock(compose->undostruct);
1648 compose->modified = FALSE;
1649 compose_set_title(compose);
1651 compose->updating = FALSE;
1653 if (compose->deferred_destroy) {
1654 compose_destroy(compose);
1661 #undef INSERT_FW_HEADER
1663 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1666 GtkTextView *textview;
1667 GtkTextBuffer *textbuf;
1671 gboolean single_mail = TRUE;
1673 g_return_val_if_fail(msginfo_list != NULL, NULL);
1675 if (g_slist_length(msginfo_list) > 1)
1676 single_mail = FALSE;
1678 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1679 if (((MsgInfo *)msginfo->data)->folder == NULL)
1682 /* guess account from first selected message */
1684 !(account = compose_guess_forward_account_from_msginfo
1685 (msginfo_list->data)))
1686 account = cur_account;
1688 g_return_val_if_fail(account != NULL, NULL);
1690 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1691 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1692 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1695 compose = compose_create(account, COMPOSE_FORWARD, FALSE);
1697 compose->updating = TRUE;
1699 textview = GTK_TEXT_VIEW(compose->text);
1700 textbuf = gtk_text_view_get_buffer(textview);
1701 compose_create_tags(textview, compose);
1703 undo_block(compose->undostruct);
1704 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1705 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1707 if (!is_file_exist(msgfile))
1708 g_warning("%s: file not exist\n", msgfile);
1710 compose_attach_append(compose, msgfile, msgfile,
1716 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1717 if (info->subject && *info->subject) {
1718 gchar *buf, *buf2, *p;
1720 buf = p = g_strdup(info->subject);
1721 p += subject_get_prefix_length(p);
1722 memmove(buf, p, strlen(p) + 1);
1724 buf2 = g_strdup_printf("Fw: %s", buf);
1725 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1731 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1732 _("Fw: multiple emails"));
1735 SIGNAL_BLOCK(textbuf);
1737 if (account->auto_sig)
1738 compose_insert_sig(compose, FALSE);
1740 compose_wrap_all(compose);
1742 SIGNAL_UNBLOCK(textbuf);
1744 gtk_text_buffer_get_start_iter(textbuf, &iter);
1745 gtk_text_buffer_place_cursor(textbuf, &iter);
1747 gtk_widget_grab_focus(compose->header_last->entry);
1748 undo_unblock(compose->undostruct);
1749 compose->modified = FALSE;
1750 compose_set_title(compose);
1752 compose->updating = FALSE;
1754 if (compose->deferred_destroy) {
1755 compose_destroy(compose);
1762 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1764 GtkTextIter start = *iter;
1765 GtkTextIter end_iter;
1766 int start_pos = gtk_text_iter_get_offset(&start);
1768 if (!compose->account->sig_sep)
1771 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1772 start_pos+strlen(compose->account->sig_sep));
1774 /* check sig separator */
1775 str = gtk_text_iter_get_text(&start, &end_iter);
1776 if (!strcmp(str, compose->account->sig_sep)) {
1778 /* check end of line (\n) */
1779 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1780 start_pos+strlen(compose->account->sig_sep));
1781 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1782 start_pos+strlen(compose->account->sig_sep)+1);
1783 tmp = gtk_text_iter_get_text(&start, &end_iter);
1784 if (!strcmp(tmp,"\n")) {
1796 static void compose_colorize_signature(Compose *compose)
1798 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1800 GtkTextIter end_iter;
1801 gtk_text_buffer_get_start_iter(buffer, &iter);
1802 while (gtk_text_iter_forward_line(&iter))
1803 if (compose_is_sig_separator(compose, buffer, &iter)) {
1804 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1805 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1809 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
1811 Compose *compose = NULL;
1812 PrefsAccount *account = NULL;
1813 GtkTextView *textview;
1814 GtkTextBuffer *textbuf;
1818 gchar buf[BUFFSIZE];
1819 gboolean use_signing = FALSE;
1820 gboolean use_encryption = FALSE;
1821 gchar *privacy_system = NULL;
1822 int priority = PRIORITY_NORMAL;
1823 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
1825 g_return_val_if_fail(msginfo != NULL, NULL);
1826 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1828 if (compose_put_existing_to_front(msginfo)) {
1832 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
1833 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
1834 gchar queueheader_buf[BUFFSIZE];
1837 /* Select Account from queue headers */
1838 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1839 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
1840 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
1841 account = account_find_from_id(id);
1843 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1844 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
1845 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
1846 account = account_find_from_id(id);
1848 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1849 sizeof(queueheader_buf), "NAID:")) {
1850 id = atoi(&queueheader_buf[strlen("NAID:")]);
1851 account = account_find_from_id(id);
1853 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1854 sizeof(queueheader_buf), "MAID:")) {
1855 id = atoi(&queueheader_buf[strlen("MAID:")]);
1856 account = account_find_from_id(id);
1858 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1859 sizeof(queueheader_buf), "S:")) {
1860 account = account_find_from_address(queueheader_buf);
1862 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1863 sizeof(queueheader_buf), "X-Claws-Sign:")) {
1864 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
1865 use_signing = param;
1868 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1869 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
1870 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
1871 use_signing = param;
1874 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1875 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
1876 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
1877 use_encryption = param;
1879 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1880 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
1881 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
1882 use_encryption = param;
1884 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1885 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
1886 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
1888 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1889 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
1890 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
1892 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1893 sizeof(queueheader_buf), "X-Priority: ")) {
1894 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
1897 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1898 sizeof(queueheader_buf), "RMID:")) {
1899 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
1900 if (tokens[0] && tokens[1] && tokens[2]) {
1901 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
1902 if (orig_item != NULL) {
1903 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
1908 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1909 sizeof(queueheader_buf), "FMID:")) {
1910 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
1911 if (tokens[0] && tokens[1] && tokens[2]) {
1912 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
1913 if (orig_item != NULL) {
1914 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
1920 account = msginfo->folder->folder->account;
1923 if (!account && prefs_common.reedit_account_autosel) {
1924 gchar from[BUFFSIZE];
1925 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
1926 extract_address(from);
1927 account = account_find_from_address(from);
1931 account = cur_account;
1933 g_return_val_if_fail(account != NULL, NULL);
1935 compose = compose_create(account, COMPOSE_REEDIT, batch);
1937 compose->replyinfo = replyinfo;
1938 compose->fwdinfo = fwdinfo;
1940 compose->updating = TRUE;
1941 compose->priority = priority;
1943 if (privacy_system != NULL) {
1944 compose->privacy_system = privacy_system;
1945 compose_use_signing(compose, use_signing);
1946 compose_use_encryption(compose, use_encryption);
1947 compose_update_privacy_system_menu_item(compose, FALSE);
1949 activate_privacy_system(compose, account, FALSE);
1952 compose->targetinfo = procmsg_msginfo_copy(msginfo);
1954 compose_extract_original_charset(compose);
1956 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
1957 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
1958 gchar queueheader_buf[BUFFSIZE];
1960 /* Set message save folder */
1961 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
1964 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1965 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
1966 gtk_editable_insert_text(GTK_EDITABLE(compose->savemsg_entry), &queueheader_buf[4], strlen(&queueheader_buf[4]), &startpos);
1968 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
1969 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
1971 GtkItemFactory *ifactory;
1972 ifactory = gtk_item_factory_from_widget(compose->menubar);
1973 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
1978 if (compose_parse_header(compose, msginfo) < 0) {
1979 compose->updating = FALSE;
1980 compose_destroy(compose);
1983 compose_reedit_set_entry(compose, msginfo);
1985 textview = GTK_TEXT_VIEW(compose->text);
1986 textbuf = gtk_text_view_get_buffer(textview);
1987 compose_create_tags(textview, compose);
1989 mark = gtk_text_buffer_get_insert(textbuf);
1990 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1992 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
1993 G_CALLBACK(compose_changed_cb),
1996 if (procmime_msginfo_is_encrypted(msginfo)) {
1997 fp = procmime_get_first_encrypted_text_content(msginfo);
1999 compose_force_encryption(compose, account, TRUE);
2002 fp = procmime_get_first_text_content(msginfo);
2005 g_warning("Can't get text part\n");
2009 gboolean prev_autowrap = compose->autowrap;
2011 compose->autowrap = FALSE;
2012 while (fgets(buf, sizeof(buf), fp) != NULL) {
2014 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2016 compose_wrap_all_full(compose, FALSE);
2017 compose->autowrap = prev_autowrap;
2021 compose_attach_parts(compose, msginfo);
2023 compose_colorize_signature(compose);
2025 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2026 G_CALLBACK(compose_changed_cb),
2029 gtk_widget_grab_focus(compose->text);
2031 if (prefs_common.auto_exteditor) {
2032 compose_exec_ext_editor(compose);
2034 compose->modified = FALSE;
2035 compose_set_title(compose);
2037 compose->updating = FALSE;
2039 if (compose->deferred_destroy) {
2040 compose_destroy(compose);
2044 compose->sig_str = compose_get_signature_str(compose);
2049 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2054 GtkItemFactory *ifactory;
2057 g_return_val_if_fail(msginfo != NULL, NULL);
2060 account = account_get_reply_account(msginfo,
2061 prefs_common.reply_account_autosel);
2062 g_return_val_if_fail(account != NULL, NULL);
2064 compose = compose_create(account, COMPOSE_REDIRECT, batch);
2066 compose->updating = TRUE;
2068 ifactory = gtk_item_factory_from_widget(compose->menubar);
2069 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2070 compose->replyinfo = NULL;
2071 compose->fwdinfo = NULL;
2073 compose_show_first_last_header(compose, TRUE);
2075 gtk_widget_grab_focus(compose->header_last->entry);
2077 filename = procmsg_get_message_file(msginfo);
2079 if (filename == NULL) {
2080 compose->updating = FALSE;
2081 compose_destroy(compose);
2086 compose->redirect_filename = filename;
2088 /* Set save folder */
2089 item = msginfo->folder;
2090 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2091 gchar *folderidentifier;
2093 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2094 folderidentifier = folder_item_get_identifier(item);
2095 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
2096 g_free(folderidentifier);
2099 compose_attach_parts(compose, msginfo);
2101 if (msginfo->subject)
2102 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2104 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2106 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2107 _("Message redirect format error at line %d."));
2108 quote_fmt_reset_vartable();
2109 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2111 compose_colorize_signature(compose);
2113 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
2114 menu_set_sensitive(ifactory, "/Add...", FALSE);
2115 menu_set_sensitive(ifactory, "/Remove", FALSE);
2116 menu_set_sensitive(ifactory, "/Properties...", FALSE);
2118 ifactory = gtk_item_factory_from_widget(compose->menubar);
2119 menu_set_sensitive(ifactory, "/Message/Save", FALSE);
2120 menu_set_sensitive(ifactory, "/Message/Insert file", FALSE);
2121 menu_set_sensitive(ifactory, "/Message/Attach file", FALSE);
2122 menu_set_sensitive(ifactory, "/Message/Insert signature", FALSE);
2123 menu_set_sensitive(ifactory, "/Edit", FALSE);
2124 menu_set_sensitive(ifactory, "/Options", FALSE);
2125 menu_set_sensitive(ifactory, "/Tools/Show ruler", FALSE);
2126 menu_set_sensitive(ifactory, "/Tools/Actions", FALSE);
2128 if (compose->toolbar->draft_btn)
2129 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2130 if (compose->toolbar->insert_btn)
2131 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2132 if (compose->toolbar->attach_btn)
2133 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2134 if (compose->toolbar->sig_btn)
2135 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2136 if (compose->toolbar->exteditor_btn)
2137 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2138 if (compose->toolbar->linewrap_current_btn)
2139 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2140 if (compose->toolbar->linewrap_all_btn)
2141 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2143 compose->modified = FALSE;
2144 compose_set_title(compose);
2145 compose->updating = FALSE;
2147 if (compose->deferred_destroy) {
2148 compose_destroy(compose);
2155 GList *compose_get_compose_list(void)
2157 return compose_list;
2160 void compose_entry_append(Compose *compose, const gchar *address,
2161 ComposeEntryType type)
2163 const gchar *header;
2165 gboolean in_quote = FALSE;
2166 if (!address || *address == '\0') return;
2173 header = N_("Bcc:");
2175 case COMPOSE_REPLYTO:
2176 header = N_("Reply-To:");
2178 case COMPOSE_NEWSGROUPS:
2179 header = N_("Newsgroups:");
2181 case COMPOSE_FOLLOWUPTO:
2182 header = N_( "Followup-To:");
2189 header = prefs_common_translated_header_name(header);
2191 cur = begin = (gchar *)address;
2193 /* we separate the line by commas, but not if we're inside a quoted
2195 while (*cur != '\0') {
2197 in_quote = !in_quote;
2198 if (*cur == ',' && !in_quote) {
2199 gchar *tmp = g_strdup(begin);
2201 tmp[cur-begin]='\0';
2204 while (*tmp == ' ' || *tmp == '\t')
2206 compose_add_header_entry(compose, header, tmp);
2213 gchar *tmp = g_strdup(begin);
2215 tmp[cur-begin]='\0';
2218 while (*tmp == ' ' || *tmp == '\t')
2220 compose_add_header_entry(compose, header, tmp);
2225 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2227 static GdkColor yellow;
2228 static GdkColor black;
2229 static gboolean yellow_initialised = FALSE;
2233 if (!yellow_initialised) {
2234 gdk_color_parse("#f5f6be", &yellow);
2235 gdk_color_parse("#000000", &black);
2236 yellow_initialised = gdk_colormap_alloc_color(
2237 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2238 yellow_initialised &= gdk_colormap_alloc_color(
2239 gdk_colormap_get_system(), &black, FALSE, TRUE);
2242 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2243 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2244 if (gtk_entry_get_text(entry) &&
2245 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2246 if (yellow_initialised) {
2247 gtk_widget_modify_base(
2248 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2249 GTK_STATE_NORMAL, &yellow);
2250 gtk_widget_modify_text(
2251 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2252 GTK_STATE_NORMAL, &black);
2258 void compose_toolbar_cb(gint action, gpointer data)
2260 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2261 Compose *compose = (Compose*)toolbar_item->parent;
2263 g_return_if_fail(compose != NULL);
2267 compose_send_cb(compose, 0, NULL);
2270 compose_send_later_cb(compose, 0, NULL);
2273 compose_draft_cb(compose, COMPOSE_QUIT_EDITING, NULL);
2276 compose_insert_file_cb(compose, 0, NULL);
2279 compose_attach_cb(compose, 0, NULL);
2282 compose_insert_sig(compose, FALSE);
2285 compose_ext_editor_cb(compose, 0, NULL);
2287 case A_LINEWRAP_CURRENT:
2288 compose_beautify_paragraph(compose, NULL, TRUE);
2290 case A_LINEWRAP_ALL:
2291 compose_wrap_all_full(compose, TRUE);
2294 compose_address_cb(compose, 0, NULL);
2297 case A_CHECK_SPELLING:
2298 compose_check_all(compose);
2306 static void compose_entries_set(Compose *compose, const gchar *mailto)
2310 gchar *subject = NULL;
2314 gchar *attach = NULL;
2316 scan_mailto_url(mailto, &to, &cc, NULL, &subject, &body, &attach);
2319 compose_entry_append(compose, to, COMPOSE_TO);
2321 compose_entry_append(compose, cc, COMPOSE_CC);
2323 if (!g_utf8_validate (subject, -1, NULL)) {
2324 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2325 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2328 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2332 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2333 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2336 gboolean prev_autowrap = compose->autowrap;
2338 compose->autowrap = FALSE;
2340 mark = gtk_text_buffer_get_insert(buffer);
2341 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2343 if (!g_utf8_validate (body, -1, NULL)) {
2344 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2345 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2348 gtk_text_buffer_insert(buffer, &iter, body, -1);
2350 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2352 compose->autowrap = prev_autowrap;
2353 if (compose->autowrap)
2354 compose_wrap_all(compose);
2358 gchar *utf8_filename = conv_filename_to_utf8(attach);
2359 if (utf8_filename) {
2360 if (compose_attach_append(compose, attach, utf8_filename, NULL)) {
2361 alertpanel_notice(_("The file '%s' has been attached."), attach);
2363 g_free(utf8_filename);
2365 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2375 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2377 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2378 {"Cc:", NULL, TRUE},
2379 {"References:", NULL, FALSE},
2380 {"Bcc:", NULL, TRUE},
2381 {"Newsgroups:", NULL, TRUE},
2382 {"Followup-To:", NULL, TRUE},
2383 {"List-Post:", NULL, FALSE},
2384 {"X-Priority:", NULL, FALSE},
2385 {NULL, NULL, FALSE}};
2401 g_return_val_if_fail(msginfo != NULL, -1);
2403 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2404 procheader_get_header_fields(fp, hentry);
2407 if (hentry[H_REPLY_TO].body != NULL) {
2408 if (hentry[H_REPLY_TO].body[0] != '\0') {
2410 conv_unmime_header(hentry[H_REPLY_TO].body,
2413 g_free(hentry[H_REPLY_TO].body);
2414 hentry[H_REPLY_TO].body = NULL;
2416 if (hentry[H_CC].body != NULL) {
2417 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2418 g_free(hentry[H_CC].body);
2419 hentry[H_CC].body = NULL;
2421 if (hentry[H_REFERENCES].body != NULL) {
2422 if (compose->mode == COMPOSE_REEDIT)
2423 compose->references = hentry[H_REFERENCES].body;
2425 compose->references = compose_parse_references
2426 (hentry[H_REFERENCES].body, msginfo->msgid);
2427 g_free(hentry[H_REFERENCES].body);
2429 hentry[H_REFERENCES].body = NULL;
2431 if (hentry[H_BCC].body != NULL) {
2432 if (compose->mode == COMPOSE_REEDIT)
2434 conv_unmime_header(hentry[H_BCC].body, NULL);
2435 g_free(hentry[H_BCC].body);
2436 hentry[H_BCC].body = NULL;
2438 if (hentry[H_NEWSGROUPS].body != NULL) {
2439 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2440 hentry[H_NEWSGROUPS].body = NULL;
2442 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2443 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2444 compose->followup_to =
2445 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2448 g_free(hentry[H_FOLLOWUP_TO].body);
2449 hentry[H_FOLLOWUP_TO].body = NULL;
2451 if (hentry[H_LIST_POST].body != NULL) {
2454 extract_address(hentry[H_LIST_POST].body);
2455 if (hentry[H_LIST_POST].body[0] != '\0') {
2456 scan_mailto_url(hentry[H_LIST_POST].body,
2457 &to, NULL, NULL, NULL, NULL, NULL);
2459 g_free(compose->ml_post);
2460 compose->ml_post = to;
2463 g_free(hentry[H_LIST_POST].body);
2464 hentry[H_LIST_POST].body = NULL;
2467 /* CLAWS - X-Priority */
2468 if (compose->mode == COMPOSE_REEDIT)
2469 if (hentry[H_X_PRIORITY].body != NULL) {
2472 priority = atoi(hentry[H_X_PRIORITY].body);
2473 g_free(hentry[H_X_PRIORITY].body);
2475 hentry[H_X_PRIORITY].body = NULL;
2477 if (priority < PRIORITY_HIGHEST ||
2478 priority > PRIORITY_LOWEST)
2479 priority = PRIORITY_NORMAL;
2481 compose->priority = priority;
2484 if (compose->mode == COMPOSE_REEDIT) {
2485 if (msginfo->inreplyto && *msginfo->inreplyto)
2486 compose->inreplyto = g_strdup(msginfo->inreplyto);
2490 if (msginfo->msgid && *msginfo->msgid)
2491 compose->inreplyto = g_strdup(msginfo->msgid);
2493 if (!compose->references) {
2494 if (msginfo->msgid && *msginfo->msgid) {
2495 if (msginfo->inreplyto && *msginfo->inreplyto)
2496 compose->references =
2497 g_strdup_printf("<%s>\n\t<%s>",
2501 compose->references =
2502 g_strconcat("<", msginfo->msgid, ">",
2504 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2505 compose->references =
2506 g_strconcat("<", msginfo->inreplyto, ">",
2514 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2516 GSList *ref_id_list, *cur;
2520 ref_id_list = references_list_append(NULL, ref);
2521 if (!ref_id_list) return NULL;
2522 if (msgid && *msgid)
2523 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2528 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2529 /* "<" + Message-ID + ">" + CR+LF+TAB */
2530 len += strlen((gchar *)cur->data) + 5;
2532 if (len > MAX_REFERENCES_LEN) {
2533 /* remove second message-ID */
2534 if (ref_id_list && ref_id_list->next &&
2535 ref_id_list->next->next) {
2536 g_free(ref_id_list->next->data);
2537 ref_id_list = g_slist_remove
2538 (ref_id_list, ref_id_list->next->data);
2540 slist_free_strings(ref_id_list);
2541 g_slist_free(ref_id_list);
2548 new_ref = g_string_new("");
2549 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2550 if (new_ref->len > 0)
2551 g_string_append(new_ref, "\n\t");
2552 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2555 slist_free_strings(ref_id_list);
2556 g_slist_free(ref_id_list);
2558 new_ref_str = new_ref->str;
2559 g_string_free(new_ref, FALSE);
2564 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2565 const gchar *fmt, const gchar *qmark,
2566 const gchar *body, gboolean rewrap,
2567 gboolean need_unescape,
2568 const gchar *err_msg)
2570 MsgInfo* dummyinfo = NULL;
2571 gchar *quote_str = NULL;
2573 gboolean prev_autowrap;
2574 const gchar *trimmed_body = body;
2575 gint cursor_pos = -1;
2576 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2577 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2582 SIGNAL_BLOCK(buffer);
2585 dummyinfo = compose_msginfo_new_from_compose(compose);
2586 msginfo = dummyinfo;
2589 if (qmark != NULL) {
2591 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
2592 compose->gtkaspell);
2594 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
2596 quote_fmt_scan_string(qmark);
2599 buf = quote_fmt_get_buffer();
2601 alertpanel_error(_("Quote mark format error."));
2603 Xstrdup_a(quote_str, buf, goto error)
2606 if (fmt && *fmt != '\0') {
2609 while (*trimmed_body == '\n')
2613 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account,
2614 compose->gtkaspell);
2616 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account);
2618 if (need_unescape) {
2621 /* decode \-escape sequences in the internal representation of the quote format */
2622 tmp = malloc(strlen(fmt)+1);
2623 pref_get_unescaped_pref(tmp, fmt);
2624 quote_fmt_scan_string(tmp);
2628 quote_fmt_scan_string(fmt);
2632 buf = quote_fmt_get_buffer();
2634 gint line = quote_fmt_get_line();
2635 gchar *msg = g_strdup_printf(err_msg, line);
2636 alertpanel_error(msg);
2643 prev_autowrap = compose->autowrap;
2644 compose->autowrap = FALSE;
2646 mark = gtk_text_buffer_get_insert(buffer);
2647 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2648 if (g_utf8_validate(buf, -1, NULL)) {
2649 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2651 gchar *tmpout = NULL;
2652 tmpout = conv_codeset_strdup
2653 (buf, conv_get_locale_charset_str_no_utf8(),
2655 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2657 tmpout = g_malloc(strlen(buf)*2+1);
2658 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2660 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2664 cursor_pos = quote_fmt_get_cursor_pos();
2665 compose->set_cursor_pos = cursor_pos;
2666 if (cursor_pos == -1) {
2669 gtk_text_buffer_get_start_iter(buffer, &iter);
2670 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2671 gtk_text_buffer_place_cursor(buffer, &iter);
2673 compose->autowrap = prev_autowrap;
2674 if (compose->autowrap && rewrap)
2675 compose_wrap_all(compose);
2682 SIGNAL_UNBLOCK(buffer);
2684 procmsg_msginfo_free( dummyinfo );
2689 /* if ml_post is of type addr@host and from is of type
2690 * addr-anything@host, return TRUE
2692 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2694 gchar *left_ml = NULL;
2695 gchar *right_ml = NULL;
2696 gchar *left_from = NULL;
2697 gchar *right_from = NULL;
2698 gboolean result = FALSE;
2700 if (!ml_post || !from)
2703 left_ml = g_strdup(ml_post);
2704 if (strstr(left_ml, "@")) {
2705 right_ml = strstr(left_ml, "@")+1;
2706 *(strstr(left_ml, "@")) = '\0';
2709 left_from = g_strdup(from);
2710 if (strstr(left_from, "@")) {
2711 right_from = strstr(left_from, "@")+1;
2712 *(strstr(left_from, "@")) = '\0';
2715 if (left_ml && left_from && right_ml && right_from
2716 && !strncmp(left_from, left_ml, strlen(left_ml))
2717 && !strcmp(right_from, right_ml)) {
2726 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2728 gchar *my_addr1, *my_addr2;
2730 if (!addr1 || !addr2)
2733 Xstrdup_a(my_addr1, addr1, return FALSE);
2734 Xstrdup_a(my_addr2, addr2, return FALSE);
2736 extract_address(my_addr1);
2737 extract_address(my_addr2);
2739 return !strcasecmp(my_addr1, my_addr2);
2742 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2743 gboolean to_all, gboolean to_ml,
2745 gboolean followup_and_reply_to)
2747 GSList *cc_list = NULL;
2750 gchar *replyto = NULL;
2751 GHashTable *to_table;
2753 gboolean reply_to_ml = FALSE;
2754 gboolean default_reply_to = FALSE;
2756 g_return_if_fail(compose->account != NULL);
2757 g_return_if_fail(msginfo != NULL);
2759 reply_to_ml = to_ml && compose->ml_post;
2761 default_reply_to = msginfo->folder &&
2762 msginfo->folder->prefs->enable_default_reply_to;
2764 if (compose->account->protocol != A_NNTP) {
2765 if (reply_to_ml && !default_reply_to) {
2767 gboolean is_subscr = is_subscription(compose->ml_post,
2770 /* normal answer to ml post with a reply-to */
2771 compose_entry_append(compose,
2774 if (compose->replyto
2775 && !same_address(compose->ml_post, compose->replyto))
2776 compose_entry_append(compose,
2780 /* answer to subscription confirmation */
2781 if (compose->replyto)
2782 compose_entry_append(compose,
2785 else if (msginfo->from)
2786 compose_entry_append(compose,
2791 else if (!(to_all || to_sender) && default_reply_to) {
2792 compose_entry_append(compose,
2793 msginfo->folder->prefs->default_reply_to,
2795 compose_entry_mark_default_to(compose,
2796 msginfo->folder->prefs->default_reply_to);
2801 Xstrdup_a(tmp1, msginfo->from, return);
2802 extract_address(tmp1);
2803 if (to_all || to_sender ||
2804 !account_find_from_address(tmp1))
2805 compose_entry_append(compose,
2806 (compose->replyto && !to_sender)
2807 ? compose->replyto :
2808 msginfo->from ? msginfo->from : "",
2810 else if (!to_all && !to_sender) {
2811 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2812 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
2813 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2814 compose_entry_append(compose,
2815 msginfo->from ? msginfo->from : "",
2818 /* replying to own mail, use original recp */
2819 compose_entry_append(compose,
2820 msginfo->to ? msginfo->to : "",
2822 compose_entry_append(compose,
2823 msginfo->cc ? msginfo->cc : "",
2829 if (to_sender || (compose->followup_to &&
2830 !strncmp(compose->followup_to, "poster", 6)))
2831 compose_entry_append
2833 (compose->replyto ? compose->replyto :
2834 msginfo->from ? msginfo->from : ""),
2837 else if (followup_and_reply_to || to_all) {
2838 compose_entry_append
2840 (compose->replyto ? compose->replyto :
2841 msginfo->from ? msginfo->from : ""),
2844 compose_entry_append
2846 compose->followup_to ? compose->followup_to :
2847 compose->newsgroups ? compose->newsgroups : "",
2848 COMPOSE_NEWSGROUPS);
2851 compose_entry_append
2853 compose->followup_to ? compose->followup_to :
2854 compose->newsgroups ? compose->newsgroups : "",
2855 COMPOSE_NEWSGROUPS);
2858 if (msginfo->subject && *msginfo->subject) {
2862 buf = p = g_strdup(msginfo->subject);
2863 p += subject_get_prefix_length(p);
2864 memmove(buf, p, strlen(p) + 1);
2866 buf2 = g_strdup_printf("Re: %s", buf);
2867 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2872 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
2874 if (to_ml && compose->ml_post) return;
2875 if (!to_all || compose->account->protocol == A_NNTP) return;
2877 if (compose->replyto) {
2878 Xstrdup_a(replyto, compose->replyto, return);
2879 extract_address(replyto);
2881 if (msginfo->from) {
2882 Xstrdup_a(from, msginfo->from, return);
2883 extract_address(from);
2886 if (replyto && from)
2887 cc_list = address_list_append_with_comments(cc_list, from);
2888 if (to_all && msginfo->folder &&
2889 msginfo->folder->prefs->enable_default_reply_to)
2890 cc_list = address_list_append_with_comments(cc_list,
2891 msginfo->folder->prefs->default_reply_to);
2892 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
2893 cc_list = address_list_append_with_comments(cc_list, compose->cc);
2895 to_table = g_hash_table_new(g_str_hash, g_str_equal);
2897 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
2898 if (compose->account) {
2899 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
2900 GINT_TO_POINTER(1));
2902 /* remove address on To: and that of current account */
2903 for (cur = cc_list; cur != NULL; ) {
2904 GSList *next = cur->next;
2907 addr = g_utf8_strdown(cur->data, -1);
2908 extract_address(addr);
2910 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
2911 cc_list = g_slist_remove(cc_list, cur->data);
2913 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
2917 hash_free_strings(to_table);
2918 g_hash_table_destroy(to_table);
2921 for (cur = cc_list; cur != NULL; cur = cur->next)
2922 compose_entry_append(compose, (gchar *)cur->data,
2924 slist_free_strings(cc_list);
2925 g_slist_free(cc_list);
2930 #define SET_ENTRY(entry, str) \
2933 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
2936 #define SET_ADDRESS(type, str) \
2939 compose_entry_append(compose, str, type); \
2942 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
2944 g_return_if_fail(msginfo != NULL);
2946 SET_ENTRY(subject_entry, msginfo->subject);
2947 SET_ENTRY(from_name, msginfo->from);
2948 SET_ADDRESS(COMPOSE_TO, msginfo->to);
2949 SET_ADDRESS(COMPOSE_CC, compose->cc);
2950 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
2951 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
2952 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
2953 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
2955 compose_update_priority_menu_item(compose);
2956 compose_update_privacy_system_menu_item(compose, FALSE);
2957 compose_show_first_last_header(compose, TRUE);
2963 static void compose_insert_sig(Compose *compose, gboolean replace)
2965 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2966 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2968 GtkTextIter iter, iter_end;
2970 gchar *search = NULL;
2971 gboolean prev_autowrap;
2972 gboolean found = FALSE, shift = FALSE;
2975 g_return_if_fail(compose->account != NULL);
2977 prev_autowrap = compose->autowrap;
2978 compose->autowrap = FALSE;
2980 g_signal_handlers_block_by_func(G_OBJECT(buffer),
2981 G_CALLBACK(compose_changed_cb),
2984 mark = gtk_text_buffer_get_insert(buffer);
2985 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2986 cur_pos = gtk_text_iter_get_offset (&iter);
2988 gtk_text_buffer_get_end_iter(buffer, &iter);
2990 search = compose->sig_str;
2992 if (replace && search) {
2993 GtkTextIter first_iter, start_iter, end_iter;
2995 gtk_text_buffer_get_start_iter(buffer, &first_iter);
2997 if (compose->sig_str[0] == '\0')
3000 found = gtk_text_iter_forward_search(&first_iter,
3002 GTK_TEXT_SEARCH_TEXT_ONLY,
3003 &start_iter, &end_iter,
3007 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3011 if (replace && !found && search && strlen(search) > 2
3012 && search[0] == '\n' && search[1] == '\n') {
3018 g_free(compose->sig_str);
3019 compose->sig_str = compose_get_signature_str(compose);
3020 if (!compose->sig_str || (replace && !compose->account->auto_sig))
3021 compose->sig_str = g_strdup("");
3023 cur_pos = gtk_text_iter_get_offset(&iter);
3025 gtk_text_buffer_insert(buffer, &iter, compose->sig_str + 1, -1);
3027 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3029 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3030 gtk_text_iter_forward_char(&iter);
3031 gtk_text_iter_forward_char(&iter);
3032 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3033 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3035 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3036 cur_pos = gtk_text_buffer_get_char_count (buffer);
3038 /* put the cursor where it should be
3039 * either where the quote_fmt says, either before the signature */
3040 if (compose->set_cursor_pos < 0)
3041 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3043 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3044 compose->set_cursor_pos);
3046 gtk_text_buffer_place_cursor(buffer, &iter);
3047 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3048 G_CALLBACK(compose_changed_cb),
3051 compose->autowrap = prev_autowrap;
3052 if (compose->autowrap)
3053 compose_wrap_all(compose);
3056 static gchar *compose_get_signature_str(Compose *compose)
3058 gchar *sig_body = NULL;
3059 gchar *sig_str = NULL;
3060 gchar *utf8_sig_str = NULL;
3062 g_return_val_if_fail(compose->account != NULL, NULL);
3064 if (!compose->account->sig_path)
3067 if (compose->account->sig_type == SIG_FILE) {
3068 if (!is_file_or_fifo_exist(compose->account->sig_path)) {
3069 g_warning("can't open signature file: %s\n",
3070 compose->account->sig_path);
3075 if (compose->account->sig_type == SIG_COMMAND)
3076 sig_body = get_command_output(compose->account->sig_path);
3080 tmp = file_read_to_str(compose->account->sig_path);
3083 sig_body = normalize_newlines(tmp);
3087 if (compose->account->sig_sep) {
3088 sig_str = g_strconcat("\n\n", compose->account->sig_sep, "\n", sig_body,
3092 sig_str = g_strconcat("\n\n", sig_body, NULL);
3095 if (g_utf8_validate(sig_str, -1, NULL) == TRUE)
3096 utf8_sig_str = sig_str;
3098 utf8_sig_str = conv_codeset_strdup
3099 (sig_str, conv_get_locale_charset_str_no_utf8(),
3105 return utf8_sig_str;
3108 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3111 GtkTextBuffer *buffer;
3114 const gchar *cur_encoding;
3115 gchar buf[BUFFSIZE];
3118 gboolean prev_autowrap;
3119 gboolean badtxt = FALSE;
3121 g_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3123 if ((fp = g_fopen(file, "rb")) == NULL) {
3124 FILE_OP_ERROR(file, "fopen");
3125 return COMPOSE_INSERT_READ_ERROR;
3128 prev_autowrap = compose->autowrap;
3129 compose->autowrap = FALSE;
3131 text = GTK_TEXT_VIEW(compose->text);
3132 buffer = gtk_text_view_get_buffer(text);
3133 mark = gtk_text_buffer_get_insert(buffer);
3134 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3136 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3137 G_CALLBACK(text_inserted),
3140 cur_encoding = conv_get_locale_charset_str_no_utf8();
3142 while (fgets(buf, sizeof(buf), fp) != NULL) {
3145 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3146 str = g_strdup(buf);
3148 str = conv_codeset_strdup
3149 (buf, cur_encoding, CS_INTERNAL);
3152 /* strip <CR> if DOS/Windows file,
3153 replace <CR> with <LF> if Macintosh file. */
3156 if (len > 0 && str[len - 1] != '\n') {
3158 if (str[len] == '\r') str[len] = '\n';
3161 gtk_text_buffer_insert(buffer, &iter, str, -1);
3165 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3166 G_CALLBACK(text_inserted),
3168 compose->autowrap = prev_autowrap;
3169 if (compose->autowrap)
3170 compose_wrap_all(compose);
3175 return COMPOSE_INSERT_INVALID_CHARACTER;
3177 return COMPOSE_INSERT_SUCCESS;
3180 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3181 const gchar *filename,
3182 const gchar *content_type)
3190 GtkListStore *store;
3192 gboolean has_binary = FALSE;
3194 if (!is_file_exist(file)) {
3195 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3196 gboolean result = FALSE;
3197 if (file_from_uri && is_file_exist(file_from_uri)) {
3198 result = compose_attach_append(
3199 compose, file_from_uri,