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 GtkItemFactoryEntry compose_popup_entries[] =
553 {N_("/_Add..."), NULL, compose_attach_cb, 0, NULL},
554 {N_("/_Remove"), NULL, compose_attach_remove_selected, 0, NULL},
555 {"/---", NULL, NULL, 0, "<Separator>"},
556 {N_("/_Properties..."), NULL, compose_attach_property, 0, NULL}
559 static GtkItemFactoryEntry compose_entries[] =
561 {N_("/_Message"), NULL, NULL, 0, "<Branch>"},
562 {N_("/_Message/S_end"), "<control>Return",
563 compose_send_cb, 0, NULL},
564 {N_("/_Message/Send _later"), "<shift><control>S",
565 compose_send_later_cb, 0, NULL},
566 {N_("/_Message/---"), NULL, NULL, 0, "<Separator>"},
567 {N_("/_Message/_Attach file"), "<control>M", compose_attach_cb, 0, NULL},
568 {N_("/_Message/_Insert file"), "<control>I", compose_insert_file_cb, 0, NULL},
569 {N_("/_Message/Insert si_gnature"), "<control>G", compose_insert_sig_cb, 0, NULL},
570 {N_("/_Message/---"), NULL, NULL, 0, "<Separator>"},
571 {N_("/_Message/_Save"),
572 "<control>S", compose_draft_cb, COMPOSE_KEEP_EDITING, NULL},
573 {N_("/_Message/---"), NULL, NULL, 0, "<Separator>"},
574 {N_("/_Message/_Close"), "<control>W", compose_close_cb, 0, NULL},
576 {N_("/_Edit"), NULL, NULL, 0, "<Branch>"},
577 {N_("/_Edit/_Undo"), "<control>Z", compose_undo_cb, 0, NULL},
578 {N_("/_Edit/_Redo"), "<control>Y", compose_redo_cb, 0, NULL},
579 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
580 {N_("/_Edit/Cu_t"), "<control>X", compose_cut_cb, 0, NULL},
581 {N_("/_Edit/_Copy"), "<control>C", compose_copy_cb, 0, NULL},
582 {N_("/_Edit/_Paste"), "<control>V", compose_paste_cb, 0, NULL},
583 {N_("/_Edit/Special paste"), NULL, NULL, 0, "<Branch>"},
584 {N_("/_Edit/Special paste/as _quotation"),
585 NULL, compose_paste_as_quote_cb, 0, NULL},
586 {N_("/_Edit/Special paste/_wrapped"),
587 NULL, compose_paste_wrap_cb, 0, NULL},
588 {N_("/_Edit/Special paste/_unwrapped"),
589 NULL, compose_paste_no_wrap_cb, 0, NULL},
590 {N_("/_Edit/Select _all"), "<control>A", compose_allsel_cb, 0, NULL},
591 {N_("/_Edit/A_dvanced"), NULL, NULL, 0, "<Branch>"},
592 {N_("/_Edit/A_dvanced/Move a character backward"),
594 compose_advanced_action_cb,
595 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
597 {N_("/_Edit/A_dvanced/Move a character forward"),
599 compose_advanced_action_cb,
600 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
602 {N_("/_Edit/A_dvanced/Move a word backward"),
604 compose_advanced_action_cb,
605 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
607 {N_("/_Edit/A_dvanced/Move a word forward"),
609 compose_advanced_action_cb,
610 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
612 {N_("/_Edit/A_dvanced/Move to beginning of line"),
613 NULL, /* "<control>A" */
614 compose_advanced_action_cb,
615 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
617 {N_("/_Edit/A_dvanced/Move to end of line"),
619 compose_advanced_action_cb,
620 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
622 {N_("/_Edit/A_dvanced/Move to previous line"),
624 compose_advanced_action_cb,
625 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
627 {N_("/_Edit/A_dvanced/Move to next line"),
629 compose_advanced_action_cb,
630 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
632 {N_("/_Edit/A_dvanced/Delete a character backward"),
634 compose_advanced_action_cb,
635 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
637 {N_("/_Edit/A_dvanced/Delete a character forward"),
639 compose_advanced_action_cb,
640 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
642 {N_("/_Edit/A_dvanced/Delete a word backward"),
643 NULL, /* "<control>W" */
644 compose_advanced_action_cb,
645 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
647 {N_("/_Edit/A_dvanced/Delete a word forward"),
648 NULL, /* "<alt>D", */
649 compose_advanced_action_cb,
650 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
652 {N_("/_Edit/A_dvanced/Delete line"),
654 compose_advanced_action_cb,
655 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
657 {N_("/_Edit/A_dvanced/Delete entire line"),
659 compose_advanced_action_cb,
660 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE_N,
662 {N_("/_Edit/A_dvanced/Delete to end of line"),
664 compose_advanced_action_cb,
665 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END,
667 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
669 "<control>F", compose_find_cb, 0, NULL},
670 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
671 {N_("/_Edit/_Wrap current paragraph"),
672 "<control>L", compose_wrap_cb, 0, NULL},
673 {N_("/_Edit/Wrap all long _lines"),
674 "<control><alt>L", compose_wrap_cb, 1, NULL},
675 {N_("/_Edit/Aut_o wrapping"), "<shift><control>L", compose_toggle_autowrap_cb, 0, "<ToggleItem>"},
676 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
677 {N_("/_Edit/Edit with e_xternal editor"),
678 "<shift><control>X", compose_ext_editor_cb, 0, NULL},
680 {N_("/_Spelling"), NULL, NULL, 0, "<Branch>"},
681 {N_("/_Spelling/_Check all or check selection"),
682 NULL, compose_check_all, 0, NULL},
683 {N_("/_Spelling/_Highlight all misspelled words"),
684 NULL, compose_highlight_all, 0, NULL},
685 {N_("/_Spelling/Check _backwards misspelled word"),
686 NULL, compose_check_backwards , 0, NULL},
687 {N_("/_Spelling/_Forward to next misspelled word"),
688 NULL, compose_check_forwards_go, 0, NULL},
689 {N_("/_Spelling/---"), NULL, NULL, 0, "<Separator>"},
690 {N_("/_Spelling/Options"),
691 NULL, NULL, 0, "<Branch>"},
693 {N_("/_Options"), NULL, NULL, 0, "<Branch>"},
694 {N_("/_Options/Reply _mode"), NULL, NULL, 0, "<Branch>"},
695 {N_("/_Options/Reply _mode/_Normal"), NULL, compose_reply_change_mode, COMPOSE_REPLY, "<RadioItem>"},
696 {N_("/_Options/Reply _mode/_All"), NULL, compose_reply_change_mode, COMPOSE_REPLY_TO_ALL, "/Options/Reply mode/Normal"},
697 {N_("/_Options/Reply _mode/_Sender"), NULL, compose_reply_change_mode, COMPOSE_REPLY_TO_SENDER, "/Options/Reply mode/Normal"},
698 {N_("/_Options/Reply _mode/_Mailing-list"), NULL, compose_reply_change_mode, COMPOSE_REPLY_TO_LIST, "/Options/Reply mode/Normal"},
699 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
700 {N_("/_Options/Privacy _System"), NULL, NULL, 0, "<Branch>"},
701 {N_("/_Options/Privacy _System/None"), NULL, NULL, 0, "<RadioItem>"},
702 {N_("/_Options/Si_gn"), NULL, compose_toggle_sign_cb , 0, "<ToggleItem>"},
703 {N_("/_Options/_Encrypt"), NULL, compose_toggle_encrypt_cb, 0, "<ToggleItem>"},
704 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
705 {N_("/_Options/_Priority"), NULL, NULL, 0, "<Branch>"},
706 {N_("/_Options/Priority/_Highest"), NULL, compose_set_priority_cb, PRIORITY_HIGHEST, "<RadioItem>"},
707 {N_("/_Options/Priority/Hi_gh"), NULL, compose_set_priority_cb, PRIORITY_HIGH, "/Options/Priority/Highest"},
708 {N_("/_Options/Priority/_Normal"), NULL, compose_set_priority_cb, PRIORITY_NORMAL, "/Options/Priority/Highest"},
709 {N_("/_Options/Priority/Lo_w"), NULL, compose_set_priority_cb, PRIORITY_LOW, "/Options/Priority/Highest"},
710 {N_("/_Options/Priority/_Lowest"), NULL, compose_set_priority_cb, PRIORITY_LOWEST, "/Options/Priority/Highest"},
711 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
712 {N_("/_Options/_Request Return Receipt"), NULL, compose_toggle_return_receipt_cb, 0, "<ToggleItem>"},
713 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
714 {N_("/_Options/Remo_ve references"), NULL, compose_toggle_remove_refs_cb, 0, "<ToggleItem>"},
715 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
717 #define ENC_ACTION(action) \
718 NULL, compose_set_encoding_cb, action, \
719 "/Options/Character encoding/Automatic"
721 {N_("/_Options/Character _encoding"), NULL, NULL, 0, "<Branch>"},
722 {N_("/_Options/Character _encoding/_Automatic"),
723 NULL, compose_set_encoding_cb, C_AUTO, "<RadioItem>"},
724 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
726 {N_("/_Options/Character _encoding/7bit ASCII (US-ASC_II)"),
727 ENC_ACTION(C_US_ASCII)},
728 {N_("/_Options/Character _encoding/Unicode (_UTF-8)"),
729 ENC_ACTION(C_UTF_8)},
730 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
732 {N_("/_Options/Character _encoding/Western European"), NULL, NULL, 0, "<Branch>"},
733 {N_("/_Options/Character _encoding/Western European/ISO-8859-_1"),
734 ENC_ACTION(C_ISO_8859_1)},
735 {N_("/_Options/Character _encoding/Western European/ISO-8859-15"),
736 ENC_ACTION(C_ISO_8859_15)},
737 {N_("/_Options/Character _encoding/Western European/Windows-1252"),
738 ENC_ACTION(C_WINDOWS_1252)},
740 {N_("/_Options/Character _encoding/Central European (ISO-8859-_2)"),
741 ENC_ACTION(C_ISO_8859_2)},
743 {N_("/_Options/Character _encoding/Baltic"), NULL, NULL, 0, "<Branch>"},
744 {N_("/_Options/Character _encoding/Baltic/ISO-8859-13"),
745 ENC_ACTION(C_ISO_8859_13)},
746 {N_("/_Options/Character _encoding/Baltic/ISO-8859-_4"),
747 ENC_ACTION(C_ISO_8859_4)},
749 {N_("/_Options/Character _encoding/Greek (ISO-8859-_7)"),
750 ENC_ACTION(C_ISO_8859_7)},
752 {N_("/_Options/Character _encoding/Hebrew"), NULL, NULL, 0, "<Branch>"},
753 {N_("/_Options/Character _encoding/Hebrew/ISO-8859-_8"),
754 ENC_ACTION(C_ISO_8859_8)},
755 {N_("/_Options/Character _encoding/Hebrew/Windows-1255"),
756 ENC_ACTION(C_WINDOWS_1255)},
758 {N_("/_Options/Character _encoding/Arabic"), NULL, NULL, 0, "<Branch>"},
759 {N_("/_Options/Character _encoding/Arabic/ISO-8859-_6"),
760 ENC_ACTION(C_ISO_8859_6)},
761 {N_("/_Options/Character _encoding/Arabic/Windows-1256"),
762 ENC_ACTION(C_CP1256)},
764 {N_("/_Options/Character _encoding/Turkish (ISO-8859-_9)"),
765 ENC_ACTION(C_ISO_8859_9)},
767 {N_("/_Options/Character _encoding/Cyrillic"), NULL, NULL, 0, "<Branch>"},
768 {N_("/_Options/Character _encoding/Cyrillic/ISO-8859-_5"),
769 ENC_ACTION(C_ISO_8859_5)},
770 {N_("/_Options/Character _encoding/Cyrillic/KOI8-_R"),
771 ENC_ACTION(C_KOI8_R)},
772 {N_("/_Options/Character _encoding/Cyrillic/KOI8-U"),
773 ENC_ACTION(C_KOI8_U)},
774 {N_("/_Options/Character _encoding/Cyrillic/Windows-1251"),
775 ENC_ACTION(C_WINDOWS_1251)},
777 {N_("/_Options/Character _encoding/Japanese"), NULL, NULL, 0, "<Branch>"},
778 {N_("/_Options/Character _encoding/Japanese/ISO-2022-_JP"),
779 ENC_ACTION(C_ISO_2022_JP)},
780 {N_("/_Options/Character _encoding/Japanese/ISO-2022-JP-2"),
781 ENC_ACTION(C_ISO_2022_JP_2)},
782 {N_("/_Options/Character _encoding/Japanese/_EUC-JP"),
783 ENC_ACTION(C_EUC_JP)},
784 {N_("/_Options/Character _encoding/Japanese/_Shift__JIS"),
785 ENC_ACTION(C_SHIFT_JIS)},
787 {N_("/_Options/Character _encoding/Chinese"), NULL, NULL, 0, "<Branch>"},
788 {N_("/_Options/Character _encoding/Chinese/Simplified (_GB2312)"),
789 ENC_ACTION(C_GB2312)},
790 {N_("/_Options/Character _encoding/Chinese/Simplified (GBK)"),
792 {N_("/_Options/Character _encoding/Chinese/Traditional (_Big5)"),
794 {N_("/_Options/Character _encoding/Chinese/Traditional (EUC-_TW)"),
795 ENC_ACTION(C_EUC_TW)},
797 {N_("/_Options/Character _encoding/Korean"), NULL, NULL, 0, "<Branch>"},
798 {N_("/_Options/Character _encoding/Korean/EUC-_KR"),
799 ENC_ACTION(C_EUC_KR)},
800 {N_("/_Options/Character _encoding/Korean/ISO-2022-KR"),
801 ENC_ACTION(C_ISO_2022_KR)},
803 {N_("/_Options/Character _encoding/Thai"), NULL, NULL, 0, "<Branch>"},
804 {N_("/_Options/Character _encoding/Thai/TIS-620"),
805 ENC_ACTION(C_TIS_620)},
806 {N_("/_Options/Character _encoding/Thai/Windows-874"),
807 ENC_ACTION(C_WINDOWS_874)},
809 {N_("/_Tools"), NULL, NULL, 0, "<Branch>"},
810 {N_("/_Tools/Show _ruler"), NULL, compose_toggle_ruler_cb, 0, "<ToggleItem>"},
811 {N_("/_Tools/_Address book"), "<shift><control>A", compose_address_cb , 0, NULL},
812 {N_("/_Tools/_Template"), NULL, NULL, 0, "<Branch>"},
813 {N_("/_Tools/Actio_ns"), NULL, NULL, 0, "<Branch>"},
814 {N_("/_Help"), NULL, NULL, 0, "<Branch>"},
815 {N_("/_Help/_About"), NULL, about_show, 0, NULL}
818 static GtkTargetEntry compose_mime_types[] =
820 {"text/uri-list", 0, 0},
821 {"UTF8_STRING", 0, 0},
825 static gboolean compose_put_existing_to_front(MsgInfo *info)
827 GList *compose_list = compose_get_compose_list();
831 for (elem = compose_list; elem != NULL && elem->data != NULL;
833 Compose *c = (Compose*)elem->data;
835 if (!c->targetinfo || !c->targetinfo->msgid ||
839 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
840 gtkut_window_popup(c->window);
848 static GdkColor quote_color1 =
849 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
850 static GdkColor quote_color2 =
851 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
852 static GdkColor quote_color3 =
853 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
855 static GdkColor quote_bgcolor1 =
856 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
857 static GdkColor quote_bgcolor2 =
858 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
859 static GdkColor quote_bgcolor3 =
860 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
862 static GdkColor signature_color = {
869 static GdkColor uri_color = {
876 static void compose_create_tags(GtkTextView *text, Compose *compose)
878 GtkTextBuffer *buffer;
879 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
885 buffer = gtk_text_view_get_buffer(text);
887 if (prefs_common.enable_color) {
888 /* grab the quote colors, converting from an int to a GdkColor */
889 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
891 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
893 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
895 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
897 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
899 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
901 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
903 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
906 signature_color = quote_color1 = quote_color2 = quote_color3 =
907 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
910 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
911 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
912 "foreground-gdk", "e_color1,
913 "paragraph-background-gdk", "e_bgcolor1,
915 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
916 "foreground-gdk", "e_color2,
917 "paragraph-background-gdk", "e_bgcolor2,
919 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
920 "foreground-gdk", "e_color3,
921 "paragraph-background-gdk", "e_bgcolor3,
924 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
925 "foreground-gdk", "e_color1,
927 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
928 "foreground-gdk", "e_color2,
930 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
931 "foreground-gdk", "e_color3,
935 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
936 "foreground-gdk", &signature_color,
939 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
940 "foreground-gdk", &uri_color,
942 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
943 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
945 color[0] = quote_color1;
946 color[1] = quote_color2;
947 color[2] = quote_color3;
948 color[3] = quote_bgcolor1;
949 color[4] = quote_bgcolor2;
950 color[5] = quote_bgcolor3;
951 color[6] = signature_color;
952 color[7] = uri_color;
953 cmap = gdk_drawable_get_colormap(compose->window->window);
954 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
956 for (i = 0; i < 8; i++) {
957 if (success[i] == FALSE) {
960 g_warning("Compose: color allocation failed.\n");
961 style = gtk_widget_get_style(GTK_WIDGET(text));
962 quote_color1 = quote_color2 = quote_color3 =
963 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
964 signature_color = uri_color = black;
969 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
970 GPtrArray *attach_files)
972 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
975 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
977 return compose_generic_new(account, mailto, item, NULL, NULL);
980 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
982 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
985 #define SCROLL_TO_CURSOR(compose) { \
986 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
987 gtk_text_view_get_buffer( \
988 GTK_TEXT_VIEW(compose->text))); \
989 gtk_text_view_scroll_mark_onscreen( \
990 GTK_TEXT_VIEW(compose->text), \
994 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
995 GPtrArray *attach_files, GList *listAddress )
998 GtkTextView *textview;
999 GtkTextBuffer *textbuf;
1001 GtkItemFactory *ifactory;
1002 const gchar *subject_format = NULL;
1003 const gchar *body_format = NULL;
1005 if (item && item->prefs && item->prefs->enable_default_account)
1006 account = account_find_from_id(item->prefs->default_account);
1008 if (!account) account = cur_account;
1009 g_return_val_if_fail(account != NULL, NULL);
1011 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1013 ifactory = gtk_item_factory_from_widget(compose->menubar);
1015 compose->replyinfo = NULL;
1016 compose->fwdinfo = NULL;
1018 textview = GTK_TEXT_VIEW(compose->text);
1019 textbuf = gtk_text_view_get_buffer(textview);
1020 compose_create_tags(textview, compose);
1022 undo_block(compose->undostruct);
1024 compose_set_dictionaries_from_folder_prefs(compose, item);
1027 if (account->auto_sig)
1028 compose_insert_sig(compose, FALSE);
1029 gtk_text_buffer_get_start_iter(textbuf, &iter);
1030 gtk_text_buffer_place_cursor(textbuf, &iter);
1032 if (account->protocol != A_NNTP) {
1033 if (mailto && *mailto != '\0') {
1034 compose_entries_set(compose, mailto);
1036 } else if (item && item->prefs->enable_default_to) {
1037 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
1038 compose_entry_mark_default_to(compose, item->prefs->default_to);
1040 if (item && item->ret_rcpt) {
1041 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
1045 compose_entry_append(compose, mailto, COMPOSE_NEWSGROUPS);
1046 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1047 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1050 * CLAWS: just don't allow return receipt request, even if the user
1051 * may want to send an email. simple but foolproof.
1053 menu_set_sensitive(ifactory, "/Options/Request Return Receipt", FALSE);
1055 compose_add_field_list( compose, listAddress );
1057 if (item && item->prefs && item->prefs->compose_with_format) {
1058 subject_format = item->prefs->compose_subject_format;
1059 body_format = item->prefs->compose_body_format;
1060 } else if (account->compose_with_format) {
1061 subject_format = account->compose_subject_format;
1062 body_format = account->compose_body_format;
1063 } else if (prefs_common.compose_with_format) {
1064 subject_format = prefs_common.compose_subject_format;
1065 body_format = prefs_common.compose_body_format;
1068 if (subject_format || body_format) {
1069 MsgInfo* dummyinfo = NULL;
1072 && *subject_format != '\0' )
1074 gchar *subject = NULL;
1078 dummyinfo = compose_msginfo_new_from_compose(compose);
1080 /* decode \-escape sequences in the internal representation of the quote format */
1081 tmp = malloc(strlen(subject_format)+1);
1082 pref_get_unescaped_pref(tmp, subject_format);
1084 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1086 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account,
1087 compose->gtkaspell);
1089 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account);
1091 quote_fmt_scan_string(tmp);
1094 buf = quote_fmt_get_buffer();
1096 alertpanel_error(_("New message subject format error."));
1098 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1099 quote_fmt_reset_vartable();
1106 && *body_format != '\0' )
1109 GtkTextBuffer *buffer;
1110 GtkTextIter start, end;
1113 if ( dummyinfo == NULL )
1114 dummyinfo = compose_msginfo_new_from_compose(compose);
1116 text = GTK_TEXT_VIEW(compose->text);
1117 buffer = gtk_text_view_get_buffer(text);
1118 gtk_text_buffer_get_start_iter(buffer, &start);
1119 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1120 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1122 compose_quote_fmt(compose, dummyinfo,
1124 NULL, tmp, FALSE, TRUE,
1125 _("New message body format error at line %d."));
1126 quote_fmt_reset_vartable();
1131 procmsg_msginfo_free( dummyinfo );
1138 for (i = 0; i < attach_files->len; i++) {
1139 file = g_ptr_array_index(attach_files, i);
1140 compose_attach_append(compose, file, file, NULL);
1144 compose_show_first_last_header(compose, TRUE);
1146 /* Set save folder */
1147 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1148 gchar *folderidentifier;
1150 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1151 folderidentifier = folder_item_get_identifier(item);
1152 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1153 g_free(folderidentifier);
1156 gtk_widget_grab_focus(compose->header_last->entry);
1158 undo_unblock(compose->undostruct);
1160 if (prefs_common.auto_exteditor)
1161 compose_exec_ext_editor(compose);
1163 compose->draft_timeout_tag = -1;
1164 SCROLL_TO_CURSOR(compose);
1166 compose->modified = FALSE;
1167 compose_set_title(compose);
1171 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1172 gboolean override_pref)
1174 gchar *privacy = NULL;
1176 g_return_if_fail(compose != NULL);
1177 g_return_if_fail(account != NULL);
1179 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1182 if (account->default_privacy_system
1183 && strlen(account->default_privacy_system)) {
1184 privacy = account->default_privacy_system;
1186 GSList *privacy_avail = privacy_get_system_ids();
1187 if (privacy_avail && g_slist_length(privacy_avail)) {
1188 privacy = (gchar *)(privacy_avail->data);
1191 if (privacy != NULL) {
1192 if (compose->privacy_system == NULL)
1193 compose->privacy_system = g_strdup(privacy);
1194 compose_update_privacy_system_menu_item(compose, FALSE);
1195 compose_use_encryption(compose, TRUE);
1199 static void compose_force_signing(Compose *compose, PrefsAccount *account)
1201 gchar *privacy = NULL;
1203 if (account->default_privacy_system
1204 && strlen(account->default_privacy_system)) {
1205 privacy = account->default_privacy_system;
1207 GSList *privacy_avail = privacy_get_system_ids();
1208 if (privacy_avail && g_slist_length(privacy_avail)) {
1209 privacy = (gchar *)(privacy_avail->data);
1212 if (privacy != NULL) {
1213 if (compose->privacy_system == NULL)
1214 compose->privacy_system = g_strdup(privacy);
1215 compose_update_privacy_system_menu_item(compose, FALSE);
1216 compose_use_signing(compose, TRUE);
1220 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1224 Compose *compose = NULL;
1225 GtkItemFactory *ifactory = NULL;
1227 g_return_val_if_fail(msginfo_list != NULL, NULL);
1229 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1230 g_return_val_if_fail(msginfo != NULL, NULL);
1232 list_len = g_slist_length(msginfo_list);
1236 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1237 FALSE, prefs_common.default_reply_list, FALSE, body);
1239 case COMPOSE_REPLY_WITH_QUOTE:
1240 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1241 FALSE, prefs_common.default_reply_list, FALSE, body);
1243 case COMPOSE_REPLY_WITHOUT_QUOTE:
1244 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1245 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1247 case COMPOSE_REPLY_TO_SENDER:
1248 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1249 FALSE, FALSE, TRUE, body);
1251 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1252 compose = compose_followup_and_reply_to(msginfo,
1253 COMPOSE_QUOTE_CHECK,
1254 FALSE, FALSE, body);
1256 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1257 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1258 FALSE, FALSE, TRUE, body);
1260 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1261 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1262 FALSE, FALSE, TRUE, NULL);
1264 case COMPOSE_REPLY_TO_ALL:
1265 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1266 TRUE, FALSE, FALSE, body);
1268 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1269 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1270 TRUE, FALSE, FALSE, body);
1272 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1273 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1274 TRUE, FALSE, FALSE, NULL);
1276 case COMPOSE_REPLY_TO_LIST:
1277 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1278 FALSE, TRUE, FALSE, body);
1280 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1281 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1282 FALSE, TRUE, FALSE, body);
1284 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1285 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1286 FALSE, TRUE, FALSE, NULL);
1288 case COMPOSE_FORWARD:
1289 if (prefs_common.forward_as_attachment) {
1290 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1293 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1297 case COMPOSE_FORWARD_INLINE:
1298 /* check if we reply to more than one Message */
1299 if (list_len == 1) {
1300 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1303 /* more messages FALL THROUGH */
1304 case COMPOSE_FORWARD_AS_ATTACH:
1305 compose = compose_forward_multiple(NULL, msginfo_list);
1307 case COMPOSE_REDIRECT:
1308 compose = compose_redirect(NULL, msginfo, FALSE);
1311 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1314 if (compose == NULL) {
1315 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1318 ifactory = gtk_item_factory_from_widget(compose->menubar);
1320 compose->rmode = mode;
1321 switch (compose->rmode) {
1323 case COMPOSE_REPLY_WITH_QUOTE:
1324 case COMPOSE_REPLY_WITHOUT_QUOTE:
1325 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1326 debug_print("reply mode Normal\n");
1327 menu_set_active(ifactory, "/Options/Reply mode/Normal", TRUE);
1328 compose_reply_change_mode(compose, COMPOSE_REPLY, NULL); /* force update */
1330 case COMPOSE_REPLY_TO_SENDER:
1331 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1332 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1333 debug_print("reply mode Sender\n");
1334 menu_set_active(ifactory, "/Options/Reply mode/Sender", TRUE);
1336 case COMPOSE_REPLY_TO_ALL:
1337 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1338 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1339 debug_print("reply mode All\n");
1340 menu_set_active(ifactory, "/Options/Reply mode/All", TRUE);
1342 case COMPOSE_REPLY_TO_LIST:
1343 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1344 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1345 debug_print("reply mode List\n");
1346 menu_set_active(ifactory, "/Options/Reply mode/Mailing-list", TRUE);
1354 static Compose *compose_reply(MsgInfo *msginfo,
1355 ComposeQuoteMode quote_mode,
1361 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1362 to_sender, FALSE, body);
1365 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1366 ComposeQuoteMode quote_mode,
1371 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1372 to_sender, TRUE, body);
1375 static void compose_extract_original_charset(Compose *compose)
1377 MsgInfo *info = NULL;
1378 if (compose->replyinfo) {
1379 info = compose->replyinfo;
1380 } else if (compose->fwdinfo) {
1381 info = compose->fwdinfo;
1382 } else if (compose->targetinfo) {
1383 info = compose->targetinfo;
1386 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1387 MimeInfo *partinfo = mimeinfo;
1388 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1389 partinfo = procmime_mimeinfo_next(partinfo);
1391 compose->orig_charset =
1392 g_strdup(procmime_mimeinfo_get_parameter(
1393 partinfo, "charset"));
1395 procmime_mimeinfo_free_all(mimeinfo);
1399 #define SIGNAL_BLOCK(buffer) { \
1400 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1401 G_CALLBACK(compose_changed_cb), \
1403 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1404 G_CALLBACK(text_inserted), \
1408 #define SIGNAL_UNBLOCK(buffer) { \
1409 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1410 G_CALLBACK(compose_changed_cb), \
1412 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1413 G_CALLBACK(text_inserted), \
1417 static Compose *compose_generic_reply(MsgInfo *msginfo,
1418 ComposeQuoteMode quote_mode,
1419 gboolean to_all, gboolean to_ml,
1421 gboolean followup_and_reply_to,
1424 GtkItemFactory *ifactory;
1426 PrefsAccount *account = NULL;
1427 GtkTextView *textview;
1428 GtkTextBuffer *textbuf;
1429 gboolean quote = FALSE;
1430 const gchar *qmark = NULL;
1431 const gchar *body_fmt = NULL;
1433 g_return_val_if_fail(msginfo != NULL, NULL);
1434 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1436 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1438 g_return_val_if_fail(account != NULL, NULL);
1440 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1442 compose->updating = TRUE;
1444 ifactory = gtk_item_factory_from_widget(compose->menubar);
1446 menu_set_active(ifactory, "/Options/Remove references", FALSE);
1447 menu_set_sensitive(ifactory, "/Options/Remove references", TRUE);
1449 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1450 if (!compose->replyinfo)
1451 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1453 compose_extract_original_charset(compose);
1455 if (msginfo->folder && msginfo->folder->ret_rcpt)
1456 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
1458 /* Set save folder */
1459 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1460 gchar *folderidentifier;
1462 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1463 folderidentifier = folder_item_get_identifier(msginfo->folder);
1464 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1465 g_free(folderidentifier);
1468 if (compose_parse_header(compose, msginfo) < 0) return NULL;
1470 textview = (GTK_TEXT_VIEW(compose->text));
1471 textbuf = gtk_text_view_get_buffer(textview);
1472 compose_create_tags(textview, compose);
1474 undo_block(compose->undostruct);
1476 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1479 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1480 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1481 /* use the reply format of folder (if enabled), or the account's one
1482 (if enabled) or fallback to the global reply format, which is always
1483 enabled (even if empty), and use the relevant quotemark */
1485 if (msginfo->folder && msginfo->folder->prefs &&
1486 msginfo->folder->prefs->reply_with_format) {
1487 qmark = msginfo->folder->prefs->reply_quotemark;
1488 body_fmt = msginfo->folder->prefs->reply_body_format;
1490 } else if (account->reply_with_format) {
1491 qmark = account->reply_quotemark;
1492 body_fmt = account->reply_body_format;
1495 qmark = prefs_common.quotemark;
1496 body_fmt = prefs_common.quotefmt;
1501 /* empty quotemark is not allowed */
1502 if (qmark == NULL || *qmark == '\0')
1504 compose_quote_fmt(compose, compose->replyinfo,
1505 body_fmt, qmark, body, FALSE, TRUE,
1506 _("Message reply format error at line %d."));
1507 quote_fmt_reset_vartable();
1510 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1511 compose_force_encryption(compose, account, FALSE);
1514 SIGNAL_BLOCK(textbuf);
1516 if (account->auto_sig)
1517 compose_insert_sig(compose, FALSE);
1519 compose_wrap_all(compose);
1521 SIGNAL_UNBLOCK(textbuf);
1523 gtk_widget_grab_focus(compose->text);
1525 undo_unblock(compose->undostruct);
1527 if (prefs_common.auto_exteditor)
1528 compose_exec_ext_editor(compose);
1530 compose->modified = FALSE;
1531 compose_set_title(compose);
1533 compose->updating = FALSE;
1534 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1535 SCROLL_TO_CURSOR(compose);
1537 if (compose->deferred_destroy) {
1538 compose_destroy(compose);
1545 #define INSERT_FW_HEADER(var, hdr) \
1546 if (msginfo->var && *msginfo->var) { \
1547 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1548 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1549 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1552 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1553 gboolean as_attach, const gchar *body,
1554 gboolean no_extedit,
1558 GtkTextView *textview;
1559 GtkTextBuffer *textbuf;
1562 g_return_val_if_fail(msginfo != NULL, NULL);
1563 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1566 !(account = compose_guess_forward_account_from_msginfo
1568 account = cur_account;
1570 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1572 compose->updating = TRUE;
1573 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1574 if (!compose->fwdinfo)
1575 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1577 compose_extract_original_charset(compose);
1579 if (msginfo->subject && *msginfo->subject) {
1580 gchar *buf, *buf2, *p;
1582 buf = p = g_strdup(msginfo->subject);
1583 p += subject_get_prefix_length(p);
1584 memmove(buf, p, strlen(p) + 1);
1586 buf2 = g_strdup_printf("Fw: %s", buf);
1587 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1593 textview = GTK_TEXT_VIEW(compose->text);
1594 textbuf = gtk_text_view_get_buffer(textview);
1595 compose_create_tags(textview, compose);
1597 undo_block(compose->undostruct);
1601 msgfile = procmsg_get_message_file(msginfo);
1602 if (!is_file_exist(msgfile))
1603 g_warning("%s: file not exist\n", msgfile);
1605 compose_attach_append(compose, msgfile, msgfile,
1610 const gchar *qmark = NULL;
1611 const gchar *body_fmt = prefs_common.fw_quotefmt;
1612 MsgInfo *full_msginfo;
1614 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1616 full_msginfo = procmsg_msginfo_copy(msginfo);
1618 /* use the forward format of folder (if enabled), or the account's one
1619 (if enabled) or fallback to the global forward format, which is always
1620 enabled (even if empty), and use the relevant quotemark */
1621 if (msginfo->folder && msginfo->folder->prefs &&
1622 msginfo->folder->prefs->forward_with_format) {
1623 qmark = msginfo->folder->prefs->forward_quotemark;
1624 body_fmt = msginfo->folder->prefs->forward_body_format;
1626 } else if (account->forward_with_format) {
1627 qmark = account->forward_quotemark;
1628 body_fmt = account->forward_body_format;
1631 qmark = prefs_common.fw_quotemark;
1632 body_fmt = prefs_common.fw_quotefmt;
1635 /* empty quotemark is not allowed */
1636 if (qmark == NULL || *qmark == '\0')
1639 compose_quote_fmt(compose, full_msginfo,
1640 body_fmt, qmark, body, FALSE, TRUE,
1641 _("Message forward format error at line %d."));
1642 quote_fmt_reset_vartable();
1643 compose_attach_parts(compose, msginfo);
1645 procmsg_msginfo_free(full_msginfo);
1648 SIGNAL_BLOCK(textbuf);
1650 if (account->auto_sig)
1651 compose_insert_sig(compose, FALSE);
1653 compose_wrap_all(compose);
1655 SIGNAL_UNBLOCK(textbuf);
1657 gtk_text_buffer_get_start_iter(textbuf, &iter);
1658 gtk_text_buffer_place_cursor(textbuf, &iter);
1660 gtk_widget_grab_focus(compose->header_last->entry);
1662 if (!no_extedit && prefs_common.auto_exteditor)
1663 compose_exec_ext_editor(compose);
1666 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1667 gchar *folderidentifier;
1669 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1670 folderidentifier = folder_item_get_identifier(msginfo->folder);
1671 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1672 g_free(folderidentifier);
1675 undo_unblock(compose->undostruct);
1677 compose->modified = FALSE;
1678 compose_set_title(compose);
1680 compose->updating = FALSE;
1681 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1682 SCROLL_TO_CURSOR(compose);
1684 if (compose->deferred_destroy) {
1685 compose_destroy(compose);
1692 #undef INSERT_FW_HEADER
1694 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1697 GtkTextView *textview;
1698 GtkTextBuffer *textbuf;
1702 gboolean single_mail = TRUE;
1704 g_return_val_if_fail(msginfo_list != NULL, NULL);
1706 if (g_slist_length(msginfo_list) > 1)
1707 single_mail = FALSE;
1709 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1710 if (((MsgInfo *)msginfo->data)->folder == NULL)
1713 /* guess account from first selected message */
1715 !(account = compose_guess_forward_account_from_msginfo
1716 (msginfo_list->data)))
1717 account = cur_account;
1719 g_return_val_if_fail(account != NULL, NULL);
1721 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1722 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1723 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1726 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1728 compose->updating = TRUE;
1730 textview = GTK_TEXT_VIEW(compose->text);
1731 textbuf = gtk_text_view_get_buffer(textview);
1732 compose_create_tags(textview, compose);
1734 undo_block(compose->undostruct);
1735 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1736 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1738 if (!is_file_exist(msgfile))
1739 g_warning("%s: file not exist\n", msgfile);
1741 compose_attach_append(compose, msgfile, msgfile,
1747 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1748 if (info->subject && *info->subject) {
1749 gchar *buf, *buf2, *p;
1751 buf = p = g_strdup(info->subject);
1752 p += subject_get_prefix_length(p);
1753 memmove(buf, p, strlen(p) + 1);
1755 buf2 = g_strdup_printf("Fw: %s", buf);
1756 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1762 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1763 _("Fw: multiple emails"));
1766 SIGNAL_BLOCK(textbuf);
1768 if (account->auto_sig)
1769 compose_insert_sig(compose, FALSE);
1771 compose_wrap_all(compose);
1773 SIGNAL_UNBLOCK(textbuf);
1775 gtk_text_buffer_get_start_iter(textbuf, &iter);
1776 gtk_text_buffer_place_cursor(textbuf, &iter);
1778 gtk_widget_grab_focus(compose->header_last->entry);
1779 undo_unblock(compose->undostruct);
1780 compose->modified = FALSE;
1781 compose_set_title(compose);
1783 compose->updating = FALSE;
1784 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1785 SCROLL_TO_CURSOR(compose);
1787 if (compose->deferred_destroy) {
1788 compose_destroy(compose);
1795 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1797 GtkTextIter start = *iter;
1798 GtkTextIter end_iter;
1799 int start_pos = gtk_text_iter_get_offset(&start);
1801 if (!compose->account->sig_sep)
1804 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1805 start_pos+strlen(compose->account->sig_sep));
1807 /* check sig separator */
1808 str = gtk_text_iter_get_text(&start, &end_iter);
1809 if (!strcmp(str, compose->account->sig_sep)) {
1811 /* check end of line (\n) */
1812 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1813 start_pos+strlen(compose->account->sig_sep));
1814 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1815 start_pos+strlen(compose->account->sig_sep)+1);
1816 tmp = gtk_text_iter_get_text(&start, &end_iter);
1817 if (!strcmp(tmp,"\n")) {
1829 static void compose_colorize_signature(Compose *compose)
1831 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1833 GtkTextIter end_iter;
1834 gtk_text_buffer_get_start_iter(buffer, &iter);
1835 while (gtk_text_iter_forward_line(&iter))
1836 if (compose_is_sig_separator(compose, buffer, &iter)) {
1837 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1838 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1842 #define BLOCK_WRAP() { \
1843 prev_autowrap = compose->autowrap; \
1844 buffer = gtk_text_view_get_buffer( \
1845 GTK_TEXT_VIEW(compose->text)); \
1846 compose->autowrap = FALSE; \
1848 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1849 G_CALLBACK(compose_changed_cb), \
1851 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1852 G_CALLBACK(text_inserted), \
1855 #define UNBLOCK_WRAP() { \
1856 compose->autowrap = prev_autowrap; \
1857 if (compose->autowrap) { \
1858 gint old = compose->draft_timeout_tag; \
1859 compose->draft_timeout_tag = -2; \
1860 compose_wrap_all(compose); \
1861 compose->draft_timeout_tag = old; \
1864 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1865 G_CALLBACK(compose_changed_cb), \
1867 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1868 G_CALLBACK(text_inserted), \
1872 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
1874 Compose *compose = NULL;
1875 PrefsAccount *account = NULL;
1876 GtkTextView *textview;
1877 GtkTextBuffer *textbuf;
1881 gchar buf[BUFFSIZE];
1882 gboolean use_signing = FALSE;
1883 gboolean use_encryption = FALSE;
1884 gchar *privacy_system = NULL;
1885 int priority = PRIORITY_NORMAL;
1886 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
1888 g_return_val_if_fail(msginfo != NULL, NULL);
1889 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1891 if (compose_put_existing_to_front(msginfo)) {
1895 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
1896 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
1897 gchar queueheader_buf[BUFFSIZE];
1900 /* Select Account from queue headers */
1901 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1902 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
1903 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
1904 account = account_find_from_id(id);
1906 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1907 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
1908 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
1909 account = account_find_from_id(id);
1911 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1912 sizeof(queueheader_buf), "NAID:")) {
1913 id = atoi(&queueheader_buf[strlen("NAID:")]);
1914 account = account_find_from_id(id);
1916 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1917 sizeof(queueheader_buf), "MAID:")) {
1918 id = atoi(&queueheader_buf[strlen("MAID:")]);
1919 account = account_find_from_id(id);
1921 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1922 sizeof(queueheader_buf), "S:")) {
1923 account = account_find_from_address(queueheader_buf);
1925 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1926 sizeof(queueheader_buf), "X-Claws-Sign:")) {
1927 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
1928 use_signing = param;
1931 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1932 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
1933 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
1934 use_signing = param;
1937 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1938 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
1939 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
1940 use_encryption = param;
1942 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1943 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
1944 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
1945 use_encryption = param;
1947 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1948 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
1949 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
1951 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1952 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
1953 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
1955 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1956 sizeof(queueheader_buf), "X-Priority: ")) {
1957 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
1960 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1961 sizeof(queueheader_buf), "RMID:")) {
1962 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
1963 if (tokens[0] && tokens[1] && tokens[2]) {
1964 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
1965 if (orig_item != NULL) {
1966 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
1971 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1972 sizeof(queueheader_buf), "FMID:")) {
1973 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
1974 if (tokens[0] && tokens[1] && tokens[2]) {
1975 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
1976 if (orig_item != NULL) {
1977 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
1983 account = msginfo->folder->folder->account;
1986 if (!account && prefs_common.reedit_account_autosel) {
1987 gchar from[BUFFSIZE];
1988 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
1989 extract_address(from);
1990 account = account_find_from_address(from);
1994 account = cur_account;
1996 g_return_val_if_fail(account != NULL, NULL);
1998 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2000 compose->replyinfo = replyinfo;
2001 compose->fwdinfo = fwdinfo;
2003 compose->updating = TRUE;
2004 compose->priority = priority;
2006 if (privacy_system != NULL) {
2007 compose->privacy_system = privacy_system;
2008 compose_use_signing(compose, use_signing);
2009 compose_use_encryption(compose, use_encryption);
2010 compose_update_privacy_system_menu_item(compose, FALSE);
2012 activate_privacy_system(compose, account, FALSE);
2015 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2017 compose_extract_original_charset(compose);
2019 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2020 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2021 gchar queueheader_buf[BUFFSIZE];
2023 /* Set message save folder */
2024 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2027 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2028 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
2029 gtk_editable_insert_text(GTK_EDITABLE(compose->savemsg_entry), &queueheader_buf[4], strlen(&queueheader_buf[4]), &startpos);
2031 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2032 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2034 GtkItemFactory *ifactory;
2035 ifactory = gtk_item_factory_from_widget(compose->menubar);
2036 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
2041 if (compose_parse_header(compose, msginfo) < 0) {
2042 compose->updating = FALSE;
2043 compose_destroy(compose);
2046 compose_reedit_set_entry(compose, msginfo);
2048 textview = GTK_TEXT_VIEW(compose->text);
2049 textbuf = gtk_text_view_get_buffer(textview);
2050 compose_create_tags(textview, compose);
2052 mark = gtk_text_buffer_get_insert(textbuf);
2053 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2055 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2056 G_CALLBACK(compose_changed_cb),
2059 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2060 fp = procmime_get_first_encrypted_text_content(msginfo);
2062 compose_force_encryption(compose, account, TRUE);
2065 fp = procmime_get_first_text_content(msginfo);
2068 g_warning("Can't get text part\n");
2072 gboolean prev_autowrap = compose->autowrap;
2073 GtkTextBuffer *buffer = textbuf;
2075 while (fgets(buf, sizeof(buf), fp) != NULL) {
2077 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2083 compose_attach_parts(compose, msginfo);
2085 compose_colorize_signature(compose);
2087 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2088 G_CALLBACK(compose_changed_cb),
2091 gtk_widget_grab_focus(compose->text);
2093 if (prefs_common.auto_exteditor) {
2094 compose_exec_ext_editor(compose);
2096 compose->modified = FALSE;
2097 compose_set_title(compose);
2099 compose->updating = FALSE;
2100 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2101 SCROLL_TO_CURSOR(compose);
2103 if (compose->deferred_destroy) {
2104 compose_destroy(compose);
2108 compose->sig_str = compose_get_signature_str(compose);
2113 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2118 GtkItemFactory *ifactory;
2121 g_return_val_if_fail(msginfo != NULL, NULL);
2124 account = account_get_reply_account(msginfo,
2125 prefs_common.reply_account_autosel);
2126 g_return_val_if_fail(account != NULL, NULL);
2128 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2130 compose->updating = TRUE;
2132 ifactory = gtk_item_factory_from_widget(compose->menubar);
2133 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2134 compose->replyinfo = NULL;
2135 compose->fwdinfo = NULL;
2137 compose_show_first_last_header(compose, TRUE);
2139 gtk_widget_grab_focus(compose->header_last->entry);
2141 filename = procmsg_get_message_file(msginfo);
2143 if (filename == NULL) {
2144 compose->updating = FALSE;
2145 compose_destroy(compose);
2150 compose->redirect_filename = filename;
2152 /* Set save folder */
2153 item = msginfo->folder;
2154 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2155 gchar *folderidentifier;
2157 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2158 folderidentifier = folder_item_get_identifier(item);
2159 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
2160 g_free(folderidentifier);
2163 compose_attach_parts(compose, msginfo);
2165 if (msginfo->subject)
2166 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2168 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2170 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2171 _("Message redirect format error at line %d."));
2172 quote_fmt_reset_vartable();
2173 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2175 compose_colorize_signature(compose);
2177 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
2178 menu_set_sensitive(ifactory, "/Add...", FALSE);
2179 menu_set_sensitive(ifactory, "/Remove", FALSE);
2180 menu_set_sensitive(ifactory, "/Properties...", FALSE);
2182 ifactory = gtk_item_factory_from_widget(compose->menubar);
2183 menu_set_sensitive(ifactory, "/Message/Save", FALSE);
2184 menu_set_sensitive(ifactory, "/Message/Insert file", FALSE);
2185 menu_set_sensitive(ifactory, "/Message/Attach file", FALSE);
2186 menu_set_sensitive(ifactory, "/Message/Insert signature", FALSE);
2187 menu_set_sensitive(ifactory, "/Edit", FALSE);
2188 menu_set_sensitive(ifactory, "/Options", FALSE);
2189 menu_set_sensitive(ifactory, "/Tools/Show ruler", FALSE);
2190 menu_set_sensitive(ifactory, "/Tools/Actions", FALSE);
2192 if (compose->toolbar->draft_btn)
2193 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2194 if (compose->toolbar->insert_btn)
2195 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2196 if (compose->toolbar->attach_btn)
2197 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2198 if (compose->toolbar->sig_btn)
2199 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2200 if (compose->toolbar->exteditor_btn)
2201 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2202 if (compose->toolbar->linewrap_current_btn)
2203 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2204 if (compose->toolbar->linewrap_all_btn)
2205 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2207 compose->modified = FALSE;
2208 compose_set_title(compose);
2209 compose->updating = FALSE;
2210 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2211 SCROLL_TO_CURSOR(compose);
2213 if (compose->deferred_destroy) {
2214 compose_destroy(compose);
2221 GList *compose_get_compose_list(void)
2223 return compose_list;
2226 void compose_entry_append(Compose *compose, const gchar *address,
2227 ComposeEntryType type)
2229 const gchar *header;
2231 gboolean in_quote = FALSE;
2232 if (!address || *address == '\0') return;
2239 header = N_("Bcc:");
2241 case COMPOSE_REPLYTO:
2242 header = N_("Reply-To:");
2244 case COMPOSE_NEWSGROUPS:
2245 header = N_("Newsgroups:");
2247 case COMPOSE_FOLLOWUPTO:
2248 header = N_( "Followup-To:");
2255 header = prefs_common_translated_header_name(header);
2257 cur = begin = (gchar *)address;
2259 /* we separate the line by commas, but not if we're inside a quoted
2261 while (*cur != '\0') {
2263 in_quote = !in_quote;
2264 if (*cur == ',' && !in_quote) {
2265 gchar *tmp = g_strdup(begin);
2267 tmp[cur-begin]='\0';
2270 while (*tmp == ' ' || *tmp == '\t')
2272 compose_add_header_entry(compose, header, tmp);
2279 gchar *tmp = g_strdup(begin);
2281 tmp[cur-begin]='\0';
2284 while (*tmp == ' ' || *tmp == '\t')
2286 compose_add_header_entry(compose, header, tmp);
2291 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2293 static GdkColor yellow;
2294 static GdkColor black;
2295 static gboolean yellow_initialised = FALSE;
2299 if (!yellow_initialised) {
2300 gdk_color_parse("#f5f6be", &yellow);
2301 gdk_color_parse("#000000", &black);
2302 yellow_initialised = gdk_colormap_alloc_color(
2303 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2304 yellow_initialised &= gdk_colormap_alloc_color(
2305 gdk_colormap_get_system(), &black, FALSE, TRUE);
2308 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2309 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2310 if (gtk_entry_get_text(entry) &&
2311 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2312 if (yellow_initialised) {
2313 gtk_widget_modify_base(
2314 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2315 GTK_STATE_NORMAL, &yellow);
2316 gtk_widget_modify_text(
2317 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2318 GTK_STATE_NORMAL, &black);
2324 void compose_toolbar_cb(gint action, gpointer data)
2326 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2327 Compose *compose = (Compose*)toolbar_item->parent;
2329 g_return_if_fail(compose != NULL);
2333 compose_send_cb(compose, 0, NULL);
2336 compose_send_later_cb(compose, 0, NULL);
2339 compose_draft_cb(compose, COMPOSE_QUIT_EDITING, NULL);
2342 compose_insert_file_cb(compose, 0, NULL);
2345 compose_attach_cb(compose, 0, NULL);
2348 compose_insert_sig(compose, FALSE);
2351 compose_ext_editor_cb(compose, 0, NULL);
2353 case A_LINEWRAP_CURRENT:
2354 compose_beautify_paragraph(compose, NULL, TRUE);
2356 case A_LINEWRAP_ALL:
2357 compose_wrap_all_full(compose, TRUE);
2360 compose_address_cb(compose, 0, NULL);
2363 case A_CHECK_SPELLING:
2364 compose_check_all(compose);
2372 static void compose_entries_set(Compose *compose, const gchar *mailto)
2376 gchar *subject = NULL;
2380 gchar *attach = NULL;
2382 scan_mailto_url(mailto, &to, &cc, NULL, &subject, &body, &attach);
2385 compose_entry_append(compose, to, COMPOSE_TO);
2387 compose_entry_append(compose, cc, COMPOSE_CC);
2389 if (!g_utf8_validate (subject, -1, NULL)) {
2390 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2391 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2394 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2398 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2399 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2402 gboolean prev_autowrap = compose->autowrap;
2404 compose->autowrap = FALSE;
2406 mark = gtk_text_buffer_get_insert(buffer);
2407 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2409 if (!g_utf8_validate (body, -1, NULL)) {
2410 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2411 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2414 gtk_text_buffer_insert(buffer, &iter, body, -1);
2416 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2418 compose->autowrap = prev_autowrap;
2419 if (compose->autowrap)
2420 compose_wrap_all(compose);
2424 gchar *utf8_filename = conv_filename_to_utf8(attach);
2425 if (utf8_filename) {
2426 if (compose_attach_append(compose, attach, utf8_filename, NULL)) {
2427 alertpanel_notice(_("The file '%s' has been attached."), attach);
2429 g_free(utf8_filename);
2431 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2441 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2443 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2444 {"Cc:", NULL, TRUE},
2445 {"References:", NULL, FALSE},
2446 {"Bcc:", NULL, TRUE},
2447 {"Newsgroups:", NULL, TRUE},
2448 {"Followup-To:", NULL, TRUE},
2449 {"List-Post:", NULL, FALSE},
2450 {"X-Priority:", NULL, FALSE},
2451 {NULL, NULL, FALSE}};
2467 g_return_val_if_fail(msginfo != NULL, -1);
2469 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2470 procheader_get_header_fields(fp, hentry);
2473 if (hentry[H_REPLY_TO].body != NULL) {
2474 if (hentry[H_REPLY_TO].body[0] != '\0') {
2476 conv_unmime_header(hentry[H_REPLY_TO].body,
2479 g_free(hentry[H_REPLY_TO].body);
2480 hentry[H_REPLY_TO].body = NULL;
2482 if (hentry[H_CC].body != NULL) {
2483 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2484 g_free(hentry[H_CC].body);
2485 hentry[H_CC].body = NULL;
2487 if (hentry[H_REFERENCES].body != NULL) {
2488 if (compose->mode == COMPOSE_REEDIT)
2489 compose->references = hentry[H_REFERENCES].body;
2491 compose->references = compose_parse_references
2492 (hentry[H_REFERENCES].body, msginfo->msgid);
2493 g_free(hentry[H_REFERENCES].body);
2495 hentry[H_REFERENCES].body = NULL;
2497 if (hentry[H_BCC].body != NULL) {
2498 if (compose->mode == COMPOSE_REEDIT)
2500 conv_unmime_header(hentry[H_BCC].body, NULL);
2501 g_free(hentry[H_BCC].body);
2502 hentry[H_BCC].body = NULL;
2504 if (hentry[H_NEWSGROUPS].body != NULL) {
2505 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2506 hentry[H_NEWSGROUPS].body = NULL;
2508 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2509 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2510 compose->followup_to =
2511 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2514 g_free(hentry[H_FOLLOWUP_TO].body);
2515 hentry[H_FOLLOWUP_TO].body = NULL;
2517 if (hentry[H_LIST_POST].body != NULL) {
2520 extract_address(hentry[H_LIST_POST].body);
2521 if (hentry[H_LIST_POST].body[0] != '\0') {
2522 scan_mailto_url(hentry[H_LIST_POST].body,
2523 &to, NULL, NULL, NULL, NULL, NULL);
2525 g_free(compose->ml_post);
2526 compose->ml_post = to;
2529 g_free(hentry[H_LIST_POST].body);
2530 hentry[H_LIST_POST].body = NULL;
2533 /* CLAWS - X-Priority */
2534 if (compose->mode == COMPOSE_REEDIT)
2535 if (hentry[H_X_PRIORITY].body != NULL) {
2538 priority = atoi(hentry[H_X_PRIORITY].body);
2539 g_free(hentry[H_X_PRIORITY].body);
2541 hentry[H_X_PRIORITY].body = NULL;
2543 if (priority < PRIORITY_HIGHEST ||
2544 priority > PRIORITY_LOWEST)
2545 priority = PRIORITY_NORMAL;
2547 compose->priority = priority;
2550 if (compose->mode == COMPOSE_REEDIT) {
2551 if (msginfo->inreplyto && *msginfo->inreplyto)
2552 compose->inreplyto = g_strdup(msginfo->inreplyto);
2556 if (msginfo->msgid && *msginfo->msgid)
2557 compose->inreplyto = g_strdup(msginfo->msgid);
2559 if (!compose->references) {
2560 if (msginfo->msgid && *msginfo->msgid) {
2561 if (msginfo->inreplyto && *msginfo->inreplyto)
2562 compose->references =
2563 g_strdup_printf("<%s>\n\t<%s>",
2567 compose->references =
2568 g_strconcat("<", msginfo->msgid, ">",
2570 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2571 compose->references =
2572 g_strconcat("<", msginfo->inreplyto, ">",
2580 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2582 GSList *ref_id_list, *cur;
2586 ref_id_list = references_list_append(NULL, ref);
2587 if (!ref_id_list) return NULL;
2588 if (msgid && *msgid)
2589 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2594 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2595 /* "<" + Message-ID + ">" + CR+LF+TAB */
2596 len += strlen((gchar *)cur->data) + 5;
2598 if (len > MAX_REFERENCES_LEN) {
2599 /* remove second message-ID */
2600 if (ref_id_list && ref_id_list->next &&
2601 ref_id_list->next->next) {
2602 g_free(ref_id_list->next->data);
2603 ref_id_list = g_slist_remove
2604 (ref_id_list, ref_id_list->next->data);
2606 slist_free_strings(ref_id_list);
2607 g_slist_free(ref_id_list);
2614 new_ref = g_string_new("");
2615 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2616 if (new_ref->len > 0)
2617 g_string_append(new_ref, "\n\t");
2618 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2621 slist_free_strings(ref_id_list);
2622 g_slist_free(ref_id_list);
2624 new_ref_str = new_ref->str;
2625 g_string_free(new_ref, FALSE);
2630 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2631 const gchar *fmt, const gchar *qmark,
2632 const gchar *body, gboolean rewrap,
2633 gboolean need_unescape,
2634 const gchar *err_msg)
2636 MsgInfo* dummyinfo = NULL;
2637 gchar *quote_str = NULL;
2639 gboolean prev_autowrap;
2640 const gchar *trimmed_body = body;
2641 gint cursor_pos = -1;
2642 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2643 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2648 SIGNAL_BLOCK(buffer);
2651 dummyinfo = compose_msginfo_new_from_compose(compose);
2652 msginfo = dummyinfo;
2655 if (qmark != NULL) {
2657 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
2658 compose->gtkaspell);
2660 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
2662 quote_fmt_scan_string(qmark);
2665 buf = quote_fmt_get_buffer();
2667 alertpanel_error(_("Quote mark format error."));
2669 Xstrdup_a(quote_str, buf, goto error)
2672 if (fmt && *fmt != '\0') {
2675 while (*trimmed_body == '\n')
2679 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account,
2680 compose->gtkaspell);
2682 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account);
2684 if (need_unescape) {
2687 /* decode \-escape sequences in the internal representation of the quote format */
2688 tmp = malloc(strlen(fmt)+1);
2689 pref_get_unescaped_pref(tmp, fmt);
2690 quote_fmt_scan_string(tmp);
2694 quote_fmt_scan_string(fmt);
2698 buf = quote_fmt_get_buffer();
2700 gint line = quote_fmt_get_line();
2701 alertpanel_error(err_msg, line);
2707 prev_autowrap = compose->autowrap;
2708 compose->autowrap = FALSE;
2710 mark = gtk_text_buffer_get_insert(buffer);
2711 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2712 if (g_utf8_validate(buf, -1, NULL)) {
2713 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2715 gchar *tmpout = NULL;
2716 tmpout = conv_codeset_strdup
2717 (buf, conv_get_locale_charset_str_no_utf8(),
2719 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2721 tmpout = g_malloc(strlen(buf)*2+1);
2722 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2724 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2728 cursor_pos = quote_fmt_get_cursor_pos();
2729 compose->set_cursor_pos = cursor_pos;
2730 if (cursor_pos == -1) {
2733 gtk_text_buffer_get_start_iter(buffer, &iter);
2734 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2735 gtk_text_buffer_place_cursor(buffer, &iter);
2737 compose->autowrap = prev_autowrap;
2738 if (compose->autowrap && rewrap)
2739 compose_wrap_all(compose);
2746 SIGNAL_UNBLOCK(buffer);
2748 procmsg_msginfo_free( dummyinfo );
2753 /* if ml_post is of type addr@host and from is of type
2754 * addr-anything@host, return TRUE
2756 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2758 gchar *left_ml = NULL;
2759 gchar *right_ml = NULL;
2760 gchar *left_from = NULL;
2761 gchar *right_from = NULL;
2762 gboolean result = FALSE;
2764 if (!ml_post || !from)
2767 left_ml = g_strdup(ml_post);
2768 if (strstr(left_ml, "@")) {
2769 right_ml = strstr(left_ml, "@")+1;
2770 *(strstr(left_ml, "@")) = '\0';
2773 left_from = g_strdup(from);
2774 if (strstr(left_from, "@")) {
2775 right_from = strstr(left_from, "@")+1;
2776 *(strstr(left_from, "@")) = '\0';
2779 if (left_ml && left_from && right_ml && right_from
2780 && !strncmp(left_from, left_ml, strlen(left_ml))
2781 && !strcmp(right_from, right_ml)) {
2790 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2792 gchar *my_addr1, *my_addr2;
2794 if (!addr1 || !addr2)
2797 Xstrdup_a(my_addr1, addr1, return FALSE);
2798 Xstrdup_a(my_addr2, addr2, return FALSE);
2800 extract_address(my_addr1);
2801 extract_address(my_addr2);
2803 return !strcasecmp(my_addr1, my_addr2);
2806 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2807 gboolean to_all, gboolean to_ml,
2809 gboolean followup_and_reply_to)
2811 GSList *cc_list = NULL;
2814 gchar *replyto = NULL;
2815 GHashTable *to_table;
2817 gboolean reply_to_ml = FALSE;
2818 gboolean default_reply_to = FALSE;
2820 g_return_if_fail(compose->account != NULL);
2821 g_return_if_fail(msginfo != NULL);
2823 reply_to_ml = to_ml && compose->ml_post;
2825 default_reply_to = msginfo->folder &&
2826 msginfo->folder->prefs->enable_default_reply_to;
2828 if (compose->account->protocol != A_NNTP) {
2829 if (reply_to_ml && !default_reply_to) {
2831 gboolean is_subscr = is_subscription(compose->ml_post,
2834 /* normal answer to ml post with a reply-to */
2835 compose_entry_append(compose,
2838 if (compose->replyto
2839 && !same_address(compose->ml_post, compose->replyto))
2840 compose_entry_append(compose,
2844 /* answer to subscription confirmation */
2845 if (compose->replyto)
2846 compose_entry_append(compose,
2849 else if (msginfo->from)
2850 compose_entry_append(compose,
2855 else if (!(to_all || to_sender) && default_reply_to) {
2856 compose_entry_append(compose,
2857 msginfo->folder->prefs->default_reply_to,
2859 compose_entry_mark_default_to(compose,
2860 msginfo->folder->prefs->default_reply_to);
2865 Xstrdup_a(tmp1, msginfo->from, return);
2866 extract_address(tmp1);
2867 if (to_all || to_sender ||
2868 !account_find_from_address(tmp1))
2869 compose_entry_append(compose,
2870 (compose->replyto && !to_sender)
2871 ? compose->replyto :
2872 msginfo->from ? msginfo->from : "",
2874 else if (!to_all && !to_sender) {
2875 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2876 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
2877 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2878 compose_entry_append(compose,
2879 msginfo->from ? msginfo->from : "",
2882 /* replying to own mail, use original recp */
2883 compose_entry_append(compose,
2884 msginfo->to ? msginfo->to : "",
2886 compose_entry_append(compose,
2887 msginfo->cc ? msginfo->cc : "",
2893 if (to_sender || (compose->followup_to &&
2894 !strncmp(compose->followup_to, "poster", 6)))
2895 compose_entry_append
2897 (compose->replyto ? compose->replyto :
2898 msginfo->from ? msginfo->from : ""),
2901 else if (followup_and_reply_to || to_all) {
2902 compose_entry_append
2904 (compose->replyto ? compose->replyto :
2905 msginfo->from ? msginfo->from : ""),
2908 compose_entry_append
2910 compose->followup_to ? compose->followup_to :
2911 compose->newsgroups ? compose->newsgroups : "",
2912 COMPOSE_NEWSGROUPS);
2915 compose_entry_append
2917 compose->followup_to ? compose->followup_to :
2918 compose->newsgroups ? compose->newsgroups : "",
2919 COMPOSE_NEWSGROUPS);
2922 if (msginfo->subject && *msginfo->subject) {
2926 buf = p = g_strdup(msginfo->subject);
2927 p += subject_get_prefix_length(p);
2928 memmove(buf, p, strlen(p) + 1);
2930 buf2 = g_strdup_printf("Re: %s", buf);
2931 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2936 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
2938 if (to_ml && compose->ml_post) return;
2939 if (!to_all || compose->account->protocol == A_NNTP) return;
2941 if (compose->replyto) {
2942 Xstrdup_a(replyto, compose->replyto, return);
2943 extract_address(replyto);
2945 if (msginfo->from) {
2946 Xstrdup_a(from, msginfo->from, return);
2947 extract_address(from);
2950 if (replyto && from)
2951 cc_list = address_list_append_with_comments(cc_list, from);
2952 if (to_all && msginfo->folder &&
2953 msginfo->folder->prefs->enable_default_reply_to)
2954 cc_list = address_list_append_with_comments(cc_list,
2955 msginfo->folder->prefs->default_reply_to);
2956 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
2957 cc_list = address_list_append_with_comments(cc_list, compose->cc);
2959 to_table = g_hash_table_new(g_str_hash, g_str_equal);
2961 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
2962 if (compose->account) {
2963 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
2964 GINT_TO_POINTER(1));
2966 /* remove address on To: and that of current account */
2967 for (cur = cc_list; cur != NULL; ) {
2968 GSList *next = cur->next;
2971 addr = g_utf8_strdown(cur->data, -1);
2972 extract_address(addr);
2974 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
2975 cc_list = g_slist_remove(cc_list, cur->data);
2977 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
2981 hash_free_strings(to_table);
2982 g_hash_table_destroy(to_table);
2985 for (cur = cc_list; cur != NULL; cur = cur->next)
2986 compose_entry_append(compose, (gchar *)cur->data,
2988 slist_free_strings(cc_list);
2989 g_slist_free(cc_list);
2994 #define SET_ENTRY(entry, str) \
2997 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3000 #define SET_ADDRESS(type, str) \
3003 compose_entry_append(compose, str, type); \
3006 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3008 g_return_if_fail(msginfo != NULL);
3010 SET_ENTRY(subject_entry, msginfo->subject);
3011 SET_ENTRY(from_name, msginfo->from);
3012 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3013 SET_ADDRESS(COMPOSE_CC, compose->cc);
3014 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3015 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3016 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3017 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3019 compose_update_priority_menu_item(compose);
3020 compose_update_privacy_system_menu_item(compose, FALSE);
3021 compose_show_first_last_header(compose, TRUE);
3027 static void compose_insert_sig(Compose *compose, gboolean replace)
3029 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3030 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3032 GtkTextIter iter, iter_end;
3034 gboolean prev_autowrap;
3035 gboolean found = FALSE;
3036 gboolean exists = FALSE;
3038 g_return_if_fail(compose->account != NULL);
3042 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3043 G_CALLBACK(compose_changed_cb),
3046 mark = gtk_text_buffer_get_insert(buffer);
3047 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3048 cur_pos = gtk_text_iter_get_offset (&iter);
3050 gtk_text_buffer_get_end_iter(buffer, &iter);
3052 exists = (compose->sig_str != NULL);
3055 GtkTextIter first_iter, start_iter, end_iter;
3057 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3059 if (!exists || compose->sig_str[0] == '\0')
3062 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3063 compose->signature_tag);
3066 /* include previous \n\n */
3067 gtk_text_iter_backward_chars(&first_iter, 2);
3068 start_iter = first_iter;
3069 end_iter = first_iter;
3071 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3072 compose->signature_tag);
3073 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3074 compose->signature_tag);
3076 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3082 g_free(compose->sig_str);
3083 compose->sig_str = compose_get_signature_str(compose);
3085 cur_pos = gtk_text_iter_get_offset(&iter);
3087 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3088 g_free(compose->sig_str);
3089 compose->sig_str = NULL;
3091 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3093 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3094 gtk_text_iter_forward_chars(&iter, 2);
3095 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3096 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3098 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3099 cur_pos = gtk_text_buffer_get_char_count (buffer);
3101 /* put the cursor where it should be
3102 * either where the quote_fmt says, either before the signature */
3103 if (compose->set_cursor_pos < 0)
3104 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3106 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3107 compose->set_cursor_pos);
3109 gtk_text_buffer_place_cursor(buffer, &iter);
3110 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3111 G_CALLBACK(compose_changed_cb),
3117 static gchar *compose_get_signature_str(Compose *compose)
3119 gchar *sig_body = NULL;
3120 gchar *sig_str = NULL;
3121 gchar *utf8_sig_str = NULL;
3123 g_return_val_if_fail(compose->account != NULL, NULL);
3125 if (!compose->account->sig_path)
3128 if (compose->account->sig_type == SIG_FILE) {
3129 if (!is_file_or_fifo_exist(compose->account->sig_path)) {
3130 g_warning("can't open signature file: %s\n",
3131 compose->account->sig_path);
3136 if (compose->account->sig_type == SIG_COMMAND)
3137 sig_body = get_command_output(compose->account->sig_path);
3141 tmp = file_read_to_str(compose->account->sig_path);
3144 sig_body = normalize_newlines(tmp);
3148 if (compose->account->sig_sep) {
3149 sig_str = g_strconcat("\n\n", compose->account->sig_sep, "\n", sig_body,
3153 sig_str = g_strconcat("\n\n", sig_body, NULL);
3156 if (g_utf8_validate(sig_str, -1, NULL) == TRUE)
3157 utf8_sig_str = sig_str;
3159 utf8_sig_str = conv_codeset_strdup
3160 (sig_str, conv_get_locale_charset_str_no_utf8(),
3166 return utf8_sig_str;
3169 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3172 GtkTextBuffer *buffer;
3175 const gchar *cur_encoding;
3176 gchar buf[BUFFSIZE];
3179 gboolean prev_autowrap;
3180 gboolean badtxt = FALSE;
3182 g_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3184 if ((fp = g_fopen(file, "rb")) == NULL) {
3185 FILE_OP_ERROR(file, "fopen");
3186 return COMPOSE_INSERT_READ_ERROR;
3189 prev_autowrap = compose->autowrap;
3190 compose->autowrap = FALSE;
3192 text = GTK_TEXT_VIEW(compose->text);
3193 buffer = gtk_text_view_get_buffer(text);
3194 mark = gtk_text_buffer_get_insert(buffer);
3195 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3197 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3198 G_CALLBACK(text_inserted),
3201 cur_encoding = conv_get_locale_charset_str_no_utf8();
3203 while (fgets(buf, sizeof(buf), fp) != NULL) {
3206 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3207 str = g_strdup(buf);
3209 str = conv_codeset_strdup
3210 (buf, cur_encoding, CS_INTERNAL);
3213 /* strip <CR> if DOS/Windows file,
3214 replace <CR> with <LF> if Macintosh file. */
3217 if (len > 0 && str[len - 1] != '\n') {
3219 if (str[len] == '\r') str[len] = '\n';
3222 gtk_text_buffer_insert(buffer, &iter, str, -1);
3226 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3227 G_CALLBACK(text_inserted),
3229 compose->autowrap = prev_autowrap;
3230 if (compose->autowrap)
3231 compose_wrap_all(compose);
3236 return COMPOSE_INSERT_INVALID_CHARACTER;
3238 return COMPOSE_INSERT_SUCCESS;
3241 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3242 const gchar *filename,
3243 const gchar *content_type)
3251 GtkListStore *store;
3253 gboolean has_binary = FALSE;
3255 if (!is_file_exist(file)) {
3256 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3257 gboolean result = FALSE;
3258 if (file_from_uri && is_file_exist(file_from_uri)) {
3259 result = compose_attach_append(
3260 compose, file_from_uri,
3264 g_free(file_from_uri);
3267 alertpanel_error("File %s doesn't exist\n", filename);
3270 if ((size = get_file_size(file)) < 0) {
3271 alertpanel_error("Can't get file size of %s\n", filename);
3275 alertpanel_error(_("File %s is empty."), filename);
3278 if ((fp = g_fopen(file, "rb")) == NULL) {
3279 alertpanel_error(_("Can't read %s."), filename);
3284 ainfo = g_new0(AttachInfo, 1);
3285 auto_ainfo = g_auto_pointer_new_with_free
3286 (ainfo, (GFreeFunc) compose_attach_info_free);
3287 ainfo->file = g_strdup(file);
3290 ainfo->content_type = g_strdup(content_type);
3291 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3293 MsgFlags flags = {0, 0};
3295 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3296 ainfo->encoding = ENC_7BIT;
3298 ainfo->encoding = ENC_8BIT;
3300 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3301 if (msginfo && msginfo->subject)
3302 name = g_strdup(msginfo->subject);
3304 name = g_path_get_basename(filename ? filename : file);
3306 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3308 procmsg_msginfo_free(msginfo);
3310 if (!g_ascii_strncasecmp(content_type, "text", 4))
3311 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3313 ainfo->encoding = ENC_BASE64;
3314 name = g_path_get_basename(filename ? filename : file);
3315 ainfo->name = g_strdup(name);
3319 ainfo->content_type = procmime_get_mime_type(file);
3320 if (!ainfo->content_type) {
3321 ainfo->content_type =
3322 g_strdup("application/octet-stream");
3323 ainfo->encoding = ENC_BASE64;
3324 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3326 procmime_get_encoding_for_text_file(file, &has_binary);
3328 ainfo->encoding = ENC_BASE64;
3329 name = g_path_get_basename(filename ? filename : file);
3330 ainfo->name = g_strdup(name);
3334 if (ainfo->name != NULL
3335 && !strcmp(ainfo->name, ".")) {
3336 g_free(ainfo->name);
3340 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3341 g_free(ainfo->content_type);
3342 ainfo->content_type = g_strdup("application/octet-stream");
3346 size_text = to_human_readable(size);
3348 store = GTK_LIST_STORE(gtk_tree_view_get_model
3349 (GTK_TREE_VIEW(compose->attach_clist)));
3351 gtk_list_store_append(store, &iter);
3352 gtk_list_store_set(store, &iter,
3353 COL_MIMETYPE, ainfo->content_type,
3354 COL_SIZE, size_text,
3355 COL_NAME, ainfo->name,
3357 COL_AUTODATA, auto_ainfo,
3360 g_auto_pointer_free(auto_ainfo);
3361 compose_attach_update_label(compose);
3365 static void compose_use_signing(Compose *compose, gboolean use_signing)
3367 GtkItemFactory *ifactory;
3368 GtkWidget *menuitem = NULL;
3370 compose->use_signing = use_signing;
3371 ifactory = gtk_item_factory_from_widget(compose->menubar);
3372 menuitem = gtk_item_factory_get_item
3373 (ifactory, "/Options/Sign");
3374 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
3378 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3380 GtkItemFactory *ifactory;
3381 GtkWidget *menuitem = NULL;
3383 compose->use_encryption = use_encryption;
3384 ifactory = gtk_item_factory_from_widget(compose->menubar);
3385 menuitem = gtk_item_factory_get_item
3386 (ifactory, "/Options/Encrypt");
3388 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
3392 #define NEXT_PART_NOT_CHILD(info) \
3394 node = info->node; \
3395 while (node->children) \
3396 node = g_node_last_child(node); \
3397 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3400 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3404 MimeInfo *firsttext = NULL;
3405 MimeInfo *encrypted = NULL;
3408 const gchar *partname = NULL;
3410 mimeinfo = procmime_scan_message(msginfo);
3411 if (!mimeinfo) return;
3413 if (mimeinfo->node->children == NULL) {
3414 procmime_mimeinfo_free_all(mimeinfo);
3418 /* find first content part */
3419 child = (MimeInfo *) mimeinfo->node->children->data;
3420 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3421 child = (MimeInfo *)child->node->children->data;
3423 if (child->type == MIMETYPE_TEXT) {
3425 debug_print("First text part found\n");
3426 } else if (compose->mode == COMPOSE_REEDIT &&
3427 child->type == MIMETYPE_APPLICATION &&
3428 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3429 encrypted = (MimeInfo *)child->node->parent->data;
3432 child = (MimeInfo *) mimeinfo->node->children->data;
3433 while (child != NULL) {
3436 if (child == encrypted) {
3437 /* skip this part of tree */
3438 NEXT_PART_NOT_CHILD(child);
3442 if (child->type == MIMETYPE_MULTIPART) {
3443 /* get the actual content */
3444 child = procmime_mimeinfo_next(child);
3448 if (child == firsttext) {
3449 child = procmime_mimeinfo_next(child);
3453 outfile = procmime_get_tmp_file_name(child);
3454 if ((err = procmime_get_part(outfile, child)) < 0)
3455 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3457 gchar *content_type;
3459 content_type = procmime_get_content_type_str(child->type, child->subtype);
3461 /* if we meet a pgp signature, we don't attach it, but
3462 * we force signing. */
3463 if ((strcmp(content_type, "application/pgp-signature") &&
3464 strcmp(content_type, "application/pkcs7-signature") &&
3465 strcmp(content_type, "application/x-pkcs7-signature"))
3466 || compose->mode == COMPOSE_REDIRECT) {
3467 partname = procmime_mimeinfo_get_parameter(child, "filename");
3468 if (partname == NULL)
3469 partname = procmime_mimeinfo_get_parameter(child, "name");
3470 if (partname == NULL)
3472 compose_attach_append(compose, outfile,
3473 partname, content_type);
3475 compose_force_signing(compose, compose->account);
3477 g_free(content_type);
3480 NEXT_PART_NOT_CHILD(child);
3482 procmime_mimeinfo_free_all(mimeinfo);
3485 #undef NEXT_PART_NOT_CHILD
3490 WAIT_FOR_INDENT_CHAR,
3491 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3494 /* return indent length, we allow:
3495 indent characters followed by indent characters or spaces/tabs,
3496 alphabets and numbers immediately followed by indent characters,
3497 and the repeating sequences of the above
3498 If quote ends with multiple spaces, only the first one is included. */
3499 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3500 const GtkTextIter *start, gint *len)
3502 GtkTextIter iter = *start;
3506 IndentState state = WAIT_FOR_INDENT_CHAR;
3509 gint alnum_count = 0;
3510 gint space_count = 0;
3513 if (prefs_common.quote_chars == NULL) {
3517 while (!gtk_text_iter_ends_line(&iter)) {
3518 wc = gtk_text_iter_get_char(&iter);
3519 if (g_unichar_iswide(wc))
3521 clen = g_unichar_to_utf8(wc, ch);
3525 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3526 is_space = g_unichar_isspace(wc);
3528 if (state == WAIT_FOR_INDENT_CHAR) {
3529 if (!is_indent && !g_unichar_isalnum(wc))
3532 quote_len += alnum_count + space_count + 1;
3533 alnum_count = space_count = 0;
3534 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3537 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3538 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3542 else if (is_indent) {
3543 quote_len += alnum_count + space_count + 1;
3544 alnum_count = space_count = 0;
3547 state = WAIT_FOR_INDENT_CHAR;
3551 gtk_text_iter_forward_char(&iter);
3554 if (quote_len > 0 && space_count > 0)
3560 if (quote_len > 0) {
3562 gtk_text_iter_forward_chars(&iter, quote_len);
3563 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3569 /* return TRUE if the line is itemized */
3570 static gboolean compose_is_itemized(GtkTextBuffer *buffer,
3571 const GtkTextIter *start)
3573 GtkTextIter iter = *start;
3578 if (gtk_text_iter_ends_line(&iter))
3582 wc = gtk_text_iter_get_char(&iter);
3583 if (!g_unichar_isspace(wc))
3585 gtk_text_iter_forward_char(&iter);
3586 if (gtk_text_iter_ends_line(&iter))
3590 clen = g_unichar_to_utf8(wc, ch);
3594 if (!strchr("*-+", ch[0]))
3597 gtk_text_iter_forward_char(&iter);
3598 if (gtk_text_iter_ends_line(&iter))
3600 wc = gtk_text_iter_get_char(&iter);
3601 if (g_unichar_isspace(wc))
3607 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3608 const GtkTextIter *start,
3609 GtkTextIter *break_pos,
3613 GtkTextIter iter = *start, line_end = *start;
3614 PangoLogAttr *attrs;
3621 gboolean can_break = FALSE;
3622 gboolean do_break = FALSE;
3623 gboolean was_white = FALSE;
3624 gboolean prev_dont_break = FALSE;
3626 gtk_text_iter_forward_to_line_end(&line_end);
3627 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3628 len = g_utf8_strlen(str, -1);
3632 g_warning("compose_get_line_break_pos: len = 0!\n");
3636 /* g_print("breaking line: %d: %s (len = %d)\n",
3637 gtk_text_iter_get_line(&iter), str, len); */
3639 attrs = g_new(PangoLogAttr, len + 1);
3641 pango_default_break(str, -1, NULL, attrs, len + 1);
3645 /* skip quote and leading spaces */
3646 for (i = 0; *p != '\0' && i < len; i++) {
3649 wc = g_utf8_get_char(p);
3650 if (i >= quote_len && !g_unichar_isspace(wc))
3652 if (g_unichar_iswide(wc))
3654 else if (*p == '\t')
3658 p = g_utf8_next_char(p);
3661 for (; *p != '\0' && i < len; i++) {
3662 PangoLogAttr *attr = attrs + i;
3666 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3669 was_white = attr->is_white;
3671 /* don't wrap URI */
3672 if ((uri_len = get_uri_len(p)) > 0) {
3674 if (pos > 0 && col > max_col) {
3684 wc = g_utf8_get_char(p);
3685 if (g_unichar_iswide(wc)) {
3687 if (prev_dont_break && can_break && attr->is_line_break)
3689 } else if (*p == '\t')
3693 if (pos > 0 && col > max_col) {
3698 if (*p == '-' || *p == '/')
3699 prev_dont_break = TRUE;
3701 prev_dont_break = FALSE;
3703 p = g_utf8_next_char(p);