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 PrefsAccount *reply_account;
1402 GtkTextView *textview;
1403 GtkTextBuffer *textbuf;
1404 gboolean quote = FALSE;
1405 gchar *qmark = NULL;
1406 gchar *body_fmt = NULL;
1408 g_return_val_if_fail(msginfo != NULL, NULL);
1409 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1411 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1413 g_return_val_if_fail(account != NULL, NULL);
1415 if (to_sender && account->protocol == A_NNTP &&
1416 !followup_and_reply_to) {
1418 account_find_from_address(account->address);
1420 reply_account = compose_current_mail_account();
1424 reply_account = account;
1425 /* wwp: TODO: why isn't reply_account used below? shouldn't we use reply_account instead of account? */
1427 compose = compose_create(account, COMPOSE_REPLY, FALSE);
1429 compose->updating = TRUE;
1431 ifactory = gtk_item_factory_from_widget(compose->menubar);
1433 menu_set_active(ifactory, "/Options/Remove references", FALSE);
1434 menu_set_sensitive(ifactory, "/Options/Remove references", TRUE);
1436 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1437 if (!compose->replyinfo)
1438 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1440 compose_extract_original_charset(compose);
1442 if (msginfo->folder && msginfo->folder->ret_rcpt)
1443 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
1445 /* Set save folder */
1446 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1447 gchar *folderidentifier;
1449 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1450 folderidentifier = folder_item_get_identifier(msginfo->folder);
1451 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1452 g_free(folderidentifier);
1455 if (compose_parse_header(compose, msginfo) < 0) return NULL;
1457 textview = (GTK_TEXT_VIEW(compose->text));
1458 textbuf = gtk_text_view_get_buffer(textview);
1459 compose_create_tags(textview, compose);
1461 undo_block(compose->undostruct);
1463 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1466 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1467 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1468 /* use the reply format of folder (if enabled), or the account's one
1469 (if enabled) or fallback to the global reply format, which is always
1470 enabled (even if empty), and use the relevant quotemark */
1472 if (msginfo->folder && msginfo->folder->prefs &&
1473 msginfo->folder->prefs->reply_with_format) {
1474 qmark = msginfo->folder->prefs->reply_quotemark;
1475 body_fmt = msginfo->folder->prefs->reply_body_format;
1477 } else if (account->reply_with_format) {
1478 qmark = account->reply_quotemark;
1479 body_fmt = account->reply_body_format;
1482 qmark = prefs_common.quotemark;
1483 body_fmt = prefs_common.quotefmt;
1488 /* empty quotemark is not allowed */
1489 if (qmark && !*qmark);
1491 compose_quote_fmt(compose, compose->replyinfo,
1492 body_fmt, qmark, body, FALSE, TRUE,
1493 _("Message reply format error at line %d."));
1494 quote_fmt_reset_vartable();
1496 if (procmime_msginfo_is_encrypted(compose->replyinfo)) {
1497 compose_force_encryption(compose, account, FALSE);
1500 SIGNAL_BLOCK(textbuf);
1502 if (account->auto_sig)
1503 compose_insert_sig(compose, FALSE);
1505 compose_wrap_all(compose);
1507 SIGNAL_UNBLOCK(textbuf);
1509 gtk_widget_grab_focus(compose->text);
1511 undo_unblock(compose->undostruct);
1513 if (prefs_common.auto_exteditor)
1514 compose_exec_ext_editor(compose);
1516 compose->modified = FALSE;
1517 compose_set_title(compose);
1519 compose->updating = FALSE;
1521 if (compose->deferred_destroy) {
1522 compose_destroy(compose);
1529 #define INSERT_FW_HEADER(var, hdr) \
1530 if (msginfo->var && *msginfo->var) { \
1531 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1532 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1533 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1536 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1537 gboolean as_attach, const gchar *body,
1538 gboolean no_extedit,
1542 GtkTextView *textview;
1543 GtkTextBuffer *textbuf;
1546 g_return_val_if_fail(msginfo != NULL, NULL);
1547 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1550 !(account = compose_guess_forward_account_from_msginfo
1552 account = cur_account;
1554 compose = compose_create(account, COMPOSE_FORWARD, batch);
1556 compose->updating = TRUE;
1557 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1558 if (!compose->fwdinfo)
1559 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1561 compose_extract_original_charset(compose);
1563 if (msginfo->subject && *msginfo->subject) {
1564 gchar *buf, *buf2, *p;
1566 buf = p = g_strdup(msginfo->subject);
1567 p += subject_get_prefix_length(p);
1568 memmove(buf, p, strlen(p) + 1);
1570 buf2 = g_strdup_printf("Fw: %s", buf);
1571 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1577 textview = GTK_TEXT_VIEW(compose->text);
1578 textbuf = gtk_text_view_get_buffer(textview);
1579 compose_create_tags(textview, compose);
1581 undo_block(compose->undostruct);
1585 msgfile = procmsg_get_message_file_path(msginfo);
1586 if (!is_file_exist(msgfile))
1587 g_warning("%s: file not exist\n", msgfile);
1589 compose_attach_append(compose, msgfile, msgfile,
1594 gchar *qmark = NULL;
1595 gchar *body_fmt = prefs_common.fw_quotefmt;
1596 MsgInfo *full_msginfo;
1598 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1600 full_msginfo = procmsg_msginfo_copy(msginfo);
1602 /* use the forward format of folder (if enabled), or the account's one
1603 (if enabled) or fallback to the global forward format, which is always
1604 enabled (even if empty), and use the relevant quotemark */
1605 if (msginfo->folder && msginfo->folder->prefs &&
1606 msginfo->folder->prefs->forward_with_format) {
1607 qmark = msginfo->folder->prefs->forward_quotemark;
1608 body_fmt = msginfo->folder->prefs->forward_body_format;
1610 } else if (account->forward_with_format) {
1611 qmark = account->forward_quotemark;
1612 body_fmt = account->forward_body_format;
1615 qmark = prefs_common.fw_quotemark;
1616 body_fmt = prefs_common.fw_quotefmt;
1619 /* empty quotemark is not allowed */
1620 if (qmark && !*qmark)
1623 compose_quote_fmt(compose, full_msginfo,
1624 body_fmt, qmark, body, FALSE, TRUE,
1625 _("Message forward format error at line %d."));
1626 quote_fmt_reset_vartable();
1627 compose_attach_parts(compose, msginfo);
1629 procmsg_msginfo_free(full_msginfo);
1632 SIGNAL_BLOCK(textbuf);
1634 if (account->auto_sig)
1635 compose_insert_sig(compose, FALSE);
1637 compose_wrap_all(compose);
1639 SIGNAL_UNBLOCK(textbuf);
1641 gtk_text_buffer_get_start_iter(textbuf, &iter);
1642 gtk_text_buffer_place_cursor(textbuf, &iter);
1644 gtk_widget_grab_focus(compose->header_last->entry);
1646 if (!no_extedit && prefs_common.auto_exteditor)
1647 compose_exec_ext_editor(compose);
1650 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1651 gchar *folderidentifier;
1653 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1654 folderidentifier = folder_item_get_identifier(msginfo->folder);
1655 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1656 g_free(folderidentifier);
1659 undo_unblock(compose->undostruct);
1661 compose->modified = FALSE;
1662 compose_set_title(compose);
1664 compose->updating = FALSE;
1666 if (compose->deferred_destroy) {
1667 compose_destroy(compose);
1674 #undef INSERT_FW_HEADER
1676 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1679 GtkTextView *textview;
1680 GtkTextBuffer *textbuf;
1684 gboolean single_mail = TRUE;
1686 g_return_val_if_fail(msginfo_list != NULL, NULL);
1688 if (g_slist_length(msginfo_list) > 1)
1689 single_mail = FALSE;
1691 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1692 if (((MsgInfo *)msginfo->data)->folder == NULL)
1695 /* guess account from first selected message */
1697 !(account = compose_guess_forward_account_from_msginfo
1698 (msginfo_list->data)))
1699 account = cur_account;
1701 g_return_val_if_fail(account != NULL, NULL);
1703 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1704 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1705 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1708 compose = compose_create(account, COMPOSE_FORWARD, FALSE);
1710 compose->updating = TRUE;
1712 textview = GTK_TEXT_VIEW(compose->text);
1713 textbuf = gtk_text_view_get_buffer(textview);
1714 compose_create_tags(textview, compose);
1716 undo_block(compose->undostruct);
1717 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1718 msgfile = procmsg_get_message_file_path((MsgInfo *)msginfo->data);
1720 if (!is_file_exist(msgfile))
1721 g_warning("%s: file not exist\n", msgfile);
1723 compose_attach_append(compose, msgfile, msgfile,
1729 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1730 if (info->subject && *info->subject) {
1731 gchar *buf, *buf2, *p;
1733 buf = p = g_strdup(info->subject);
1734 p += subject_get_prefix_length(p);
1735 memmove(buf, p, strlen(p) + 1);
1737 buf2 = g_strdup_printf("Fw: %s", buf);
1738 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1744 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1745 _("Fw: multiple emails"));
1748 SIGNAL_BLOCK(textbuf);
1750 if (account->auto_sig)
1751 compose_insert_sig(compose, FALSE);
1753 compose_wrap_all(compose);
1755 SIGNAL_UNBLOCK(textbuf);
1757 gtk_text_buffer_get_start_iter(textbuf, &iter);
1758 gtk_text_buffer_place_cursor(textbuf, &iter);
1760 gtk_widget_grab_focus(compose->header_last->entry);
1761 undo_unblock(compose->undostruct);
1762 compose->modified = FALSE;
1763 compose_set_title(compose);
1765 compose->updating = FALSE;
1767 if (compose->deferred_destroy) {
1768 compose_destroy(compose);
1775 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1777 GtkTextIter start = *iter;
1778 GtkTextIter end_iter;
1779 int start_pos = gtk_text_iter_get_offset(&start);
1781 if (!compose->account->sig_sep)
1784 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1785 start_pos+strlen(compose->account->sig_sep));
1787 /* check sig separator */
1788 str = gtk_text_iter_get_text(&start, &end_iter);
1789 if (!strcmp(str, compose->account->sig_sep)) {
1791 /* check end of line (\n) */
1792 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1793 start_pos+strlen(compose->account->sig_sep));
1794 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1795 start_pos+strlen(compose->account->sig_sep)+1);
1796 tmp = gtk_text_iter_get_text(&start, &end_iter);
1797 if (!strcmp(tmp,"\n")) {
1809 static void compose_colorize_signature(Compose *compose)
1811 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1813 GtkTextIter end_iter;
1814 gtk_text_buffer_get_start_iter(buffer, &iter);
1815 while (gtk_text_iter_forward_line(&iter))
1816 if (compose_is_sig_separator(compose, buffer, &iter)) {
1817 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1818 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1822 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
1824 Compose *compose = NULL;
1825 PrefsAccount *account = NULL;
1826 GtkTextView *textview;
1827 GtkTextBuffer *textbuf;
1831 gchar buf[BUFFSIZE];
1832 gboolean use_signing = FALSE;
1833 gboolean use_encryption = FALSE;
1834 gchar *privacy_system = NULL;
1835 int priority = PRIORITY_NORMAL;
1836 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
1838 g_return_val_if_fail(msginfo != NULL, NULL);
1839 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1841 if (compose_put_existing_to_front(msginfo)) {
1845 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
1846 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
1847 gchar queueheader_buf[BUFFSIZE];
1850 /* Select Account from queue headers */
1851 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1852 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
1853 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
1854 account = account_find_from_id(id);
1856 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1857 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
1858 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
1859 account = account_find_from_id(id);
1861 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1862 sizeof(queueheader_buf), "NAID:")) {
1863 id = atoi(&queueheader_buf[strlen("NAID:")]);
1864 account = account_find_from_id(id);
1866 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1867 sizeof(queueheader_buf), "MAID:")) {
1868 id = atoi(&queueheader_buf[strlen("MAID:")]);
1869 account = account_find_from_id(id);
1871 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1872 sizeof(queueheader_buf), "S:")) {
1873 account = account_find_from_address(queueheader_buf);
1875 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1876 sizeof(queueheader_buf), "X-Claws-Sign:")) {
1877 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
1878 use_signing = param;
1881 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1882 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
1883 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
1884 use_signing = param;
1887 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1888 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
1889 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
1890 use_encryption = param;
1892 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1893 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
1894 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
1895 use_encryption = param;
1897 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1898 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
1899 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
1901 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1902 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
1903 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
1905 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1906 sizeof(queueheader_buf), "X-Priority: ")) {
1907 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
1910 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1911 sizeof(queueheader_buf), "RMID:")) {
1912 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
1913 if (tokens[0] && tokens[1] && tokens[2]) {
1914 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
1915 if (orig_item != NULL) {
1916 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
1921 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1922 sizeof(queueheader_buf), "FMID:")) {
1923 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
1924 if (tokens[0] && tokens[1] && tokens[2]) {
1925 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
1926 if (orig_item != NULL) {
1927 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
1933 account = msginfo->folder->folder->account;
1936 if (!account && prefs_common.reedit_account_autosel) {
1937 gchar from[BUFFSIZE];
1938 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
1939 extract_address(from);
1940 account = account_find_from_address(from);
1944 account = cur_account;
1946 g_return_val_if_fail(account != NULL, NULL);
1948 compose = compose_create(account, COMPOSE_REEDIT, batch);
1950 compose->replyinfo = replyinfo;
1951 compose->fwdinfo = fwdinfo;
1953 compose->updating = TRUE;
1954 compose->priority = priority;
1956 if (privacy_system != NULL) {
1957 compose->privacy_system = privacy_system;
1958 compose_use_signing(compose, use_signing);
1959 compose_use_encryption(compose, use_encryption);
1960 compose_update_privacy_system_menu_item(compose, FALSE);
1962 activate_privacy_system(compose, account, FALSE);
1965 compose->targetinfo = procmsg_msginfo_copy(msginfo);
1967 compose_extract_original_charset(compose);
1969 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
1970 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
1971 gchar queueheader_buf[BUFFSIZE];
1973 /* Set message save folder */
1974 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
1977 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1978 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
1979 gtk_editable_insert_text(GTK_EDITABLE(compose->savemsg_entry), &queueheader_buf[4], strlen(&queueheader_buf[4]), &startpos);
1981 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
1982 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
1984 GtkItemFactory *ifactory;
1985 ifactory = gtk_item_factory_from_widget(compose->menubar);
1986 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
1991 if (compose_parse_header(compose, msginfo) < 0) {
1992 compose->updating = FALSE;
1993 compose_destroy(compose);
1996 compose_reedit_set_entry(compose, msginfo);
1998 textview = GTK_TEXT_VIEW(compose->text);
1999 textbuf = gtk_text_view_get_buffer(textview);
2000 compose_create_tags(textview, compose);
2002 mark = gtk_text_buffer_get_insert(textbuf);
2003 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2005 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2006 G_CALLBACK(compose_changed_cb),
2009 if (procmime_msginfo_is_encrypted(msginfo)) {
2010 fp = procmime_get_first_encrypted_text_content(msginfo);
2012 compose_force_encryption(compose, account, TRUE);
2015 fp = procmime_get_first_text_content(msginfo);
2018 g_warning("Can't get text part\n");
2022 gboolean prev_autowrap = compose->autowrap;
2024 compose->autowrap = FALSE;
2025 while (fgets(buf, sizeof(buf), fp) != NULL) {
2027 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2029 compose_wrap_all_full(compose, FALSE);
2030 compose->autowrap = prev_autowrap;
2034 compose_attach_parts(compose, msginfo);
2036 compose_colorize_signature(compose);
2038 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2039 G_CALLBACK(compose_changed_cb),
2042 gtk_widget_grab_focus(compose->text);
2044 if (prefs_common.auto_exteditor) {
2045 compose_exec_ext_editor(compose);
2047 compose->modified = FALSE;
2048 compose_set_title(compose);
2050 compose->updating = FALSE;
2052 if (compose->deferred_destroy) {
2053 compose_destroy(compose);
2057 compose->sig_str = compose_get_signature_str(compose);
2062 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2067 GtkItemFactory *ifactory;
2070 g_return_val_if_fail(msginfo != NULL, NULL);
2073 account = account_get_reply_account(msginfo,
2074 prefs_common.reply_account_autosel);
2075 g_return_val_if_fail(account != NULL, NULL);
2077 compose = compose_create(account, COMPOSE_REDIRECT, batch);
2079 compose->updating = TRUE;
2081 ifactory = gtk_item_factory_from_widget(compose->menubar);
2082 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2083 compose->replyinfo = NULL;
2084 compose->fwdinfo = NULL;
2086 compose_show_first_last_header(compose, TRUE);
2088 gtk_widget_grab_focus(compose->header_last->entry);
2090 filename = procmsg_get_message_file_path(msginfo);
2092 if (filename == NULL) {
2093 compose->updating = FALSE;
2094 compose_destroy(compose);
2099 compose->redirect_filename = filename;
2101 /* Set save folder */
2102 item = msginfo->folder;
2103 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2104 gchar *folderidentifier;
2106 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2107 folderidentifier = folder_item_get_identifier(item);
2108 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
2109 g_free(folderidentifier);
2112 compose_attach_parts(compose, msginfo);
2114 if (msginfo->subject)
2115 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2117 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2119 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2120 _("Message redirect format error at line %d."));
2121 quote_fmt_reset_vartable();
2122 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2124 compose_colorize_signature(compose);
2126 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
2127 menu_set_sensitive(ifactory, "/Add...", FALSE);
2128 menu_set_sensitive(ifactory, "/Remove", FALSE);
2129 menu_set_sensitive(ifactory, "/Properties...", FALSE);
2131 ifactory = gtk_item_factory_from_widget(compose->menubar);
2132 menu_set_sensitive(ifactory, "/Message/Save", FALSE);
2133 menu_set_sensitive(ifactory, "/Message/Insert file", FALSE);
2134 menu_set_sensitive(ifactory, "/Message/Attach file", FALSE);
2135 menu_set_sensitive(ifactory, "/Message/Insert signature", FALSE);
2136 menu_set_sensitive(ifactory, "/Edit", FALSE);
2137 menu_set_sensitive(ifactory, "/Options", FALSE);
2138 menu_set_sensitive(ifactory, "/Tools/Show ruler", FALSE);
2139 menu_set_sensitive(ifactory, "/Tools/Actions", FALSE);
2141 if (compose->toolbar->draft_btn)
2142 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2143 if (compose->toolbar->insert_btn)
2144 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2145 if (compose->toolbar->attach_btn)
2146 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2147 if (compose->toolbar->sig_btn)
2148 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2149 if (compose->toolbar->exteditor_btn)
2150 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2151 if (compose->toolbar->linewrap_current_btn)
2152 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2153 if (compose->toolbar->linewrap_all_btn)
2154 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2156 compose->modified = FALSE;
2157 compose_set_title(compose);
2158 compose->updating = FALSE;
2160 if (compose->deferred_destroy) {
2161 compose_destroy(compose);
2168 GList *compose_get_compose_list(void)
2170 return compose_list;
2173 void compose_entry_append(Compose *compose, const gchar *address,
2174 ComposeEntryType type)
2176 const gchar *header;
2178 gboolean in_quote = FALSE;
2179 if (!address || *address == '\0') return;
2186 header = N_("Bcc:");
2188 case COMPOSE_REPLYTO:
2189 header = N_("Reply-To:");
2191 case COMPOSE_NEWSGROUPS:
2192 header = N_("Newsgroups:");
2194 case COMPOSE_FOLLOWUPTO:
2195 header = N_( "Followup-To:");
2202 header = prefs_common_translated_header_name(header);
2204 cur = begin = (gchar *)address;
2206 /* we separate the line by commas, but not if we're inside a quoted
2208 while (*cur != '\0') {
2210 in_quote = !in_quote;
2211 if (*cur == ',' && !in_quote) {
2212 gchar *tmp = g_strdup(begin);
2214 tmp[cur-begin]='\0';
2217 while (*tmp == ' ' || *tmp == '\t')
2219 compose_add_header_entry(compose, header, tmp);
2226 gchar *tmp = g_strdup(begin);
2228 tmp[cur-begin]='\0';
2231 while (*tmp == ' ' || *tmp == '\t')
2233 compose_add_header_entry(compose, header, tmp);
2238 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2240 static GdkColor yellow;
2241 static GdkColor black;
2242 static gboolean yellow_initialised = FALSE;
2246 if (!yellow_initialised) {
2247 gdk_color_parse("#f5f6be", &yellow);
2248 gdk_color_parse("#000000", &black);
2249 yellow_initialised = gdk_colormap_alloc_color(
2250 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2251 yellow_initialised &= gdk_colormap_alloc_color(
2252 gdk_colormap_get_system(), &black, FALSE, TRUE);
2255 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2256 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2257 if (gtk_entry_get_text(entry) &&
2258 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2259 if (yellow_initialised) {
2260 gtk_widget_modify_base(
2261 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2262 GTK_STATE_NORMAL, &yellow);
2263 gtk_widget_modify_text(
2264 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2265 GTK_STATE_NORMAL, &black);
2271 void compose_toolbar_cb(gint action, gpointer data)
2273 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2274 Compose *compose = (Compose*)toolbar_item->parent;
2276 g_return_if_fail(compose != NULL);
2280 compose_send_cb(compose, 0, NULL);
2283 compose_send_later_cb(compose, 0, NULL);
2286 compose_draft_cb(compose, COMPOSE_QUIT_EDITING, NULL);
2289 compose_insert_file_cb(compose, 0, NULL);
2292 compose_attach_cb(compose, 0, NULL);
2295 compose_insert_sig(compose, FALSE);
2298 compose_ext_editor_cb(compose, 0, NULL);
2300 case A_LINEWRAP_CURRENT:
2301 compose_beautify_paragraph(compose, NULL, TRUE);
2303 case A_LINEWRAP_ALL:
2304 compose_wrap_all_full(compose, TRUE);
2307 compose_address_cb(compose, 0, NULL);
2310 case A_CHECK_SPELLING:
2311 compose_check_all(compose);
2319 static void compose_entries_set(Compose *compose, const gchar *mailto)
2323 gchar *subject = NULL;
2327 gchar *attach = NULL;
2329 scan_mailto_url(mailto, &to, &cc, NULL, &subject, &body, &attach);
2332 compose_entry_append(compose, to, COMPOSE_TO);
2334 compose_entry_append(compose, cc, COMPOSE_CC);
2336 if (!g_utf8_validate (subject, -1, NULL)) {
2337 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2338 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2341 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2345 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2346 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2349 gboolean prev_autowrap = compose->autowrap;
2351 compose->autowrap = FALSE;
2353 mark = gtk_text_buffer_get_insert(buffer);
2354 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2356 if (!g_utf8_validate (body, -1, NULL)) {
2357 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2358 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2361 gtk_text_buffer_insert(buffer, &iter, body, -1);
2363 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2365 compose->autowrap = prev_autowrap;
2366 if (compose->autowrap)
2367 compose_wrap_all(compose);
2371 gchar *utf8_filename = conv_filename_to_utf8(attach);
2372 if (utf8_filename) {
2373 if (compose_attach_append(compose, attach, utf8_filename, NULL)) {
2374 alertpanel_notice(_("The file '%s' has been attached."), attach);
2376 g_free(utf8_filename);
2378 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2388 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2390 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2391 {"Cc:", NULL, TRUE},
2392 {"References:", NULL, FALSE},
2393 {"Bcc:", NULL, TRUE},
2394 {"Newsgroups:", NULL, TRUE},
2395 {"Followup-To:", NULL, TRUE},
2396 {"List-Post:", NULL, FALSE},
2397 {"X-Priority:", NULL, FALSE},
2398 {NULL, NULL, FALSE}};
2414 g_return_val_if_fail(msginfo != NULL, -1);
2416 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2417 procheader_get_header_fields(fp, hentry);
2420 if (hentry[H_REPLY_TO].body != NULL) {
2421 if (hentry[H_REPLY_TO].body[0] != '\0') {
2423 conv_unmime_header(hentry[H_REPLY_TO].body,
2426 g_free(hentry[H_REPLY_TO].body);
2427 hentry[H_REPLY_TO].body = NULL;
2429 if (hentry[H_CC].body != NULL) {
2430 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2431 g_free(hentry[H_CC].body);
2432 hentry[H_CC].body = NULL;
2434 if (hentry[H_REFERENCES].body != NULL) {
2435 if (compose->mode == COMPOSE_REEDIT)
2436 compose->references = hentry[H_REFERENCES].body;
2438 compose->references = compose_parse_references
2439 (hentry[H_REFERENCES].body, msginfo->msgid);
2440 g_free(hentry[H_REFERENCES].body);
2442 hentry[H_REFERENCES].body = NULL;
2444 if (hentry[H_BCC].body != NULL) {
2445 if (compose->mode == COMPOSE_REEDIT)
2447 conv_unmime_header(hentry[H_BCC].body, NULL);
2448 g_free(hentry[H_BCC].body);
2449 hentry[H_BCC].body = NULL;
2451 if (hentry[H_NEWSGROUPS].body != NULL) {
2452 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2453 hentry[H_NEWSGROUPS].body = NULL;
2455 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2456 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2457 compose->followup_to =
2458 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2461 g_free(hentry[H_FOLLOWUP_TO].body);
2462 hentry[H_FOLLOWUP_TO].body = NULL;
2464 if (hentry[H_LIST_POST].body != NULL) {
2467 extract_address(hentry[H_LIST_POST].body);
2468 if (hentry[H_LIST_POST].body[0] != '\0') {
2469 scan_mailto_url(hentry[H_LIST_POST].body,
2470 &to, NULL, NULL, NULL, NULL, NULL);
2472 g_free(compose->ml_post);
2473 compose->ml_post = to;
2476 g_free(hentry[H_LIST_POST].body);
2477 hentry[H_LIST_POST].body = NULL;
2480 /* CLAWS - X-Priority */
2481 if (compose->mode == COMPOSE_REEDIT)
2482 if (hentry[H_X_PRIORITY].body != NULL) {
2485 priority = atoi(hentry[H_X_PRIORITY].body);
2486 g_free(hentry[H_X_PRIORITY].body);
2488 hentry[H_X_PRIORITY].body = NULL;
2490 if (priority < PRIORITY_HIGHEST ||
2491 priority > PRIORITY_LOWEST)
2492 priority = PRIORITY_NORMAL;
2494 compose->priority = priority;
2497 if (compose->mode == COMPOSE_REEDIT) {
2498 if (msginfo->inreplyto && *msginfo->inreplyto)
2499 compose->inreplyto = g_strdup(msginfo->inreplyto);
2503 if (msginfo->msgid && *msginfo->msgid)
2504 compose->inreplyto = g_strdup(msginfo->msgid);
2506 if (!compose->references) {
2507 if (msginfo->msgid && *msginfo->msgid) {
2508 if (msginfo->inreplyto && *msginfo->inreplyto)
2509 compose->references =
2510 g_strdup_printf("<%s>\n\t<%s>",
2514 compose->references =
2515 g_strconcat("<", msginfo->msgid, ">",
2517 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2518 compose->references =
2519 g_strconcat("<", msginfo->inreplyto, ">",
2527 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2529 GSList *ref_id_list, *cur;
2533 ref_id_list = references_list_append(NULL, ref);
2534 if (!ref_id_list) return NULL;
2535 if (msgid && *msgid)
2536 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2541 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2542 /* "<" + Message-ID + ">" + CR+LF+TAB */
2543 len += strlen((gchar *)cur->data) + 5;
2545 if (len > MAX_REFERENCES_LEN) {
2546 /* remove second message-ID */
2547 if (ref_id_list && ref_id_list->next &&
2548 ref_id_list->next->next) {
2549 g_free(ref_id_list->next->data);
2550 ref_id_list = g_slist_remove
2551 (ref_id_list, ref_id_list->next->data);
2553 slist_free_strings(ref_id_list);
2554 g_slist_free(ref_id_list);
2561 new_ref = g_string_new("");
2562 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2563 if (new_ref->len > 0)
2564 g_string_append(new_ref, "\n\t");
2565 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2568 slist_free_strings(ref_id_list);
2569 g_slist_free(ref_id_list);
2571 new_ref_str = new_ref->str;
2572 g_string_free(new_ref, FALSE);
2577 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2578 const gchar *fmt, const gchar *qmark,
2579 const gchar *body, gboolean rewrap,
2580 gboolean need_unescape,
2581 const gchar *err_msg)
2583 MsgInfo* dummyinfo = NULL;
2584 gchar *quote_str = NULL;
2586 gboolean prev_autowrap;
2587 const gchar *trimmed_body = body;
2588 gint cursor_pos = -1;
2589 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2590 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2595 SIGNAL_BLOCK(buffer);
2598 dummyinfo = compose_msginfo_new_from_compose(compose);
2599 msginfo = dummyinfo;
2602 if (qmark != NULL) {
2604 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
2605 compose->gtkaspell);
2607 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
2609 quote_fmt_scan_string(qmark);
2612 buf = quote_fmt_get_buffer();
2614 alertpanel_error(_("Quote mark format error."));
2616 Xstrdup_a(quote_str, buf, goto error)
2619 if (fmt && *fmt != '\0') {
2622 while (*trimmed_body == '\n')
2626 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account,
2627 compose->gtkaspell);
2629 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account);
2631 if (need_unescape) {
2634 /* decode \-escape sequences in the internal representation of the quote format */
2635 tmp = malloc(strlen(fmt)+1);
2636 pref_get_unescaped_pref(tmp, fmt);
2637 quote_fmt_scan_string(tmp);
2641 quote_fmt_scan_string(fmt);
2645 buf = quote_fmt_get_buffer();
2647 gint line = quote_fmt_get_line();
2648 gchar *msg = g_strdup_printf(err_msg, line);
2649 alertpanel_error(msg);
2656 prev_autowrap = compose->autowrap;
2657 compose->autowrap = FALSE;
2659 mark = gtk_text_buffer_get_insert(buffer);
2660 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2661 if (g_utf8_validate(buf, -1, NULL)) {
2662 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2664 gchar *tmpout = NULL;
2665 tmpout = conv_codeset_strdup
2666 (buf, conv_get_locale_charset_str_no_utf8(),
2668 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2670 tmpout = g_malloc(strlen(buf)*2+1);
2671 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2673 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2677 cursor_pos = quote_fmt_get_cursor_pos();
2678 compose->set_cursor_pos = cursor_pos;
2679 if (cursor_pos == -1) {
2682 gtk_text_buffer_get_start_iter(buffer, &iter);
2683 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2684 gtk_text_buffer_place_cursor(buffer, &iter);
2686 compose->autowrap = prev_autowrap;
2687 if (compose->autowrap && rewrap)
2688 compose_wrap_all(compose);
2695 SIGNAL_UNBLOCK(buffer);
2697 procmsg_msginfo_free( dummyinfo );
2702 /* if ml_post is of type addr@host and from is of type
2703 * addr-anything@host, return TRUE
2705 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2707 gchar *left_ml = NULL;
2708 gchar *right_ml = NULL;
2709 gchar *left_from = NULL;
2710 gchar *right_from = NULL;
2711 gboolean result = FALSE;
2713 if (!ml_post || !from)
2716 left_ml = g_strdup(ml_post);
2717 if (strstr(left_ml, "@")) {
2718 right_ml = strstr(left_ml, "@")+1;
2719 *(strstr(left_ml, "@")) = '\0';
2722 left_from = g_strdup(from);
2723 if (strstr(left_from, "@")) {
2724 right_from = strstr(left_from, "@")+1;
2725 *(strstr(left_from, "@")) = '\0';
2728 if (left_ml && left_from && right_ml && right_from
2729 && !strncmp(left_from, left_ml, strlen(left_ml))
2730 && !strcmp(right_from, right_ml)) {
2739 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2741 gchar *my_addr1, *my_addr2;
2743 if (!addr1 || !addr2)
2746 Xstrdup_a(my_addr1, addr1, return FALSE);
2747 Xstrdup_a(my_addr2, addr2, return FALSE);
2749 extract_address(my_addr1);
2750 extract_address(my_addr2);
2752 return !strcasecmp(my_addr1, my_addr2);
2755 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2756 gboolean to_all, gboolean to_ml,
2758 gboolean followup_and_reply_to)
2760 GSList *cc_list = NULL;
2763 gchar *replyto = NULL;
2764 GHashTable *to_table;
2766 gboolean reply_to_ml = FALSE;
2767 gboolean default_reply_to = FALSE;
2769 g_return_if_fail(compose->account != NULL);
2770 g_return_if_fail(msginfo != NULL);
2772 reply_to_ml = to_ml && compose->ml_post;
2774 default_reply_to = msginfo->folder &&
2775 msginfo->folder->prefs->enable_default_reply_to;
2777 if (compose->account->protocol != A_NNTP) {
2778 if (reply_to_ml && !default_reply_to) {
2780 gboolean is_subscr = is_subscription(compose->ml_post,
2783 /* normal answer to ml post with a reply-to */
2784 compose_entry_append(compose,
2787 if (compose->replyto
2788 && !same_address(compose->ml_post, compose->replyto))
2789 compose_entry_append(compose,
2793 /* answer to subscription confirmation */
2794 if (compose->replyto)
2795 compose_entry_append(compose,
2798 else if (msginfo->from)
2799 compose_entry_append(compose,
2804 else if (!(to_all || to_sender) && default_reply_to) {
2805 compose_entry_append(compose,
2806 msginfo->folder->prefs->default_reply_to,
2808 compose_entry_mark_default_to(compose,
2809 msginfo->folder->prefs->default_reply_to);
2814 Xstrdup_a(tmp1, msginfo->from, return);
2815 extract_address(tmp1);
2816 if (to_all || to_sender ||
2817 !account_find_from_address(tmp1))
2818 compose_entry_append(compose,
2819 (compose->replyto && !to_sender)
2820 ? compose->replyto :
2821 msginfo->from ? msginfo->from : "",
2823 else if (!to_all && !to_sender) {
2824 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2825 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
2826 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2827 compose_entry_append(compose,
2828 msginfo->from ? msginfo->from : "",
2831 /* replying to own mail, use original recp */
2832 compose_entry_append(compose,
2833 msginfo->to ? msginfo->to : "",
2835 compose_entry_append(compose,
2836 msginfo->cc ? msginfo->cc : "",
2842 if (to_sender || (compose->followup_to &&
2843 !strncmp(compose->followup_to, "poster", 6)))
2844 compose_entry_append
2846 (compose->replyto ? compose->replyto :
2847 msginfo->from ? msginfo->from : ""),
2850 else if (followup_and_reply_to || to_all) {
2851 compose_entry_append
2853 (compose->replyto ? compose->replyto :
2854 msginfo->from ? msginfo->from : ""),
2857 compose_entry_append
2859 compose->followup_to ? compose->followup_to :
2860 compose->newsgroups ? compose->newsgroups : "",
2861 COMPOSE_NEWSGROUPS);
2864 compose_entry_append
2866 compose->followup_to ? compose->followup_to :
2867 compose->newsgroups ? compose->newsgroups : "",
2868 COMPOSE_NEWSGROUPS);
2871 if (msginfo->subject && *msginfo->subject) {
2875 buf = p = g_strdup(msginfo->subject);
2876 p += subject_get_prefix_length(p);
2877 memmove(buf, p, strlen(p) + 1);
2879 buf2 = g_strdup_printf("Re: %s", buf);
2880 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2885 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
2887 if (to_ml && compose->ml_post) return;
2888 if (!to_all || compose->account->protocol == A_NNTP) return;
2890 if (compose->replyto) {
2891 Xstrdup_a(replyto, compose->replyto, return);
2892 extract_address(replyto);
2894 if (msginfo->from) {
2895 Xstrdup_a(from, msginfo->from, return);
2896 extract_address(from);
2899 if (replyto && from)
2900 cc_list = address_list_append_with_comments(cc_list, from);
2901 if (to_all && msginfo->folder &&
2902 msginfo->folder->prefs->enable_default_reply_to)
2903 cc_list = address_list_append_with_comments(cc_list,
2904 msginfo->folder->prefs->default_reply_to);
2905 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
2906 cc_list = address_list_append_with_comments(cc_list, compose->cc);
2908 to_table = g_hash_table_new(g_str_hash, g_str_equal);
2910 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
2911 if (compose->account) {
2912 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
2913 GINT_TO_POINTER(1));
2915 /* remove address on To: and that of current account */
2916 for (cur = cc_list; cur != NULL; ) {
2917 GSList *next = cur->next;
2920 addr = g_utf8_strdown(cur->data, -1);
2921 extract_address(addr);
2923 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
2924 cc_list = g_slist_remove(cc_list, cur->data);
2926 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
2930 hash_free_strings(to_table);
2931 g_hash_table_destroy(to_table);
2934 for (cur = cc_list; cur != NULL; cur = cur->next)
2935 compose_entry_append(compose, (gchar *)cur->data,
2937 slist_free_strings(cc_list);
2938 g_slist_free(cc_list);
2943 #define SET_ENTRY(entry, str) \
2946 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
2949 #define SET_ADDRESS(type, str) \
2952 compose_entry_append(compose, str, type); \
2955 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
2957 g_return_if_fail(msginfo != NULL);
2959 SET_ENTRY(subject_entry, msginfo->subject);
2960 SET_ENTRY(from_name, msginfo->from);
2961 SET_ADDRESS(COMPOSE_TO, msginfo->to);
2962 SET_ADDRESS(COMPOSE_CC, compose->cc);
2963 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
2964 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
2965 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
2966 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
2968 compose_update_priority_menu_item(compose);
2969 compose_update_privacy_system_menu_item(compose, FALSE);
2970 compose_show_first_last_header(compose, TRUE);
2976 static void compose_insert_sig(Compose *compose, gboolean replace)
2978 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2979 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2981 GtkTextIter iter, iter_end;
2983 gchar *search = NULL;
2984 gboolean prev_autowrap;
2985 gboolean found = FALSE, shift = FALSE;
2988 g_return_if_fail(compose->account != NULL);
2990 prev_autowrap = compose->autowrap;
2991 compose->autowrap = FALSE;
2993 g_signal_handlers_block_by_func(G_OBJECT(buffer),
2994 G_CALLBACK(compose_changed_cb),
2997 mark = gtk_text_buffer_get_insert(buffer);
2998 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2999 cur_pos = gtk_text_iter_get_offset (&iter);
3001 gtk_text_buffer_get_end_iter(buffer, &iter);
3003 search = compose->sig_str;
3005 if (replace && search) {
3006 GtkTextIter first_iter, start_iter, end_iter;
3008 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3010 if (compose->sig_str[0] == '\0')
3013 found = gtk_text_iter_forward_search(&first_iter,
3015 GTK_TEXT_SEARCH_TEXT_ONLY,
3016 &start_iter, &end_iter,
3020 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3024 if (replace && !found && search && strlen(search) > 2
3025 && search[0] == '\n' && search[1] == '\n') {
3031 g_free(compose->sig_str);
3032 compose->sig_str = compose_get_signature_str(compose);
3033 if (!compose->sig_str || (replace && !compose->account->auto_sig))
3034 compose->sig_str = g_strdup("");
3036 cur_pos = gtk_text_iter_get_offset(&iter);
3038 gtk_text_buffer_insert(buffer, &iter, compose->sig_str + 1, -1);
3040 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3042 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3043 gtk_text_iter_forward_char(&iter);
3044 gtk_text_iter_forward_char(&iter);
3045 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3046 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3048 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3049 cur_pos = gtk_text_buffer_get_char_count (buffer);
3051 /* put the cursor where it should be
3052 * either where the quote_fmt says, either before the signature */
3053 if (compose->set_cursor_pos < 0)
3054 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3056 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3057 compose->set_cursor_pos);
3059 gtk_text_buffer_place_cursor(buffer, &iter);
3060 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3061 G_CALLBACK(compose_changed_cb),
3064 compose->autowrap = prev_autowrap;
3065 if (compose->autowrap)
3066 compose_wrap_all(compose);
3069 static gchar *compose_get_signature_str(Compose *compose)
3071 gchar *sig_body = NULL;
3072 gchar *sig_str = NULL;
3073 gchar *utf8_sig_str = NULL;
3075 g_return_val_if_fail(compose->account != NULL, NULL);
3077 if (!compose->account->sig_path)
3080 if (compose->account->sig_type == SIG_FILE) {
3081 if (!is_file_or_fifo_exist(compose->account->sig_path)) {
3082 g_warning("can't open signature file: %s\n",
3083 compose->account->sig_path);
3088 if (compose->account->sig_type == SIG_COMMAND)
3089 sig_body = get_command_output(compose->account->sig_path);
3093 tmp = file_read_to_str(compose->account->sig_path);
3096 sig_body = normalize_newlines(tmp);
3100 if (compose->account->sig_sep) {
3101 sig_str = g_strconcat("\n\n", compose->account->sig_sep, "\n", sig_body,
3105 sig_str = g_strconcat("\n\n", sig_body, NULL);
3108 if (g_utf8_validate(sig_str, -1, NULL) == TRUE)
3109 utf8_sig_str = sig_str;
3111 utf8_sig_str = conv_codeset_strdup
3112 (sig_str, conv_get_locale_charset_str_no_utf8(),
3118 return utf8_sig_str;
3121 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3124 GtkTextBuffer *buffer;
3127 const gchar *cur_encoding;
3128 gchar buf[BUFFSIZE];
3131 gboolean prev_autowrap;
3132 gboolean badtxt = FALSE;
3134 g_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3136 if ((fp = g_fopen(file, "rb")) == NULL) {
3137 FILE_OP_ERROR(file, "fopen");
3138 return COMPOSE_INSERT_READ_ERROR;
3141 prev_autowrap = compose->autowrap;
3142 compose->autowrap = FALSE;
3144 text = GTK_TEXT_VIEW(compose->text);
3145 buffer = gtk_text_view_get_buffer(text);
3146 mark = gtk_text_buffer_get_insert(buffer);
3147 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3149 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3150 G_CALLBACK(text_inserted),
3153 cur_encoding = conv_get_locale_charset_str_no_utf8();
3155 while (fgets(buf, sizeof(buf), fp) != NULL) {
3158 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3159 str = g_strdup(buf);
3161 str = conv_codeset_strdup
3162 (buf, cur_encoding, CS_INTERNAL);
3165 /* strip <CR> if DOS/Windows file,
3166 replace <CR> with <LF> if Macintosh file. */
3169 if (len > 0 && str[len - 1] != '\n') {
3171 if (str[len] == '\r') str[len] = '\n';
3174 gtk_text_buffer_insert(buffer, &iter, str, -1);
3178 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3179 G_CALLBACK(text_inserted),
3181 compose->autowrap = prev_autowrap;
3182 if (compose->autowrap)
3183 compose_wrap_all(compose);
3188 return COMPOSE_INSERT_INVALID_CHARACTER;
3190 return COMPOSE_INSERT_SUCCESS;
3193 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3194 const gchar *filename,
3195 const gchar *content_type)