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 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
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"
138 #define N_ATTACH_COLS (N_COL_COLUMNS)
142 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
143 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
144 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
145 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
146 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
147 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
148 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
149 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
150 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
151 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
152 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
153 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
154 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
155 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE_N,
156 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
157 } ComposeCallAdvancedAction;
161 PRIORITY_HIGHEST = 1,
170 COMPOSE_INSERT_SUCCESS,
171 COMPOSE_INSERT_READ_ERROR,
172 COMPOSE_INSERT_INVALID_CHARACTER,
173 COMPOSE_INSERT_NO_FILE
174 } ComposeInsertResult;
178 COMPOSE_WRITE_FOR_SEND,
179 COMPOSE_WRITE_FOR_STORE
184 COMPOSE_QUOTE_FORCED,
189 #define B64_LINE_SIZE 57
190 #define B64_BUFFSIZE 77
192 #define MAX_REFERENCES_LEN 999
194 static GList *compose_list = NULL;
196 static Compose *compose_generic_new (PrefsAccount *account,
199 GPtrArray *attach_files,
200 GList *listAddress );
202 static Compose *compose_create (PrefsAccount *account,
207 static void compose_entry_mark_default_to (Compose *compose,
208 const gchar *address);
209 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
210 ComposeQuoteMode quote_mode,
214 static Compose *compose_forward_multiple (PrefsAccount *account,
215 GSList *msginfo_list);
216 static Compose *compose_reply (MsgInfo *msginfo,
217 ComposeQuoteMode quote_mode,
222 static Compose *compose_reply_mode (ComposeMode mode,
223 GSList *msginfo_list,
225 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
226 static void compose_update_privacy_systems_menu(Compose *compose);
228 static GtkWidget *compose_account_option_menu_create
230 static void compose_set_out_encoding (Compose *compose);
231 static void compose_set_template_menu (Compose *compose);
232 static void compose_template_apply (Compose *compose,
235 static void compose_destroy (Compose *compose);
237 static void compose_entries_set (Compose *compose,
238 const gchar *mailto);
239 static gint compose_parse_header (Compose *compose,
241 static gchar *compose_parse_references (const gchar *ref,
244 static gchar *compose_quote_fmt (Compose *compose,
250 gboolean need_unescape,
251 const gchar *err_msg);
253 static void compose_reply_set_entry (Compose *compose,
259 followup_and_reply_to);
260 static void compose_reedit_set_entry (Compose *compose,
263 static void compose_insert_sig (Compose *compose,
265 static gchar *compose_get_signature_str (Compose *compose);
266 static ComposeInsertResult compose_insert_file (Compose *compose,
269 static gboolean compose_attach_append (Compose *compose,
272 const gchar *content_type);
273 static void compose_attach_parts (Compose *compose,
276 static gboolean compose_beautify_paragraph (Compose *compose,
277 GtkTextIter *par_iter,
279 static void compose_wrap_all (Compose *compose);
280 static void compose_wrap_all_full (Compose *compose,
283 static void compose_set_title (Compose *compose);
284 static void compose_select_account (Compose *compose,
285 PrefsAccount *account,
288 static PrefsAccount *compose_current_mail_account(void);
289 /* static gint compose_send (Compose *compose); */
290 static gboolean compose_check_for_valid_recipient
292 static gboolean compose_check_entries (Compose *compose,
293 gboolean check_everything);
294 static gint compose_write_to_file (Compose *compose,
297 gboolean attach_parts);
298 static gint compose_write_body_to_file (Compose *compose,
300 static gint compose_remove_reedit_target (Compose *compose,
302 static void compose_remove_draft (Compose *compose);
303 static gint compose_queue_sub (Compose *compose,
307 gboolean check_subject,
308 gboolean remove_reedit_target);
309 static void compose_add_attachments (Compose *compose,
311 static gchar *compose_get_header (Compose *compose);
313 static void compose_convert_header (Compose *compose,
318 gboolean addr_field);
320 static void compose_attach_info_free (AttachInfo *ainfo);
321 static void compose_attach_remove_selected (Compose *compose);
323 static void compose_attach_property (Compose *compose);
324 static void compose_attach_property_create (gboolean *cancelled);
325 static void attach_property_ok (GtkWidget *widget,
326 gboolean *cancelled);
327 static void attach_property_cancel (GtkWidget *widget,
328 gboolean *cancelled);
329 static gint attach_property_delete_event (GtkWidget *widget,
331 gboolean *cancelled);
332 static gboolean attach_property_key_pressed (GtkWidget *widget,
334 gboolean *cancelled);
336 static void compose_exec_ext_editor (Compose *compose);
338 static gint compose_exec_ext_editor_real (const gchar *file);
339 static gboolean compose_ext_editor_kill (Compose *compose);
340 static gboolean compose_input_cb (GIOChannel *source,
341 GIOCondition condition,
343 static void compose_set_ext_editor_sensitive (Compose *compose,
345 #endif /* G_OS_UNIX */
347 static void compose_undo_state_changed (UndoMain *undostruct,
352 static void compose_create_header_entry (Compose *compose);
353 static void compose_add_header_entry (Compose *compose, const gchar *header, gchar *text);
354 static void compose_remove_header_entries(Compose *compose);
356 static void compose_update_priority_menu_item(Compose * compose);
358 static void compose_spell_menu_changed (void *data);
360 static void compose_add_field_list ( Compose *compose,
361 GList *listAddress );
363 /* callback functions */
365 static gboolean compose_edit_size_alloc (GtkEditable *widget,
366 GtkAllocation *allocation,
367 GtkSHRuler *shruler);
368 static void account_activated (GtkComboBox *optmenu,
370 static void attach_selected (GtkTreeView *tree_view,
371 GtkTreePath *tree_path,
372 GtkTreeViewColumn *column,
374 static gboolean attach_button_pressed (GtkWidget *widget,
375 GdkEventButton *event,
377 static gboolean attach_key_pressed (GtkWidget *widget,
380 static void compose_send_cb (gpointer data,
383 static void compose_send_later_cb (gpointer data,
387 static void compose_draft_cb (gpointer data,
391 static void compose_attach_cb (gpointer data,
394 static void compose_insert_file_cb (gpointer data,
397 static void compose_insert_sig_cb (gpointer data,
401 static void compose_close_cb (gpointer data,
405 static void compose_set_encoding_cb (gpointer data,
409 static void compose_address_cb (gpointer data,
412 static void compose_template_activate_cb(GtkWidget *widget,
415 static void compose_ext_editor_cb (gpointer data,
419 static gint compose_delete_cb (GtkWidget *widget,
423 static void compose_undo_cb (Compose *compose);
424 static void compose_redo_cb (Compose *compose);
425 static void compose_cut_cb (Compose *compose);
426 static void compose_copy_cb (Compose *compose);
427 static void compose_paste_cb (Compose *compose);
428 static void compose_paste_as_quote_cb (Compose *compose);
429 static void compose_paste_no_wrap_cb (Compose *compose);
430 static void compose_paste_wrap_cb (Compose *compose);
431 static void compose_allsel_cb (Compose *compose);
433 static void compose_advanced_action_cb (Compose *compose,
434 ComposeCallAdvancedAction action);
436 static void compose_grab_focus_cb (GtkWidget *widget,
439 static void compose_changed_cb (GtkTextBuffer *textbuf,
442 static void compose_wrap_cb (gpointer data,
445 static void compose_find_cb (gpointer data,
448 static void compose_toggle_autowrap_cb (gpointer data,
452 static void compose_toggle_ruler_cb (gpointer data,
455 static void compose_toggle_sign_cb (gpointer data,
458 static void compose_toggle_encrypt_cb (gpointer data,
461 static void compose_set_privacy_system_cb(GtkWidget *widget,
463 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
464 static void activate_privacy_system (Compose *compose,
465 PrefsAccount *account,
467 static void compose_use_signing(Compose *compose, gboolean use_signing);
468 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
469 static void compose_toggle_return_receipt_cb(gpointer data, guint action,
471 static void compose_toggle_remove_refs_cb(gpointer data, guint action,
473 static void compose_set_priority_cb (gpointer data,
476 static void compose_reply_change_mode (gpointer data,
480 static void compose_attach_drag_received_cb (GtkWidget *widget,
481 GdkDragContext *drag_context,
484 GtkSelectionData *data,
488 static void compose_insert_drag_received_cb (GtkWidget *widget,
489 GdkDragContext *drag_context,
492 GtkSelectionData *data,
496 static void compose_header_drag_received_cb (GtkWidget *widget,
497 GdkDragContext *drag_context,
500 GtkSelectionData *data,
505 static gboolean compose_drag_drop (GtkWidget *widget,
506 GdkDragContext *drag_context,
508 guint time, gpointer user_data);
510 static void text_inserted (GtkTextBuffer *buffer,
515 static Compose *compose_generic_reply(MsgInfo *msginfo,
516 ComposeQuoteMode quote_mode,
520 gboolean followup_and_reply_to,
523 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
524 ComposeHeaderEntry *headerentry);
525 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
527 ComposeHeaderEntry *headerentry);
529 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
531 static void compose_allow_user_actions (Compose *compose, gboolean allow);
534 static void compose_check_all (Compose *compose);
535 static void compose_highlight_all (Compose *compose);
536 static void compose_check_backwards (Compose *compose);
537 static void compose_check_forwards_go (Compose *compose);
540 static gint compose_defer_auto_save_draft (Compose *compose);
541 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
543 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
546 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
547 FolderItem *folder_item);
549 static void compose_attach_update_label(Compose *compose);
551 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data);
553 static GtkItemFactoryEntry compose_popup_entries[] =
555 {N_("/_Add..."), NULL, compose_attach_cb, 0, NULL},
556 {N_("/_Remove"), NULL, compose_attach_remove_selected, 0, NULL},
557 {"/---", NULL, NULL, 0, "<Separator>"},
558 {N_("/_Properties..."), NULL, compose_attach_property, 0, NULL}
561 static GtkItemFactoryEntry compose_entries[] =
563 {N_("/_Message"), NULL, NULL, 0, "<Branch>"},
564 {N_("/_Message/S_end"), "<control>Return",
565 compose_send_cb, 0, NULL},
566 {N_("/_Message/Send _later"), "<shift><control>S",
567 compose_send_later_cb, 0, NULL},
568 {N_("/_Message/---"), NULL, NULL, 0, "<Separator>"},
569 {N_("/_Message/_Attach file"), "<control>M", compose_attach_cb, 0, NULL},
570 {N_("/_Message/_Insert file"), "<control>I", compose_insert_file_cb, 0, NULL},
571 {N_("/_Message/Insert si_gnature"), "<control>G", compose_insert_sig_cb, 0, NULL},
572 {N_("/_Message/---"), NULL, NULL, 0, "<Separator>"},
573 {N_("/_Message/_Save"),
574 "<control>S", compose_draft_cb, COMPOSE_KEEP_EDITING, NULL},
575 {N_("/_Message/---"), NULL, NULL, 0, "<Separator>"},
576 {N_("/_Message/_Close"), "<control>W", compose_close_cb, 0, NULL},
578 {N_("/_Edit"), NULL, NULL, 0, "<Branch>"},
579 {N_("/_Edit/_Undo"), "<control>Z", compose_undo_cb, 0, NULL},
580 {N_("/_Edit/_Redo"), "<control>Y", compose_redo_cb, 0, NULL},
581 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
582 {N_("/_Edit/Cu_t"), "<control>X", compose_cut_cb, 0, NULL},
583 {N_("/_Edit/_Copy"), "<control>C", compose_copy_cb, 0, NULL},
584 {N_("/_Edit/_Paste"), "<control>V", compose_paste_cb, 0, NULL},
585 {N_("/_Edit/Special paste"), NULL, NULL, 0, "<Branch>"},
586 {N_("/_Edit/Special paste/as _quotation"),
587 NULL, compose_paste_as_quote_cb, 0, NULL},
588 {N_("/_Edit/Special paste/_wrapped"),
589 NULL, compose_paste_wrap_cb, 0, NULL},
590 {N_("/_Edit/Special paste/_unwrapped"),
591 NULL, compose_paste_no_wrap_cb, 0, NULL},
592 {N_("/_Edit/Select _all"), "<control>A", compose_allsel_cb, 0, NULL},
593 {N_("/_Edit/A_dvanced"), NULL, NULL, 0, "<Branch>"},
594 {N_("/_Edit/A_dvanced/Move a character backward"),
596 compose_advanced_action_cb,
597 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
599 {N_("/_Edit/A_dvanced/Move a character forward"),
601 compose_advanced_action_cb,
602 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
604 {N_("/_Edit/A_dvanced/Move a word backward"),
606 compose_advanced_action_cb,
607 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
609 {N_("/_Edit/A_dvanced/Move a word forward"),
611 compose_advanced_action_cb,
612 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
614 {N_("/_Edit/A_dvanced/Move to beginning of line"),
615 NULL, /* "<control>A" */
616 compose_advanced_action_cb,
617 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
619 {N_("/_Edit/A_dvanced/Move to end of line"),
621 compose_advanced_action_cb,
622 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
624 {N_("/_Edit/A_dvanced/Move to previous line"),
626 compose_advanced_action_cb,
627 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
629 {N_("/_Edit/A_dvanced/Move to next line"),
631 compose_advanced_action_cb,
632 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
634 {N_("/_Edit/A_dvanced/Delete a character backward"),
636 compose_advanced_action_cb,
637 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
639 {N_("/_Edit/A_dvanced/Delete a character forward"),
641 compose_advanced_action_cb,
642 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
644 {N_("/_Edit/A_dvanced/Delete a word backward"),
645 NULL, /* "<control>W" */
646 compose_advanced_action_cb,
647 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
649 {N_("/_Edit/A_dvanced/Delete a word forward"),
650 NULL, /* "<alt>D", */
651 compose_advanced_action_cb,
652 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
654 {N_("/_Edit/A_dvanced/Delete line"),
656 compose_advanced_action_cb,
657 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
659 {N_("/_Edit/A_dvanced/Delete entire line"),
661 compose_advanced_action_cb,
662 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE_N,
664 {N_("/_Edit/A_dvanced/Delete to end of line"),
666 compose_advanced_action_cb,
667 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END,
669 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
671 "<control>F", compose_find_cb, 0, NULL},
672 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
673 {N_("/_Edit/_Wrap current paragraph"),
674 "<control>L", compose_wrap_cb, 0, NULL},
675 {N_("/_Edit/Wrap all long _lines"),
676 "<control><alt>L", compose_wrap_cb, 1, NULL},
677 {N_("/_Edit/Aut_o wrapping"), "<shift><control>L", compose_toggle_autowrap_cb, 0, "<ToggleItem>"},
678 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
679 {N_("/_Edit/Edit with e_xternal editor"),
680 "<shift><control>X", compose_ext_editor_cb, 0, NULL},
682 {N_("/_Spelling"), NULL, NULL, 0, "<Branch>"},
683 {N_("/_Spelling/_Check all or check selection"),
684 NULL, compose_check_all, 0, NULL},
685 {N_("/_Spelling/_Highlight all misspelled words"),
686 NULL, compose_highlight_all, 0, NULL},
687 {N_("/_Spelling/Check _backwards misspelled word"),
688 NULL, compose_check_backwards , 0, NULL},
689 {N_("/_Spelling/_Forward to next misspelled word"),
690 NULL, compose_check_forwards_go, 0, NULL},
691 {N_("/_Spelling/---"), NULL, NULL, 0, "<Separator>"},
692 {N_("/_Spelling/Options"),
693 NULL, NULL, 0, "<Branch>"},
695 {N_("/_Options"), NULL, NULL, 0, "<Branch>"},
696 {N_("/_Options/Reply _mode"), NULL, NULL, 0, "<Branch>"},
697 {N_("/_Options/Reply _mode/_Normal"), NULL, compose_reply_change_mode, COMPOSE_REPLY, "<RadioItem>"},
698 {N_("/_Options/Reply _mode/_All"), NULL, compose_reply_change_mode, COMPOSE_REPLY_TO_ALL, "/Options/Reply mode/Normal"},
699 {N_("/_Options/Reply _mode/_Sender"), NULL, compose_reply_change_mode, COMPOSE_REPLY_TO_SENDER, "/Options/Reply mode/Normal"},
700 {N_("/_Options/Reply _mode/_Mailing-list"), NULL, compose_reply_change_mode, COMPOSE_REPLY_TO_LIST, "/Options/Reply mode/Normal"},
701 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
702 {N_("/_Options/Privacy _System"), NULL, NULL, 0, "<Branch>"},
703 {N_("/_Options/Privacy _System/None"), NULL, NULL, 0, "<RadioItem>"},
704 {N_("/_Options/Si_gn"), NULL, compose_toggle_sign_cb , 0, "<ToggleItem>"},
705 {N_("/_Options/_Encrypt"), NULL, compose_toggle_encrypt_cb, 0, "<ToggleItem>"},
706 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
707 {N_("/_Options/_Priority"), NULL, NULL, 0, "<Branch>"},
708 {N_("/_Options/Priority/_Highest"), NULL, compose_set_priority_cb, PRIORITY_HIGHEST, "<RadioItem>"},
709 {N_("/_Options/Priority/Hi_gh"), NULL, compose_set_priority_cb, PRIORITY_HIGH, "/Options/Priority/Highest"},
710 {N_("/_Options/Priority/_Normal"), NULL, compose_set_priority_cb, PRIORITY_NORMAL, "/Options/Priority/Highest"},
711 {N_("/_Options/Priority/Lo_w"), NULL, compose_set_priority_cb, PRIORITY_LOW, "/Options/Priority/Highest"},
712 {N_("/_Options/Priority/_Lowest"), NULL, compose_set_priority_cb, PRIORITY_LOWEST, "/Options/Priority/Highest"},
713 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
714 {N_("/_Options/_Request Return Receipt"), NULL, compose_toggle_return_receipt_cb, 0, "<ToggleItem>"},
715 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
716 {N_("/_Options/Remo_ve references"), NULL, compose_toggle_remove_refs_cb, 0, "<ToggleItem>"},
717 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
719 #define ENC_ACTION(action) \
720 NULL, compose_set_encoding_cb, action, \
721 "/Options/Character encoding/Automatic"
723 {N_("/_Options/Character _encoding"), NULL, NULL, 0, "<Branch>"},
724 {N_("/_Options/Character _encoding/_Automatic"),
725 NULL, compose_set_encoding_cb, C_AUTO, "<RadioItem>"},
726 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
728 {N_("/_Options/Character _encoding/7bit ASCII (US-ASC_II)"),
729 ENC_ACTION(C_US_ASCII)},
730 {N_("/_Options/Character _encoding/Unicode (_UTF-8)"),
731 ENC_ACTION(C_UTF_8)},
732 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
734 {N_("/_Options/Character _encoding/Western European"), NULL, NULL, 0, "<Branch>"},
735 {N_("/_Options/Character _encoding/Western European/ISO-8859-_1"),
736 ENC_ACTION(C_ISO_8859_1)},
737 {N_("/_Options/Character _encoding/Western European/ISO-8859-15"),
738 ENC_ACTION(C_ISO_8859_15)},
739 {N_("/_Options/Character _encoding/Western European/Windows-1252"),
740 ENC_ACTION(C_WINDOWS_1252)},
742 {N_("/_Options/Character _encoding/Central European (ISO-8859-_2)"),
743 ENC_ACTION(C_ISO_8859_2)},
745 {N_("/_Options/Character _encoding/Baltic"), NULL, NULL, 0, "<Branch>"},
746 {N_("/_Options/Character _encoding/Baltic/ISO-8859-13"),
747 ENC_ACTION(C_ISO_8859_13)},
748 {N_("/_Options/Character _encoding/Baltic/ISO-8859-_4"),
749 ENC_ACTION(C_ISO_8859_4)},
751 {N_("/_Options/Character _encoding/Greek (ISO-8859-_7)"),
752 ENC_ACTION(C_ISO_8859_7)},
754 {N_("/_Options/Character _encoding/Hebrew"), NULL, NULL, 0, "<Branch>"},
755 {N_("/_Options/Character _encoding/Hebrew/ISO-8859-_8"),
756 ENC_ACTION(C_ISO_8859_8)},
757 {N_("/_Options/Character _encoding/Hebrew/Windows-1255"),
758 ENC_ACTION(C_WINDOWS_1255)},
760 {N_("/_Options/Character _encoding/Arabic"), NULL, NULL, 0, "<Branch>"},
761 {N_("/_Options/Character _encoding/Arabic/ISO-8859-_6"),
762 ENC_ACTION(C_ISO_8859_6)},
763 {N_("/_Options/Character _encoding/Arabic/Windows-1256"),
764 ENC_ACTION(C_CP1256)},
766 {N_("/_Options/Character _encoding/Turkish (ISO-8859-_9)"),
767 ENC_ACTION(C_ISO_8859_9)},
769 {N_("/_Options/Character _encoding/Cyrillic"), NULL, NULL, 0, "<Branch>"},
770 {N_("/_Options/Character _encoding/Cyrillic/ISO-8859-_5"),
771 ENC_ACTION(C_ISO_8859_5)},
772 {N_("/_Options/Character _encoding/Cyrillic/KOI8-_R"),
773 ENC_ACTION(C_KOI8_R)},
774 {N_("/_Options/Character _encoding/Cyrillic/KOI8-U"),
775 ENC_ACTION(C_KOI8_U)},
776 {N_("/_Options/Character _encoding/Cyrillic/Windows-1251"),
777 ENC_ACTION(C_WINDOWS_1251)},
779 {N_("/_Options/Character _encoding/Japanese"), NULL, NULL, 0, "<Branch>"},
780 {N_("/_Options/Character _encoding/Japanese/ISO-2022-_JP"),
781 ENC_ACTION(C_ISO_2022_JP)},
782 {N_("/_Options/Character _encoding/Japanese/ISO-2022-JP-2"),
783 ENC_ACTION(C_ISO_2022_JP_2)},
784 {N_("/_Options/Character _encoding/Japanese/_EUC-JP"),
785 ENC_ACTION(C_EUC_JP)},
786 {N_("/_Options/Character _encoding/Japanese/_Shift__JIS"),
787 ENC_ACTION(C_SHIFT_JIS)},
789 {N_("/_Options/Character _encoding/Chinese"), NULL, NULL, 0, "<Branch>"},
790 {N_("/_Options/Character _encoding/Chinese/Simplified (_GB2312)"),
791 ENC_ACTION(C_GB2312)},
792 {N_("/_Options/Character _encoding/Chinese/Simplified (GBK)"),
794 {N_("/_Options/Character _encoding/Chinese/Traditional (_Big5)"),
796 {N_("/_Options/Character _encoding/Chinese/Traditional (EUC-_TW)"),
797 ENC_ACTION(C_EUC_TW)},
799 {N_("/_Options/Character _encoding/Korean"), NULL, NULL, 0, "<Branch>"},
800 {N_("/_Options/Character _encoding/Korean/EUC-_KR"),
801 ENC_ACTION(C_EUC_KR)},
802 {N_("/_Options/Character _encoding/Korean/ISO-2022-KR"),
803 ENC_ACTION(C_ISO_2022_KR)},
805 {N_("/_Options/Character _encoding/Thai"), NULL, NULL, 0, "<Branch>"},
806 {N_("/_Options/Character _encoding/Thai/TIS-620"),
807 ENC_ACTION(C_TIS_620)},
808 {N_("/_Options/Character _encoding/Thai/Windows-874"),
809 ENC_ACTION(C_WINDOWS_874)},
811 {N_("/_Tools"), NULL, NULL, 0, "<Branch>"},
812 {N_("/_Tools/Show _ruler"), NULL, compose_toggle_ruler_cb, 0, "<ToggleItem>"},
813 {N_("/_Tools/_Address book"), "<shift><control>A", compose_address_cb , 0, NULL},
814 {N_("/_Tools/_Template"), NULL, NULL, 0, "<Branch>"},
815 {N_("/_Tools/Actio_ns"), NULL, NULL, 0, "<Branch>"},
816 {N_("/_Help"), NULL, NULL, 0, "<Branch>"},
817 {N_("/_Help/_About"), NULL, about_show, 0, NULL}
820 static GtkTargetEntry compose_mime_types[] =
822 {"text/uri-list", 0, 0},
823 {"UTF8_STRING", 0, 0},
827 static gboolean compose_put_existing_to_front(MsgInfo *info)
829 GList *compose_list = compose_get_compose_list();
833 for (elem = compose_list; elem != NULL && elem->data != NULL;
835 Compose *c = (Compose*)elem->data;
837 if (!c->targetinfo || !c->targetinfo->msgid ||
841 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
842 gtkut_window_popup(c->window);
850 static GdkColor quote_color1 =
851 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
852 static GdkColor quote_color2 =
853 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
854 static GdkColor quote_color3 =
855 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
857 static GdkColor quote_bgcolor1 =
858 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
859 static GdkColor quote_bgcolor2 =
860 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
861 static GdkColor quote_bgcolor3 =
862 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
864 static GdkColor signature_color = {
871 static GdkColor uri_color = {
878 static void compose_create_tags(GtkTextView *text, Compose *compose)
880 GtkTextBuffer *buffer;
881 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
887 buffer = gtk_text_view_get_buffer(text);
889 if (prefs_common.enable_color) {
890 /* grab the quote colors, converting from an int to a GdkColor */
891 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
893 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
895 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
897 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
899 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
901 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
903 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
905 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
908 signature_color = quote_color1 = quote_color2 = quote_color3 =
909 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
912 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
913 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
914 "foreground-gdk", "e_color1,
915 "paragraph-background-gdk", "e_bgcolor1,
917 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
918 "foreground-gdk", "e_color2,
919 "paragraph-background-gdk", "e_bgcolor2,
921 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
922 "foreground-gdk", "e_color3,
923 "paragraph-background-gdk", "e_bgcolor3,
926 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
927 "foreground-gdk", "e_color1,
929 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
930 "foreground-gdk", "e_color2,
932 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
933 "foreground-gdk", "e_color3,
937 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
938 "foreground-gdk", &signature_color,
941 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
942 "foreground-gdk", &uri_color,
944 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
945 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
947 color[0] = quote_color1;
948 color[1] = quote_color2;
949 color[2] = quote_color3;
950 color[3] = quote_bgcolor1;
951 color[4] = quote_bgcolor2;
952 color[5] = quote_bgcolor3;
953 color[6] = signature_color;
954 color[7] = uri_color;
955 cmap = gdk_drawable_get_colormap(compose->window->window);
956 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
958 for (i = 0; i < 8; i++) {
959 if (success[i] == FALSE) {
962 g_warning("Compose: color allocation failed.\n");
963 style = gtk_widget_get_style(GTK_WIDGET(text));
964 quote_color1 = quote_color2 = quote_color3 =
965 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
966 signature_color = uri_color = black;
971 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
972 GPtrArray *attach_files)
974 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
977 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
979 return compose_generic_new(account, mailto, item, NULL, NULL);
982 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
984 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
987 #define SCROLL_TO_CURSOR(compose) { \
988 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
989 gtk_text_view_get_buffer( \
990 GTK_TEXT_VIEW(compose->text))); \
991 gtk_text_view_scroll_mark_onscreen( \
992 GTK_TEXT_VIEW(compose->text), \
996 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
997 GPtrArray *attach_files, GList *listAddress )
1000 GtkTextView *textview;
1001 GtkTextBuffer *textbuf;
1003 GtkItemFactory *ifactory;
1004 const gchar *subject_format = NULL;
1005 const gchar *body_format = NULL;
1007 if (item && item->prefs && item->prefs->enable_default_account)
1008 account = account_find_from_id(item->prefs->default_account);
1010 if (!account) account = cur_account;
1011 g_return_val_if_fail(account != NULL, NULL);
1013 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1015 ifactory = gtk_item_factory_from_widget(compose->menubar);
1017 compose->replyinfo = NULL;
1018 compose->fwdinfo = NULL;
1020 textview = GTK_TEXT_VIEW(compose->text);
1021 textbuf = gtk_text_view_get_buffer(textview);
1022 compose_create_tags(textview, compose);
1024 undo_block(compose->undostruct);
1026 compose_set_dictionaries_from_folder_prefs(compose, item);
1029 if (account->auto_sig)
1030 compose_insert_sig(compose, FALSE);
1031 gtk_text_buffer_get_start_iter(textbuf, &iter);
1032 gtk_text_buffer_place_cursor(textbuf, &iter);
1034 if (account->protocol != A_NNTP) {
1035 if (mailto && *mailto != '\0') {
1036 compose_entries_set(compose, mailto);
1038 } else if (item && item->prefs->enable_default_to) {
1039 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
1040 compose_entry_mark_default_to(compose, item->prefs->default_to);
1042 if (item && item->ret_rcpt) {
1043 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
1047 compose_entry_append(compose, mailto, COMPOSE_NEWSGROUPS);
1048 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1049 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1052 * CLAWS: just don't allow return receipt request, even if the user
1053 * may want to send an email. simple but foolproof.
1055 menu_set_sensitive(ifactory, "/Options/Request Return Receipt", FALSE);
1057 compose_add_field_list( compose, listAddress );
1059 if (item && item->prefs && item->prefs->compose_with_format) {
1060 subject_format = item->prefs->compose_subject_format;
1061 body_format = item->prefs->compose_body_format;
1062 } else if (account->compose_with_format) {
1063 subject_format = account->compose_subject_format;
1064 body_format = account->compose_body_format;
1065 } else if (prefs_common.compose_with_format) {
1066 subject_format = prefs_common.compose_subject_format;
1067 body_format = prefs_common.compose_body_format;
1070 if (subject_format || body_format) {
1071 MsgInfo* dummyinfo = NULL;
1074 && *subject_format != '\0' )
1076 gchar *subject = NULL;
1080 dummyinfo = compose_msginfo_new_from_compose(compose);
1082 /* decode \-escape sequences in the internal representation of the quote format */
1083 tmp = malloc(strlen(subject_format)+1);
1084 pref_get_unescaped_pref(tmp, subject_format);
1086 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1088 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account,
1089 compose->gtkaspell);
1091 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account);
1093 quote_fmt_scan_string(tmp);
1096 buf = quote_fmt_get_buffer();
1098 alertpanel_error(_("New message subject format error."));
1100 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1101 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1102 quote_fmt_reset_vartable();
1109 && *body_format != '\0' )
1112 GtkTextBuffer *buffer;
1113 GtkTextIter start, end;
1116 if ( dummyinfo == NULL )
1117 dummyinfo = compose_msginfo_new_from_compose(compose);
1119 text = GTK_TEXT_VIEW(compose->text);
1120 buffer = gtk_text_view_get_buffer(text);
1121 gtk_text_buffer_get_start_iter(buffer, &start);
1122 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1123 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1125 compose_quote_fmt(compose, dummyinfo,
1127 NULL, tmp, FALSE, TRUE,
1128 _("New message body format error at line %d."));
1129 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1130 quote_fmt_reset_vartable();
1135 procmsg_msginfo_free( dummyinfo );
1142 for (i = 0; i < attach_files->len; i++) {
1143 file = g_ptr_array_index(attach_files, i);
1144 compose_attach_append(compose, file, file, NULL);
1148 compose_show_first_last_header(compose, TRUE);
1150 /* Set save folder */
1151 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1152 gchar *folderidentifier;
1154 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1155 folderidentifier = folder_item_get_identifier(item);
1156 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1157 g_free(folderidentifier);
1160 gtk_widget_grab_focus(compose->header_last->entry);
1162 undo_unblock(compose->undostruct);
1164 if (prefs_common.auto_exteditor)
1165 compose_exec_ext_editor(compose);
1167 compose->draft_timeout_tag = -1;
1168 SCROLL_TO_CURSOR(compose);
1170 compose->modified = FALSE;
1171 compose_set_title(compose);
1175 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1176 gboolean override_pref)
1178 gchar *privacy = NULL;
1180 g_return_if_fail(compose != NULL);
1181 g_return_if_fail(account != NULL);
1183 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1186 if (account->default_privacy_system
1187 && strlen(account->default_privacy_system)) {
1188 privacy = account->default_privacy_system;
1190 GSList *privacy_avail = privacy_get_system_ids();
1191 if (privacy_avail && g_slist_length(privacy_avail)) {
1192 privacy = (gchar *)(privacy_avail->data);
1195 if (privacy != NULL) {
1196 if (compose->privacy_system == NULL)
1197 compose->privacy_system = g_strdup(privacy);
1198 compose_update_privacy_system_menu_item(compose, FALSE);
1199 compose_use_encryption(compose, TRUE);
1203 static void compose_force_signing(Compose *compose, PrefsAccount *account)
1205 gchar *privacy = NULL;
1207 if (account->default_privacy_system
1208 && strlen(account->default_privacy_system)) {
1209 privacy = account->default_privacy_system;
1211 GSList *privacy_avail = privacy_get_system_ids();
1212 if (privacy_avail && g_slist_length(privacy_avail)) {
1213 privacy = (gchar *)(privacy_avail->data);
1216 if (privacy != NULL) {
1217 if (compose->privacy_system == NULL)
1218 compose->privacy_system = g_strdup(privacy);
1219 compose_update_privacy_system_menu_item(compose, FALSE);
1220 compose_use_signing(compose, TRUE);
1224 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1228 Compose *compose = NULL;
1229 GtkItemFactory *ifactory = NULL;
1231 g_return_val_if_fail(msginfo_list != NULL, NULL);
1233 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1234 g_return_val_if_fail(msginfo != NULL, NULL);
1236 list_len = g_slist_length(msginfo_list);
1240 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1241 FALSE, prefs_common.default_reply_list, FALSE, body);
1243 case COMPOSE_REPLY_WITH_QUOTE:
1244 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1245 FALSE, prefs_common.default_reply_list, FALSE, body);
1247 case COMPOSE_REPLY_WITHOUT_QUOTE:
1248 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1249 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1251 case COMPOSE_REPLY_TO_SENDER:
1252 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1253 FALSE, FALSE, TRUE, body);
1255 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1256 compose = compose_followup_and_reply_to(msginfo,
1257 COMPOSE_QUOTE_CHECK,
1258 FALSE, FALSE, body);
1260 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1261 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1262 FALSE, FALSE, TRUE, body);
1264 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1265 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1266 FALSE, FALSE, TRUE, NULL);
1268 case COMPOSE_REPLY_TO_ALL:
1269 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1270 TRUE, FALSE, FALSE, body);
1272 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1273 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1274 TRUE, FALSE, FALSE, body);
1276 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1277 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1278 TRUE, FALSE, FALSE, NULL);
1280 case COMPOSE_REPLY_TO_LIST:
1281 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1282 FALSE, TRUE, FALSE, body);
1284 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1285 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1286 FALSE, TRUE, FALSE, body);
1288 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1289 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1290 FALSE, TRUE, FALSE, NULL);
1292 case COMPOSE_FORWARD:
1293 if (prefs_common.forward_as_attachment) {
1294 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1297 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1301 case COMPOSE_FORWARD_INLINE:
1302 /* check if we reply to more than one Message */
1303 if (list_len == 1) {
1304 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1307 /* more messages FALL THROUGH */
1308 case COMPOSE_FORWARD_AS_ATTACH:
1309 compose = compose_forward_multiple(NULL, msginfo_list);
1311 case COMPOSE_REDIRECT:
1312 compose = compose_redirect(NULL, msginfo, FALSE);
1315 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1318 if (compose == NULL) {
1319 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1322 ifactory = gtk_item_factory_from_widget(compose->menubar);
1324 compose->rmode = mode;
1325 switch (compose->rmode) {
1327 case COMPOSE_REPLY_WITH_QUOTE:
1328 case COMPOSE_REPLY_WITHOUT_QUOTE:
1329 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1330 debug_print("reply mode Normal\n");
1331 menu_set_active(ifactory, "/Options/Reply mode/Normal", TRUE);
1332 compose_reply_change_mode(compose, COMPOSE_REPLY, NULL); /* force update */
1334 case COMPOSE_REPLY_TO_SENDER:
1335 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1336 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1337 debug_print("reply mode Sender\n");
1338 menu_set_active(ifactory, "/Options/Reply mode/Sender", TRUE);
1340 case COMPOSE_REPLY_TO_ALL:
1341 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1342 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1343 debug_print("reply mode All\n");
1344 menu_set_active(ifactory, "/Options/Reply mode/All", TRUE);
1346 case COMPOSE_REPLY_TO_LIST:
1347 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1348 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1349 debug_print("reply mode List\n");
1350 menu_set_active(ifactory, "/Options/Reply mode/Mailing-list", TRUE);
1358 static Compose *compose_reply(MsgInfo *msginfo,
1359 ComposeQuoteMode quote_mode,
1365 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1366 to_sender, FALSE, body);
1369 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1370 ComposeQuoteMode quote_mode,
1375 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1376 to_sender, TRUE, body);
1379 static void compose_extract_original_charset(Compose *compose)
1381 MsgInfo *info = NULL;
1382 if (compose->replyinfo) {
1383 info = compose->replyinfo;
1384 } else if (compose->fwdinfo) {
1385 info = compose->fwdinfo;
1386 } else if (compose->targetinfo) {
1387 info = compose->targetinfo;
1390 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1391 MimeInfo *partinfo = mimeinfo;
1392 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1393 partinfo = procmime_mimeinfo_next(partinfo);
1395 compose->orig_charset =
1396 g_strdup(procmime_mimeinfo_get_parameter(
1397 partinfo, "charset"));
1399 procmime_mimeinfo_free_all(mimeinfo);
1403 #define SIGNAL_BLOCK(buffer) { \
1404 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1405 G_CALLBACK(compose_changed_cb), \
1407 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1408 G_CALLBACK(text_inserted), \
1412 #define SIGNAL_UNBLOCK(buffer) { \
1413 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1414 G_CALLBACK(compose_changed_cb), \
1416 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1417 G_CALLBACK(text_inserted), \
1421 static Compose *compose_generic_reply(MsgInfo *msginfo,
1422 ComposeQuoteMode quote_mode,
1423 gboolean to_all, gboolean to_ml,
1425 gboolean followup_and_reply_to,
1428 GtkItemFactory *ifactory;
1430 PrefsAccount *account = NULL;
1431 GtkTextView *textview;
1432 GtkTextBuffer *textbuf;
1433 gboolean quote = FALSE;
1434 const gchar *qmark = NULL;
1435 const gchar *body_fmt = NULL;
1437 g_return_val_if_fail(msginfo != NULL, NULL);
1438 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1440 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1442 g_return_val_if_fail(account != NULL, NULL);
1444 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1446 compose->updating = TRUE;
1448 ifactory = gtk_item_factory_from_widget(compose->menubar);
1450 menu_set_active(ifactory, "/Options/Remove references", FALSE);
1451 menu_set_sensitive(ifactory, "/Options/Remove references", TRUE);
1453 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1454 if (!compose->replyinfo)
1455 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1457 compose_extract_original_charset(compose);
1459 if (msginfo->folder && msginfo->folder->ret_rcpt)
1460 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
1462 /* Set save folder */
1463 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1464 gchar *folderidentifier;
1466 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1467 folderidentifier = folder_item_get_identifier(msginfo->folder);
1468 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1469 g_free(folderidentifier);
1472 if (compose_parse_header(compose, msginfo) < 0) return NULL;
1474 textview = (GTK_TEXT_VIEW(compose->text));
1475 textbuf = gtk_text_view_get_buffer(textview);
1476 compose_create_tags(textview, compose);
1478 undo_block(compose->undostruct);
1480 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1483 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1484 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1485 /* use the reply format of folder (if enabled), or the account's one
1486 (if enabled) or fallback to the global reply format, which is always
1487 enabled (even if empty), and use the relevant quotemark */
1489 if (msginfo->folder && msginfo->folder->prefs &&
1490 msginfo->folder->prefs->reply_with_format) {
1491 qmark = msginfo->folder->prefs->reply_quotemark;
1492 body_fmt = msginfo->folder->prefs->reply_body_format;
1494 } else if (account->reply_with_format) {
1495 qmark = account->reply_quotemark;
1496 body_fmt = account->reply_body_format;
1499 qmark = prefs_common.quotemark;
1500 body_fmt = prefs_common.quotefmt;
1505 /* empty quotemark is not allowed */
1506 if (qmark == NULL || *qmark == '\0')
1508 compose_quote_fmt(compose, compose->replyinfo,
1509 body_fmt, qmark, body, FALSE, TRUE,
1510 _("Message reply format error at line %d."));
1511 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1512 quote_fmt_reset_vartable();
1515 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1516 compose_force_encryption(compose, account, FALSE);
1519 SIGNAL_BLOCK(textbuf);
1521 if (account->auto_sig)
1522 compose_insert_sig(compose, FALSE);
1524 compose_wrap_all(compose);
1526 SIGNAL_UNBLOCK(textbuf);
1528 gtk_widget_grab_focus(compose->text);
1530 undo_unblock(compose->undostruct);
1532 if (prefs_common.auto_exteditor)
1533 compose_exec_ext_editor(compose);
1535 compose->modified = FALSE;
1536 compose_set_title(compose);
1538 compose->updating = FALSE;
1539 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1540 SCROLL_TO_CURSOR(compose);
1542 if (compose->deferred_destroy) {
1543 compose_destroy(compose);
1550 #define INSERT_FW_HEADER(var, hdr) \
1551 if (msginfo->var && *msginfo->var) { \
1552 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1553 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1554 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1557 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1558 gboolean as_attach, const gchar *body,
1559 gboolean no_extedit,
1563 GtkTextView *textview;
1564 GtkTextBuffer *textbuf;
1567 g_return_val_if_fail(msginfo != NULL, NULL);
1568 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1571 !(account = compose_guess_forward_account_from_msginfo
1573 account = cur_account;
1575 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1577 compose->updating = TRUE;
1578 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1579 if (!compose->fwdinfo)
1580 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1582 compose_extract_original_charset(compose);
1584 if (msginfo->subject && *msginfo->subject) {
1585 gchar *buf, *buf2, *p;
1587 buf = p = g_strdup(msginfo->subject);
1588 p += subject_get_prefix_length(p);
1589 memmove(buf, p, strlen(p) + 1);
1591 buf2 = g_strdup_printf("Fw: %s", buf);
1592 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1598 textview = GTK_TEXT_VIEW(compose->text);
1599 textbuf = gtk_text_view_get_buffer(textview);
1600 compose_create_tags(textview, compose);
1602 undo_block(compose->undostruct);
1606 msgfile = procmsg_get_message_file(msginfo);
1607 if (!is_file_exist(msgfile))
1608 g_warning("%s: file not exist\n", msgfile);
1610 compose_attach_append(compose, msgfile, msgfile,
1615 const gchar *qmark = NULL;
1616 const gchar *body_fmt = prefs_common.fw_quotefmt;
1617 MsgInfo *full_msginfo;
1619 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1621 full_msginfo = procmsg_msginfo_copy(msginfo);
1623 /* use the forward format of folder (if enabled), or the account's one
1624 (if enabled) or fallback to the global forward format, which is always
1625 enabled (even if empty), and use the relevant quotemark */
1626 if (msginfo->folder && msginfo->folder->prefs &&
1627 msginfo->folder->prefs->forward_with_format) {
1628 qmark = msginfo->folder->prefs->forward_quotemark;
1629 body_fmt = msginfo->folder->prefs->forward_body_format;
1631 } else if (account->forward_with_format) {
1632 qmark = account->forward_quotemark;
1633 body_fmt = account->forward_body_format;
1636 qmark = prefs_common.fw_quotemark;
1637 body_fmt = prefs_common.fw_quotefmt;
1640 /* empty quotemark is not allowed */
1641 if (qmark == NULL || *qmark == '\0')
1644 compose_quote_fmt(compose, full_msginfo,
1645 body_fmt, qmark, body, FALSE, TRUE,
1646 _("Message forward format error at line %d."));
1647 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1648 quote_fmt_reset_vartable();
1649 compose_attach_parts(compose, msginfo);
1651 procmsg_msginfo_free(full_msginfo);
1654 SIGNAL_BLOCK(textbuf);
1656 if (account->auto_sig)
1657 compose_insert_sig(compose, FALSE);
1659 compose_wrap_all(compose);
1661 SIGNAL_UNBLOCK(textbuf);
1663 gtk_text_buffer_get_start_iter(textbuf, &iter);
1664 gtk_text_buffer_place_cursor(textbuf, &iter);
1666 gtk_widget_grab_focus(compose->header_last->entry);
1668 if (!no_extedit && prefs_common.auto_exteditor)
1669 compose_exec_ext_editor(compose);
1672 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1673 gchar *folderidentifier;
1675 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1676 folderidentifier = folder_item_get_identifier(msginfo->folder);
1677 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1678 g_free(folderidentifier);
1681 undo_unblock(compose->undostruct);
1683 compose->modified = FALSE;
1684 compose_set_title(compose);
1686 compose->updating = FALSE;
1687 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1688 SCROLL_TO_CURSOR(compose);
1690 if (compose->deferred_destroy) {
1691 compose_destroy(compose);
1698 #undef INSERT_FW_HEADER
1700 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1703 GtkTextView *textview;
1704 GtkTextBuffer *textbuf;
1708 gboolean single_mail = TRUE;
1710 g_return_val_if_fail(msginfo_list != NULL, NULL);
1712 if (g_slist_length(msginfo_list) > 1)
1713 single_mail = FALSE;
1715 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1716 if (((MsgInfo *)msginfo->data)->folder == NULL)
1719 /* guess account from first selected message */
1721 !(account = compose_guess_forward_account_from_msginfo
1722 (msginfo_list->data)))
1723 account = cur_account;
1725 g_return_val_if_fail(account != NULL, NULL);
1727 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1728 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1729 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1732 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1734 compose->updating = TRUE;
1736 textview = GTK_TEXT_VIEW(compose->text);
1737 textbuf = gtk_text_view_get_buffer(textview);
1738 compose_create_tags(textview, compose);
1740 undo_block(compose->undostruct);
1741 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1742 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1744 if (!is_file_exist(msgfile))
1745 g_warning("%s: file not exist\n", msgfile);
1747 compose_attach_append(compose, msgfile, msgfile,
1753 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1754 if (info->subject && *info->subject) {
1755 gchar *buf, *buf2, *p;
1757 buf = p = g_strdup(info->subject);
1758 p += subject_get_prefix_length(p);
1759 memmove(buf, p, strlen(p) + 1);
1761 buf2 = g_strdup_printf("Fw: %s", buf);
1762 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1768 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1769 _("Fw: multiple emails"));
1772 SIGNAL_BLOCK(textbuf);
1774 if (account->auto_sig)
1775 compose_insert_sig(compose, FALSE);
1777 compose_wrap_all(compose);
1779 SIGNAL_UNBLOCK(textbuf);
1781 gtk_text_buffer_get_start_iter(textbuf, &iter);
1782 gtk_text_buffer_place_cursor(textbuf, &iter);
1784 gtk_widget_grab_focus(compose->header_last->entry);
1785 undo_unblock(compose->undostruct);
1786 compose->modified = FALSE;
1787 compose_set_title(compose);
1789 compose->updating = FALSE;
1790 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1791 SCROLL_TO_CURSOR(compose);
1793 if (compose->deferred_destroy) {
1794 compose_destroy(compose);
1801 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1803 GtkTextIter start = *iter;
1804 GtkTextIter end_iter;
1805 int start_pos = gtk_text_iter_get_offset(&start);
1807 if (!compose->account->sig_sep)
1810 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1811 start_pos+strlen(compose->account->sig_sep));
1813 /* check sig separator */
1814 str = gtk_text_iter_get_text(&start, &end_iter);
1815 if (!strcmp(str, compose->account->sig_sep)) {
1817 /* check end of line (\n) */
1818 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1819 start_pos+strlen(compose->account->sig_sep));
1820 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1821 start_pos+strlen(compose->account->sig_sep)+1);
1822 tmp = gtk_text_iter_get_text(&start, &end_iter);
1823 if (!strcmp(tmp,"\n")) {
1835 static void compose_colorize_signature(Compose *compose)
1837 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1839 GtkTextIter end_iter;
1840 gtk_text_buffer_get_start_iter(buffer, &iter);
1841 while (gtk_text_iter_forward_line(&iter))
1842 if (compose_is_sig_separator(compose, buffer, &iter)) {
1843 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1844 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1848 #define BLOCK_WRAP() { \
1849 prev_autowrap = compose->autowrap; \
1850 buffer = gtk_text_view_get_buffer( \
1851 GTK_TEXT_VIEW(compose->text)); \
1852 compose->autowrap = FALSE; \
1854 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1855 G_CALLBACK(compose_changed_cb), \
1857 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1858 G_CALLBACK(text_inserted), \
1861 #define UNBLOCK_WRAP() { \
1862 compose->autowrap = prev_autowrap; \
1863 if (compose->autowrap) { \
1864 gint old = compose->draft_timeout_tag; \
1865 compose->draft_timeout_tag = -2; \
1866 compose_wrap_all(compose); \
1867 compose->draft_timeout_tag = old; \
1870 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1871 G_CALLBACK(compose_changed_cb), \
1873 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1874 G_CALLBACK(text_inserted), \
1878 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
1880 Compose *compose = NULL;
1881 PrefsAccount *account = NULL;
1882 GtkTextView *textview;
1883 GtkTextBuffer *textbuf;
1887 gchar buf[BUFFSIZE];
1888 gboolean use_signing = FALSE;
1889 gboolean use_encryption = FALSE;
1890 gchar *privacy_system = NULL;
1891 int priority = PRIORITY_NORMAL;
1892 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
1894 g_return_val_if_fail(msginfo != NULL, NULL);
1895 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1897 if (compose_put_existing_to_front(msginfo)) {
1901 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
1902 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
1903 gchar queueheader_buf[BUFFSIZE];
1906 /* Select Account from queue headers */
1907 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1908 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
1909 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
1910 account = account_find_from_id(id);
1912 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1913 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
1914 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
1915 account = account_find_from_id(id);
1917 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1918 sizeof(queueheader_buf), "NAID:")) {
1919 id = atoi(&queueheader_buf[strlen("NAID:")]);
1920 account = account_find_from_id(id);
1922 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1923 sizeof(queueheader_buf), "MAID:")) {
1924 id = atoi(&queueheader_buf[strlen("MAID:")]);
1925 account = account_find_from_id(id);
1927 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1928 sizeof(queueheader_buf), "S:")) {
1929 account = account_find_from_address(queueheader_buf);
1931 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1932 sizeof(queueheader_buf), "X-Claws-Sign:")) {
1933 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
1934 use_signing = param;
1937 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1938 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
1939 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
1940 use_signing = param;
1943 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1944 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
1945 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
1946 use_encryption = param;
1948 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1949 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
1950 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
1951 use_encryption = param;
1953 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1954 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
1955 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
1957 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1958 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
1959 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
1961 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1962 sizeof(queueheader_buf), "X-Priority: ")) {
1963 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
1966 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1967 sizeof(queueheader_buf), "RMID:")) {
1968 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
1969 if (tokens[0] && tokens[1] && tokens[2]) {
1970 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
1971 if (orig_item != NULL) {
1972 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
1977 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1978 sizeof(queueheader_buf), "FMID:")) {
1979 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
1980 if (tokens[0] && tokens[1] && tokens[2]) {
1981 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
1982 if (orig_item != NULL) {
1983 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
1989 account = msginfo->folder->folder->account;
1992 if (!account && prefs_common.reedit_account_autosel) {
1993 gchar from[BUFFSIZE];
1994 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
1995 extract_address(from);
1996 account = account_find_from_address(from);
2000 account = cur_account;
2002 g_return_val_if_fail(account != NULL, NULL);
2004 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2006 compose->replyinfo = replyinfo;
2007 compose->fwdinfo = fwdinfo;
2009 compose->updating = TRUE;
2010 compose->priority = priority;
2012 if (privacy_system != NULL) {
2013 compose->privacy_system = privacy_system;
2014 compose_use_signing(compose, use_signing);
2015 compose_use_encryption(compose, use_encryption);
2016 compose_update_privacy_system_menu_item(compose, FALSE);
2018 activate_privacy_system(compose, account, FALSE);
2021 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2023 compose_extract_original_charset(compose);
2025 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2026 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2027 gchar queueheader_buf[BUFFSIZE];
2029 /* Set message save folder */
2030 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2033 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2034 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
2035 gtk_editable_insert_text(GTK_EDITABLE(compose->savemsg_entry), &queueheader_buf[4], strlen(&queueheader_buf[4]), &startpos);
2037 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2038 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2040 GtkItemFactory *ifactory;
2041 ifactory = gtk_item_factory_from_widget(compose->menubar);
2042 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
2047 if (compose_parse_header(compose, msginfo) < 0) {
2048 compose->updating = FALSE;
2049 compose_destroy(compose);
2052 compose_reedit_set_entry(compose, msginfo);
2054 textview = GTK_TEXT_VIEW(compose->text);
2055 textbuf = gtk_text_view_get_buffer(textview);
2056 compose_create_tags(textview, compose);
2058 mark = gtk_text_buffer_get_insert(textbuf);
2059 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2061 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2062 G_CALLBACK(compose_changed_cb),
2065 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2066 fp = procmime_get_first_encrypted_text_content(msginfo);
2068 compose_force_encryption(compose, account, TRUE);
2071 fp = procmime_get_first_text_content(msginfo);
2074 g_warning("Can't get text part\n");
2078 gboolean prev_autowrap = compose->autowrap;
2079 GtkTextBuffer *buffer = textbuf;
2081 while (fgets(buf, sizeof(buf), fp) != NULL) {
2083 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2089 compose_attach_parts(compose, msginfo);
2091 compose_colorize_signature(compose);
2093 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2094 G_CALLBACK(compose_changed_cb),
2097 gtk_widget_grab_focus(compose->text);
2099 if (prefs_common.auto_exteditor) {
2100 compose_exec_ext_editor(compose);
2102 compose->modified = FALSE;
2103 compose_set_title(compose);
2105 compose->updating = FALSE;
2106 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2107 SCROLL_TO_CURSOR(compose);
2109 if (compose->deferred_destroy) {
2110 compose_destroy(compose);
2114 compose->sig_str = compose_get_signature_str(compose);
2119 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2124 GtkItemFactory *ifactory;
2127 g_return_val_if_fail(msginfo != NULL, NULL);
2130 account = account_get_reply_account(msginfo,
2131 prefs_common.reply_account_autosel);
2132 g_return_val_if_fail(account != NULL, NULL);
2134 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2136 compose->updating = TRUE;
2138 ifactory = gtk_item_factory_from_widget(compose->menubar);
2139 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2140 compose->replyinfo = NULL;
2141 compose->fwdinfo = NULL;
2143 compose_show_first_last_header(compose, TRUE);
2145 gtk_widget_grab_focus(compose->header_last->entry);
2147 filename = procmsg_get_message_file(msginfo);
2149 if (filename == NULL) {
2150 compose->updating = FALSE;
2151 compose_destroy(compose);
2156 compose->redirect_filename = filename;
2158 /* Set save folder */
2159 item = msginfo->folder;
2160 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2161 gchar *folderidentifier;
2163 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2164 folderidentifier = folder_item_get_identifier(item);
2165 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
2166 g_free(folderidentifier);
2169 compose_attach_parts(compose, msginfo);
2171 if (msginfo->subject)
2172 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2174 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2176 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2177 _("Message redirect format error at line %d."));
2178 quote_fmt_reset_vartable();
2179 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2181 compose_colorize_signature(compose);
2183 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
2184 menu_set_sensitive(ifactory, "/Add...", FALSE);
2185 menu_set_sensitive(ifactory, "/Remove", FALSE);
2186 menu_set_sensitive(ifactory, "/Properties...", FALSE);
2188 ifactory = gtk_item_factory_from_widget(compose->menubar);
2189 menu_set_sensitive(ifactory, "/Message/Save", FALSE);
2190 menu_set_sensitive(ifactory, "/Message/Insert file", FALSE);
2191 menu_set_sensitive(ifactory, "/Message/Attach file", FALSE);
2192 menu_set_sensitive(ifactory, "/Message/Insert signature", FALSE);
2193 menu_set_sensitive(ifactory, "/Edit", FALSE);
2194 menu_set_sensitive(ifactory, "/Options", FALSE);
2195 menu_set_sensitive(ifactory, "/Tools/Show ruler", FALSE);
2196 menu_set_sensitive(ifactory, "/Tools/Actions", FALSE);
2198 if (compose->toolbar->draft_btn)
2199 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2200 if (compose->toolbar->insert_btn)
2201 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2202 if (compose->toolbar->attach_btn)
2203 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2204 if (compose->toolbar->sig_btn)
2205 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2206 if (compose->toolbar->exteditor_btn)
2207 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2208 if (compose->toolbar->linewrap_current_btn)
2209 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2210 if (compose->toolbar->linewrap_all_btn)
2211 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2213 compose->modified = FALSE;
2214 compose_set_title(compose);
2215 compose->updating = FALSE;
2216 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2217 SCROLL_TO_CURSOR(compose);
2219 if (compose->deferred_destroy) {
2220 compose_destroy(compose);
2227 GList *compose_get_compose_list(void)
2229 return compose_list;
2232 void compose_entry_append(Compose *compose, const gchar *address,
2233 ComposeEntryType type)
2235 const gchar *header;
2237 gboolean in_quote = FALSE;
2238 if (!address || *address == '\0') return;
2245 header = N_("Bcc:");
2247 case COMPOSE_REPLYTO:
2248 header = N_("Reply-To:");
2250 case COMPOSE_NEWSGROUPS:
2251 header = N_("Newsgroups:");
2253 case COMPOSE_FOLLOWUPTO:
2254 header = N_( "Followup-To:");
2261 header = prefs_common_translated_header_name(header);
2263 cur = begin = (gchar *)address;
2265 /* we separate the line by commas, but not if we're inside a quoted
2267 while (*cur != '\0') {
2269 in_quote = !in_quote;
2270 if (*cur == ',' && !in_quote) {
2271 gchar *tmp = g_strdup(begin);
2273 tmp[cur-begin]='\0';
2276 while (*tmp == ' ' || *tmp == '\t')
2278 compose_add_header_entry(compose, header, tmp);
2285 gchar *tmp = g_strdup(begin);
2287 tmp[cur-begin]='\0';
2290 while (*tmp == ' ' || *tmp == '\t')
2292 compose_add_header_entry(compose, header, tmp);
2297 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2299 static GdkColor yellow;
2300 static GdkColor black;
2301 static gboolean yellow_initialised = FALSE;
2305 if (!yellow_initialised) {
2306 gdk_color_parse("#f5f6be", &yellow);
2307 gdk_color_parse("#000000", &black);
2308 yellow_initialised = gdk_colormap_alloc_color(
2309 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2310 yellow_initialised &= gdk_colormap_alloc_color(
2311 gdk_colormap_get_system(), &black, FALSE, TRUE);
2314 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2315 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2316 if (gtk_entry_get_text(entry) &&
2317 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2318 if (yellow_initialised) {
2319 gtk_widget_modify_base(
2320 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2321 GTK_STATE_NORMAL, &yellow);
2322 gtk_widget_modify_text(
2323 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2324 GTK_STATE_NORMAL, &black);
2330 void compose_toolbar_cb(gint action, gpointer data)
2332 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2333 Compose *compose = (Compose*)toolbar_item->parent;
2335 g_return_if_fail(compose != NULL);
2339 compose_send_cb(compose, 0, NULL);
2342 compose_send_later_cb(compose, 0, NULL);
2345 compose_draft_cb(compose, COMPOSE_QUIT_EDITING, NULL);
2348 compose_insert_file_cb(compose, 0, NULL);
2351 compose_attach_cb(compose, 0, NULL);
2354 compose_insert_sig(compose, FALSE);
2357 compose_ext_editor_cb(compose, 0, NULL);
2359 case A_LINEWRAP_CURRENT:
2360 compose_beautify_paragraph(compose, NULL, TRUE);
2362 case A_LINEWRAP_ALL:
2363 compose_wrap_all_full(compose, TRUE);
2366 compose_address_cb(compose, 0, NULL);
2369 case A_CHECK_SPELLING:
2370 compose_check_all(compose);
2378 static void compose_entries_set(Compose *compose, const gchar *mailto)
2383 gchar *subject = NULL;
2387 gchar *attach = NULL;
2389 scan_mailto_url(mailto, &to, &cc, &bcc, &subject, &body, &attach);
2392 compose_entry_append(compose, to, COMPOSE_TO);
2394 compose_entry_append(compose, cc, COMPOSE_CC);
2396 compose_entry_append(compose, bcc, COMPOSE_BCC);
2398 if (!g_utf8_validate (subject, -1, NULL)) {
2399 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2400 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2403 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2407 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2408 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2411 gboolean prev_autowrap = compose->autowrap;
2413 compose->autowrap = FALSE;
2415 mark = gtk_text_buffer_get_insert(buffer);
2416 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2418 if (!g_utf8_validate (body, -1, NULL)) {
2419 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2420 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2423 gtk_text_buffer_insert(buffer, &iter, body, -1);
2425 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2427 compose->autowrap = prev_autowrap;
2428 if (compose->autowrap)
2429 compose_wrap_all(compose);
2433 gchar *utf8_filename = conv_filename_to_utf8(attach);
2434 if (utf8_filename) {
2435 if (compose_attach_append(compose, attach, utf8_filename, NULL)) {
2436 alertpanel_notice(_("The file '%s' has been attached."), attach);
2438 g_free(utf8_filename);
2440 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2451 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2453 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2454 {"Cc:", NULL, TRUE},
2455 {"References:", NULL, FALSE},
2456 {"Bcc:", NULL, TRUE},
2457 {"Newsgroups:", NULL, TRUE},
2458 {"Followup-To:", NULL, TRUE},
2459 {"List-Post:", NULL, FALSE},
2460 {"X-Priority:", NULL, FALSE},
2461 {NULL, NULL, FALSE}};
2477 g_return_val_if_fail(msginfo != NULL, -1);
2479 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2480 procheader_get_header_fields(fp, hentry);
2483 if (hentry[H_REPLY_TO].body != NULL) {
2484 if (hentry[H_REPLY_TO].body[0] != '\0') {
2486 conv_unmime_header(hentry[H_REPLY_TO].body,
2489 g_free(hentry[H_REPLY_TO].body);
2490 hentry[H_REPLY_TO].body = NULL;
2492 if (hentry[H_CC].body != NULL) {
2493 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2494 g_free(hentry[H_CC].body);
2495 hentry[H_CC].body = NULL;
2497 if (hentry[H_REFERENCES].body != NULL) {
2498 if (compose->mode == COMPOSE_REEDIT)
2499 compose->references = hentry[H_REFERENCES].body;
2501 compose->references = compose_parse_references
2502 (hentry[H_REFERENCES].body, msginfo->msgid);
2503 g_free(hentry[H_REFERENCES].body);
2505 hentry[H_REFERENCES].body = NULL;
2507 if (hentry[H_BCC].body != NULL) {
2508 if (compose->mode == COMPOSE_REEDIT)
2510 conv_unmime_header(hentry[H_BCC].body, NULL);
2511 g_free(hentry[H_BCC].body);
2512 hentry[H_BCC].body = NULL;
2514 if (hentry[H_NEWSGROUPS].body != NULL) {
2515 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2516 hentry[H_NEWSGROUPS].body = NULL;
2518 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2519 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2520 compose->followup_to =
2521 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2524 g_free(hentry[H_FOLLOWUP_TO].body);
2525 hentry[H_FOLLOWUP_TO].body = NULL;
2527 if (hentry[H_LIST_POST].body != NULL) {
2530 extract_address(hentry[H_LIST_POST].body);
2531 if (hentry[H_LIST_POST].body[0] != '\0') {
2532 scan_mailto_url(hentry[H_LIST_POST].body,
2533 &to, NULL, NULL, NULL, NULL, NULL);
2535 g_free(compose->ml_post);
2536 compose->ml_post = to;
2539 g_free(hentry[H_LIST_POST].body);
2540 hentry[H_LIST_POST].body = NULL;
2543 /* CLAWS - X-Priority */
2544 if (compose->mode == COMPOSE_REEDIT)
2545 if (hentry[H_X_PRIORITY].body != NULL) {
2548 priority = atoi(hentry[H_X_PRIORITY].body);
2549 g_free(hentry[H_X_PRIORITY].body);
2551 hentry[H_X_PRIORITY].body = NULL;
2553 if (priority < PRIORITY_HIGHEST ||
2554 priority > PRIORITY_LOWEST)
2555 priority = PRIORITY_NORMAL;
2557 compose->priority = priority;
2560 if (compose->mode == COMPOSE_REEDIT) {
2561 if (msginfo->inreplyto && *msginfo->inreplyto)
2562 compose->inreplyto = g_strdup(msginfo->inreplyto);
2566 if (msginfo->msgid && *msginfo->msgid)
2567 compose->inreplyto = g_strdup(msginfo->msgid);
2569 if (!compose->references) {
2570 if (msginfo->msgid && *msginfo->msgid) {
2571 if (msginfo->inreplyto && *msginfo->inreplyto)
2572 compose->references =
2573 g_strdup_printf("<%s>\n\t<%s>",
2577 compose->references =
2578 g_strconcat("<", msginfo->msgid, ">",
2580 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2581 compose->references =
2582 g_strconcat("<", msginfo->inreplyto, ">",
2590 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2592 GSList *ref_id_list, *cur;
2596 ref_id_list = references_list_append(NULL, ref);
2597 if (!ref_id_list) return NULL;
2598 if (msgid && *msgid)
2599 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2604 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2605 /* "<" + Message-ID + ">" + CR+LF+TAB */
2606 len += strlen((gchar *)cur->data) + 5;
2608 if (len > MAX_REFERENCES_LEN) {
2609 /* remove second message-ID */
2610 if (ref_id_list && ref_id_list->next &&
2611 ref_id_list->next->next) {
2612 g_free(ref_id_list->next->data);
2613 ref_id_list = g_slist_remove
2614 (ref_id_list, ref_id_list->next->data);
2616 slist_free_strings(ref_id_list);
2617 g_slist_free(ref_id_list);
2624 new_ref = g_string_new("");
2625 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2626 if (new_ref->len > 0)
2627 g_string_append(new_ref, "\n\t");
2628 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2631 slist_free_strings(ref_id_list);
2632 g_slist_free(ref_id_list);
2634 new_ref_str = new_ref->str;
2635 g_string_free(new_ref, FALSE);
2640 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2641 const gchar *fmt, const gchar *qmark,
2642 const gchar *body, gboolean rewrap,
2643 gboolean need_unescape,
2644 const gchar *err_msg)
2646 MsgInfo* dummyinfo = NULL;
2647 gchar *quote_str = NULL;
2649 gboolean prev_autowrap;
2650 const gchar *trimmed_body = body;
2651 gint cursor_pos = -1;
2652 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2653 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2658 SIGNAL_BLOCK(buffer);
2661 dummyinfo = compose_msginfo_new_from_compose(compose);
2662 msginfo = dummyinfo;
2665 if (qmark != NULL) {
2667 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
2668 compose->gtkaspell);
2670 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
2672 quote_fmt_scan_string(qmark);
2675 buf = quote_fmt_get_buffer();
2677 alertpanel_error(_("Quote mark format error."));
2679 Xstrdup_a(quote_str, buf, goto error)
2682 if (fmt && *fmt != '\0') {
2685 while (*trimmed_body == '\n')
2689 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account,
2690 compose->gtkaspell);
2692 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account);
2694 if (need_unescape) {
2697 /* decode \-escape sequences in the internal representation of the quote format */
2698 tmp = malloc(strlen(fmt)+1);
2699 pref_get_unescaped_pref(tmp, fmt);
2700 quote_fmt_scan_string(tmp);
2704 quote_fmt_scan_string(fmt);
2708 buf = quote_fmt_get_buffer();
2710 gint line = quote_fmt_get_line();
2711 alertpanel_error(err_msg, line);
2717 prev_autowrap = compose->autowrap;
2718 compose->autowrap = FALSE;
2720 mark = gtk_text_buffer_get_insert(buffer);
2721 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2722 if (g_utf8_validate(buf, -1, NULL)) {
2723 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2725 gchar *tmpout = NULL;
2726 tmpout = conv_codeset_strdup
2727 (buf, conv_get_locale_charset_str_no_utf8(),
2729 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2731 tmpout = g_malloc(strlen(buf)*2+1);
2732 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2734 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2738 cursor_pos = quote_fmt_get_cursor_pos();
2739 compose->set_cursor_pos = cursor_pos;
2740 if (cursor_pos == -1) {
2743 gtk_text_buffer_get_start_iter(buffer, &iter);
2744 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2745 gtk_text_buffer_place_cursor(buffer, &iter);
2747 compose->autowrap = prev_autowrap;
2748 if (compose->autowrap && rewrap)
2749 compose_wrap_all(compose);
2756 SIGNAL_UNBLOCK(buffer);
2758 procmsg_msginfo_free( dummyinfo );
2763 /* if ml_post is of type addr@host and from is of type
2764 * addr-anything@host, return TRUE
2766 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2768 gchar *left_ml = NULL;
2769 gchar *right_ml = NULL;
2770 gchar *left_from = NULL;
2771 gchar *right_from = NULL;
2772 gboolean result = FALSE;
2774 if (!ml_post || !from)
2777 left_ml = g_strdup(ml_post);
2778 if (strstr(left_ml, "@")) {
2779 right_ml = strstr(left_ml, "@")+1;
2780 *(strstr(left_ml, "@")) = '\0';
2783 left_from = g_strdup(from);
2784 if (strstr(left_from, "@")) {
2785 right_from = strstr(left_from, "@")+1;
2786 *(strstr(left_from, "@")) = '\0';
2789 if (left_ml && left_from && right_ml && right_from
2790 && !strncmp(left_from, left_ml, strlen(left_ml))
2791 && !strcmp(right_from, right_ml)) {
2800 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2802 gchar *my_addr1, *my_addr2;
2804 if (!addr1 || !addr2)
2807 Xstrdup_a(my_addr1, addr1, return FALSE);
2808 Xstrdup_a(my_addr2, addr2, return FALSE);
2810 extract_address(my_addr1);
2811 extract_address(my_addr2);
2813 return !strcasecmp(my_addr1, my_addr2);
2816 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2817 gboolean to_all, gboolean to_ml,
2819 gboolean followup_and_reply_to)
2821 GSList *cc_list = NULL;
2824 gchar *replyto = NULL;
2825 GHashTable *to_table;
2827 gboolean reply_to_ml = FALSE;
2828 gboolean default_reply_to = FALSE;
2830 g_return_if_fail(compose->account != NULL);
2831 g_return_if_fail(msginfo != NULL);
2833 reply_to_ml = to_ml && compose->ml_post;
2835 default_reply_to = msginfo->folder &&
2836 msginfo->folder->prefs->enable_default_reply_to;
2838 if (compose->account->protocol != A_NNTP) {
2839 if (reply_to_ml && !default_reply_to) {
2841 gboolean is_subscr = is_subscription(compose->ml_post,
2844 /* normal answer to ml post with a reply-to */
2845 compose_entry_append(compose,
2848 if (compose->replyto
2849 && !same_address(compose->ml_post, compose->replyto))
2850 compose_entry_append(compose,
2854 /* answer to subscription confirmation */
2855 if (compose->replyto)
2856 compose_entry_append(compose,
2859 else if (msginfo->from)
2860 compose_entry_append(compose,
2865 else if (!(to_all || to_sender) && default_reply_to) {
2866 compose_entry_append(compose,
2867 msginfo->folder->prefs->default_reply_to,
2869 compose_entry_mark_default_to(compose,
2870 msginfo->folder->prefs->default_reply_to);
2875 Xstrdup_a(tmp1, msginfo->from, return);
2876 extract_address(tmp1);
2877 if (to_all || to_sender ||
2878 !account_find_from_address(tmp1))
2879 compose_entry_append(compose,
2880 (compose->replyto && !to_sender)
2881 ? compose->replyto :
2882 msginfo->from ? msginfo->from : "",
2884 else if (!to_all && !to_sender) {
2885 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2886 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
2887 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2888 compose_entry_append(compose,
2889 msginfo->from ? msginfo->from : "",
2892 /* replying to own mail, use original recp */
2893 compose_entry_append(compose,
2894 msginfo->to ? msginfo->to : "",
2896 compose_entry_append(compose,
2897 msginfo->cc ? msginfo->cc : "",
2903 if (to_sender || (compose->followup_to &&
2904 !strncmp(compose->followup_to, "poster", 6)))
2905 compose_entry_append
2907 (compose->replyto ? compose->replyto :
2908 msginfo->from ? msginfo->from : ""),
2911 else if (followup_and_reply_to || to_all) {
2912 compose_entry_append
2914 (compose->replyto ? compose->replyto :
2915 msginfo->from ? msginfo->from : ""),
2918 compose_entry_append
2920 compose->followup_to ? compose->followup_to :
2921 compose->newsgroups ? compose->newsgroups : "",
2922 COMPOSE_NEWSGROUPS);
2925 compose_entry_append
2927 compose->followup_to ? compose->followup_to :
2928 compose->newsgroups ? compose->newsgroups : "",
2929 COMPOSE_NEWSGROUPS);
2932 if (msginfo->subject && *msginfo->subject) {
2936 buf = p = g_strdup(msginfo->subject);
2937 p += subject_get_prefix_length(p);
2938 memmove(buf, p, strlen(p) + 1);
2940 buf2 = g_strdup_printf("Re: %s", buf);
2941 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2946 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
2948 if (to_ml && compose->ml_post) return;
2949 if (!to_all || compose->account->protocol == A_NNTP) return;
2951 if (compose->replyto) {
2952 Xstrdup_a(replyto, compose->replyto, return);
2953 extract_address(replyto);
2955 if (msginfo->from) {
2956 Xstrdup_a(from, msginfo->from, return);
2957 extract_address(from);
2960 if (replyto && from)
2961 cc_list = address_list_append_with_comments(cc_list, from);
2962 if (to_all && msginfo->folder &&
2963 msginfo->folder->prefs->enable_default_reply_to)
2964 cc_list = address_list_append_with_comments(cc_list,
2965 msginfo->folder->prefs->default_reply_to);
2966 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
2967 cc_list = address_list_append_with_comments(cc_list, compose->cc);
2969 to_table = g_hash_table_new(g_str_hash, g_str_equal);
2971 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
2972 if (compose->account) {
2973 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
2974 GINT_TO_POINTER(1));
2976 /* remove address on To: and that of current account */
2977 for (cur = cc_list; cur != NULL; ) {
2978 GSList *next = cur->next;
2981 addr = g_utf8_strdown(cur->data, -1);
2982 extract_address(addr);
2984 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
2985 cc_list = g_slist_remove(cc_list, cur->data);
2987 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
2991 hash_free_strings(to_table);
2992 g_hash_table_destroy(to_table);
2995 for (cur = cc_list; cur != NULL; cur = cur->next)
2996 compose_entry_append(compose, (gchar *)cur->data,
2998 slist_free_strings(cc_list);
2999 g_slist_free(cc_list);
3004 #define SET_ENTRY(entry, str) \
3007 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3010 #define SET_ADDRESS(type, str) \
3013 compose_entry_append(compose, str, type); \
3016 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3018 g_return_if_fail(msginfo != NULL);
3020 SET_ENTRY(subject_entry, msginfo->subject);
3021 SET_ENTRY(from_name, msginfo->from);
3022 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3023 SET_ADDRESS(COMPOSE_CC, compose->cc);
3024 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3025 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3026 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3027 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3029 compose_update_priority_menu_item(compose);
3030 compose_update_privacy_system_menu_item(compose, FALSE);
3031 compose_show_first_last_header(compose, TRUE);
3037 static void compose_insert_sig(Compose *compose, gboolean replace)
3039 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3040 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3042 GtkTextIter iter, iter_end;
3044 gboolean prev_autowrap;
3045 gboolean found = FALSE;
3046 gboolean exists = FALSE;
3048 g_return_if_fail(compose->account != NULL);
3052 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3053 G_CALLBACK(compose_changed_cb),
3056 mark = gtk_text_buffer_get_insert(buffer);
3057 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3058 cur_pos = gtk_text_iter_get_offset (&iter);
3060 gtk_text_buffer_get_end_iter(buffer, &iter);
3062 exists = (compose->sig_str != NULL);
3065 GtkTextIter first_iter, start_iter, end_iter;
3067 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3069 if (!exists || compose->sig_str[0] == '\0')
3072 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3073 compose->signature_tag);
3076 /* include previous \n\n */
3077 gtk_text_iter_backward_chars(&first_iter, 2);
3078 start_iter = first_iter;
3079 end_iter = first_iter;
3081 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3082 compose->signature_tag);
3083 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3084 compose->signature_tag);
3086 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3092 g_free(compose->sig_str);
3093 compose->sig_str = compose_get_signature_str(compose);
3095 cur_pos = gtk_text_iter_get_offset(&iter);
3097 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3098 g_free(compose->sig_str);
3099 compose->sig_str = NULL;
3101 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3103 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3104 gtk_text_iter_forward_chars(&iter, 2);
3105 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3106 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3108 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3109 cur_pos = gtk_text_buffer_get_char_count (buffer);
3111 /* put the cursor where it should be
3112 * either where the quote_fmt says, either before the signature */
3113 if (compose->set_cursor_pos < 0)
3114 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3116 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3117 compose->set_cursor_pos);
3119 gtk_text_buffer_place_cursor(buffer, &iter);
3120 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3121 G_CALLBACK(compose_changed_cb),
3127 static gchar *compose_get_signature_str(Compose *compose)
3129 gchar *sig_body = NULL;
3130 gchar *sig_str = NULL;
3131 gchar *utf8_sig_str = NULL;
3133 g_return_val_if_fail(compose->account != NULL, NULL);
3135 if (!compose->account->sig_path)
3138 if (compose->account->sig_type == SIG_FILE) {
3139 if (!is_file_or_fifo_exist(compose->account->sig_path)) {
3140 g_warning("can't open signature file: %s\n",
3141 compose->account->sig_path);
3146 if (compose->account->sig_type == SIG_COMMAND)
3147 sig_body = get_command_output(compose->account->sig_path);
3151 tmp = file_read_to_str(compose->account->sig_path);
3154 sig_body = normalize_newlines(tmp);
3158 if (compose->account->sig_sep) {
3159 sig_str = g_strconcat("\n\n", compose->account->sig_sep, "\n", sig_body,
3163 sig_str = g_strconcat("\n\n", sig_body, NULL);
3166 if (g_utf8_validate(sig_str, -1, NULL) == TRUE)
3167 utf8_sig_str = sig_str;
3169 utf8_sig_str = conv_codeset_strdup
3170 (sig_str, conv_get_locale_charset_str_no_utf8(),
3176 return utf8_sig_str;
3179 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3182 GtkTextBuffer *buffer;
3185 const gchar *cur_encoding;
3186 gchar buf[BUFFSIZE];
3189 gboolean prev_autowrap;
3190 gboolean badtxt = FALSE;
3192 g_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3194 if ((fp = g_fopen(file, "rb")) == NULL) {
3195 FILE_OP_ERROR(file, "fopen");
3196 return COMPOSE_INSERT_READ_ERROR;
3199 prev_autowrap = compose->autowrap;
3200 compose->autowrap = FALSE;
3202 text = GTK_TEXT_VIEW(compose->text);
3203 buffer = gtk_text_view_get_buffer(text);
3204 mark = gtk_text_buffer_get_insert(buffer);
3205 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3207 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3208 G_CALLBACK(text_inserted),
3211 cur_encoding = conv_get_locale_charset_str_no_utf8();
3213 while (fgets(buf, sizeof(buf), fp) != NULL) {
3216 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3217 str = g_strdup(buf);
3219 str = conv_codeset_strdup
3220 (buf, cur_encoding, CS_INTERNAL);
3223 /* strip <CR> if DOS/Windows file,
3224 replace <CR> with <LF> if Macintosh file. */
3227 if (len > 0 && str[len - 1] != '\n') {
3229 if (str[len] == '\r') str[len] = '\n';
3232 gtk_text_buffer_insert(buffer, &iter, str, -1);
3236 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3237 G_CALLBACK(text_inserted),
3239 compose->autowrap = prev_autowrap;
3240 if (compose->autowrap)
3241 compose_wrap_all(compose);
3246 return COMPOSE_INSERT_INVALID_CHARACTER;
3248 return COMPOSE_INSERT_SUCCESS;
3251 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3252 const gchar *filename,
3253 const gchar *content_type)
3261 GtkListStore *store;
3263 gboolean has_binary = FALSE;
3265 if (!is_file_exist(file)) {
3266 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3267 gboolean result = FALSE;
3268 if (file_from_uri && is_file_exist(file_from_uri)) {
3269 result = compose_attach_append(
3270 compose, file_from_uri,
3274 g_free(file_from_uri);
3277 alertpanel_error("File %s doesn't exist\n", filename);
3280 if ((size = get_file_size(file)) < 0) {
3281 alertpanel_error("Can't get file size of %s\n", filename);
3285 alertpanel_error(_("File %s is empty."), filename);
3288 if ((fp = g_fopen(file, "rb")) == NULL) {
3289 alertpanel_error(_("Can't read %s."), filename);
3294 ainfo = g_new0(AttachInfo, 1);
3295 auto_ainfo = g_auto_pointer_new_with_free
3296 (ainfo, (GFreeFunc) compose_attach_info_free);
3297 ainfo->file = g_strdup(file);
3300 ainfo->content_type = g_strdup(content_type);
3301 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3303 MsgFlags flags = {0, 0};
3305 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3306 ainfo->encoding = ENC_7BIT;
3308 ainfo->encoding = ENC_8BIT;
3310 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3311 if (msginfo && msginfo->subject)
3312 name = g_strdup(msginfo->subject);
3314 name = g_path_get_basename(filename ? filename : file);
3316 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3318 procmsg_msginfo_free(msginfo);
3320 if (!g_ascii_strncasecmp(content_type, "text", 4))
3321 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3323 ainfo->encoding = ENC_BASE64;
3324 name = g_path_get_basename(filename ? filename : file);
3325 ainfo->name = g_strdup(name);
3329 ainfo->content_type = procmime_get_mime_type(file);
3330 if (!ainfo->content_type) {
3331 ainfo->content_type =
3332 g_strdup("application/octet-stream");
3333 ainfo->encoding = ENC_BASE64;
3334 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3336 procmime_get_encoding_for_text_file(file, &has_binary);
3338 ainfo->encoding = ENC_BASE64;
3339 name = g_path_get_basename(filename ? filename : file);
3340 ainfo->name = g_strdup(name);
3344 if (ainfo->name != NULL
3345 && !strcmp(ainfo->name, ".")) {
3346 g_free(ainfo->name);
3350 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3351 g_free(ainfo->content_type);
3352 ainfo->content_type = g_strdup("application/octet-stream");
3356 size_text = to_human_readable(size);
3358 store = GTK_LIST_STORE(gtk_tree_view_get_model
3359 (GTK_TREE_VIEW(compose->attach_clist)));
3361 gtk_list_store_append(store, &iter);
3362 gtk_list_store_set(store, &iter,
3363 COL_MIMETYPE, ainfo->content_type,
3364 COL_SIZE, size_text,
3365 COL_NAME, ainfo->name,
3367 COL_AUTODATA, auto_ainfo,
3370 g_auto_pointer_free(auto_ainfo);
3371 compose_attach_update_label(compose);
3375 static void compose_use_signing(Compose *compose, gboolean use_signing)
3377 GtkItemFactory *ifactory;
3378 GtkWidget *menuitem = NULL;
3380 compose->use_signing = use_signing;
3381 ifactory = gtk_item_factory_from_widget(compose->menubar);
3382 menuitem = gtk_item_factory_get_item
3383 (ifactory, "/Options/Sign");
3384 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
3388 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3390 GtkItemFactory *ifactory;
3391 GtkWidget *menuitem = NULL;
3393 compose->use_encryption = use_encryption;
3394 ifactory = gtk_item_factory_from_widget(compose->menubar);
3395 menuitem = gtk_item_factory_get_item
3396 (ifactory, "/Options/Encrypt");
3398 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
3402 #define NEXT_PART_NOT_CHILD(info) \
3404 node = info->node; \
3405 while (node->children) \
3406 node = g_node_last_child(node); \
3407 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3410 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3414 MimeInfo *firsttext = NULL;
3415 MimeInfo *encrypted = NULL;
3418 const gchar *partname = NULL;
3420 mimeinfo = procmime_scan_message(msginfo);
3421 if (!mimeinfo) return;
3423 if (mimeinfo->node->children == NULL) {
3424 procmime_mimeinfo_free_all(mimeinfo);
3428 /* find first content part */
3429 child = (MimeInfo *) mimeinfo->node->children->data;
3430 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3431 child = (MimeInfo *)child->node->children->data;
3433 if (child->type == MIMETYPE_TEXT) {
3435 debug_print("First text part found\n");
3436 } else if (compose->mode == COMPOSE_REEDIT &&
3437 child->type == MIMETYPE_APPLICATION &&
3438 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3439 encrypted = (MimeInfo *)child->node->parent->data;
3442 child = (MimeInfo *) mimeinfo->node->children->data;
3443 while (child != NULL) {
3446 if (child == encrypted) {
3447 /* skip this part of tree */
3448 NEXT_PART_NOT_CHILD(child);
3452 if (child->type == MIMETYPE_MULTIPART) {
3453 /* get the actual content */
3454 child = procmime_mimeinfo_next(child);
3458 if (child == firsttext) {
3459 child = procmime_mimeinfo_next(child);
3463 outfile = procmime_get_tmp_file_name(child);
3464 if ((err = procmime_get_part(outfile, child)) < 0)
3465 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3467 gchar *content_type;
3469 content_type = procmime_get_content_type_str(child->type, child->subtype);
3471 /* if we meet a pgp signature, we don't attach it, but
3472 * we force signing. */
3473 if ((strcmp(content_type, "application/pgp-signature") &&
3474 strcmp(content_type, "application/pkcs7-signature") &&
3475 strcmp(content_type, "application/x-pkcs7-signature"))
3476 || compose->mode == COMPOSE_REDIRECT) {
3477 partname = procmime_mimeinfo_get_parameter(child, "filename");
3478 if (partname == NULL)
3479 partname = procmime_mimeinfo_get_parameter(child, "name");
3480 if (partname == NULL)
3482 compose_attach_append(compose, outfile,
3483 partname, content_type);
3485 compose_force_signing(compose, compose->account);
3487 g_free(content_type);
3490 NEXT_PART_NOT_CHILD(child);
3492 procmime_mimeinfo_free_all(mimeinfo);
3495 #undef NEXT_PART_NOT_CHILD
3500 WAIT_FOR_INDENT_CHAR,
3501 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3504 /* return indent length, we allow:
3505 indent characters followed by indent characters or spaces/tabs,
3506 alphabets and numbers immediately followed by indent characters,
3507 and the repeating sequences of the above
3508 If quote ends with multiple spaces, only the first one is included. */
3509 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3510 const GtkTextIter *start, gint *len)
3512 GtkTextIter iter = *start;
3516 IndentState state = WAIT_FOR_INDENT_CHAR;
3519 gint alnum_count = 0;
3520 gint space_count = 0;
3523 if (prefs_common.quote_chars == NULL) {
3527 while (!gtk_text_iter_ends_line(&iter)) {
3528 wc = gtk_text_iter_get_char(&iter);
3529 if (g_unichar_iswide(wc))
3531 clen = g_unichar_to_utf8(wc, ch);
3535 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3536 is_space = g_unichar_isspace(wc);
3538 if (state == WAIT_FOR_INDENT_CHAR) {
3539 if (!is_indent && !g_unichar_isalnum(wc))
3542 quote_len += alnum_count + space_count + 1;
3543 alnum_count = space_count = 0;
3544 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3547 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3548 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3552 else if (is_indent) {
3553 quote_len += alnum_count + space_count + 1;
3554 alnum_count = space_count = 0;
3557 state = WAIT_FOR_INDENT_CHAR;
3561 gtk_text_iter_forward_char(&iter);
3564 if (quote_len > 0 && space_count > 0)
3570 if (quote_len > 0) {
3572 gtk_text_iter_forward_chars(&iter, quote_len);
3573 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3579 /* return TRUE if the line is itemized */
3580 static gboolean compose_is_itemized(GtkTextBuffer *buffer,
3581 const GtkTextIter *start)
3583 GtkTextIter iter = *start;
3588 if (gtk_text_iter_ends_line(&iter))
3592 wc = gtk_text_iter_get_char(&iter);
3593 if (!g_unichar_isspace(wc))
3595 gtk_text_iter_forward_char(&iter);
3596 if (gtk_text_iter_ends_line(&iter))
3600 clen = g_unichar_to_utf8(wc, ch);
3604 if (!strchr("*-+", ch[0]))
3607 gtk_text_iter_forward_char(&iter);
3608 if (gtk_text_iter_ends_line(&iter))
3610 wc = gtk_text_iter_get_char(&iter);
3611 if (g_unichar_isspace(wc))
3617 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3618 const GtkTextIter *start,
3619 GtkTextIter *break_pos,
3623 GtkTextIter iter = *start, line_end = *start;
3624 PangoLogAttr *attrs;
3631 gboolean can_break = FALSE;
3632 gboolean do_break = FALSE;
3633 gboolean was_white = FALSE;
3634 gboolean prev_dont_break = FALSE;
3636 gtk_text_iter_forward_to_line_end(&line_end);
3637 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3638 len = g_utf8_strlen(str, -1);
3642 g_warning("compose_get_line_break_pos: len = 0!\n");
3646 /* g_print("breaking line: %d: %s (len = %d)\n",
3647 gtk_text_iter_get_line(&iter), str, len); */
3649 attrs = g_new(PangoLogAttr, len + 1);
3651 pango_default_break(str, -1, NULL, attrs, len + 1);
3655 /* skip quote and leading spaces */
3656 for (i = 0; *p != '\0' && i < len; i++) {
3659 wc = g_utf8_get_char(p);
3660 if (i >= quote_len && !g_unichar_isspace(wc))
3662 if (g_unichar_iswide(wc))
3664 else if (*p == '\t')
3668 p = g_utf8_next_char(p);
3671 for (; *p != '\0' && i < len; i++) {
3672 PangoLogAttr *attr = attrs + i;
3676 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3679 was_white = attr->is_white;
3681 /* don't wrap URI */
3682 if ((uri_len = get_uri_len(p)) > 0) {
3684 if (pos > 0 && col > max_col) {
3694 wc = g_utf8_get_char(p);
3695 if (g_unichar_iswide(wc)) {
3697 if (prev_dont_break && can_break && attr->is_line_break)
3699 } else if (*p == '\t')
3703 if (pos > 0 && col > max_col) {