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);
550 static GtkItemFactoryEntry compose_popup_entries[] =
552 {N_("/_Add..."), NULL, compose_attach_cb, 0, NULL},
553 {N_("/_Remove"), NULL, compose_attach_remove_selected, 0, NULL},
554 {"/---", NULL, NULL, 0, "<Separator>"},
555 {N_("/_Properties..."), NULL, compose_attach_property, 0, NULL}
558 static GtkItemFactoryEntry compose_entries[] =
560 {N_("/_Message"), NULL, NULL, 0, "<Branch>"},
561 {N_("/_Message/S_end"), "<control>Return",
562 compose_send_cb, 0, NULL},
563 {N_("/_Message/Send _later"), "<shift><control>S",
564 compose_send_later_cb, 0, NULL},
565 {N_("/_Message/---"), NULL, NULL, 0, "<Separator>"},
566 {N_("/_Message/_Attach file"), "<control>M", compose_attach_cb, 0, NULL},
567 {N_("/_Message/_Insert file"), "<control>I", compose_insert_file_cb, 0, NULL},
568 {N_("/_Message/Insert si_gnature"), "<control>G", compose_insert_sig_cb, 0, NULL},
569 {N_("/_Message/---"), NULL, NULL, 0, "<Separator>"},
570 {N_("/_Message/_Save"),
571 "<control>S", compose_draft_cb, COMPOSE_KEEP_EDITING, NULL},
572 {N_("/_Message/---"), NULL, NULL, 0, "<Separator>"},
573 {N_("/_Message/_Close"), "<control>W", compose_close_cb, 0, NULL},
575 {N_("/_Edit"), NULL, NULL, 0, "<Branch>"},
576 {N_("/_Edit/_Undo"), "<control>Z", compose_undo_cb, 0, NULL},
577 {N_("/_Edit/_Redo"), "<control>Y", compose_redo_cb, 0, NULL},
578 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
579 {N_("/_Edit/Cu_t"), "<control>X", compose_cut_cb, 0, NULL},
580 {N_("/_Edit/_Copy"), "<control>C", compose_copy_cb, 0, NULL},
581 {N_("/_Edit/_Paste"), "<control>V", compose_paste_cb, 0, NULL},
582 {N_("/_Edit/Special paste"), NULL, NULL, 0, "<Branch>"},
583 {N_("/_Edit/Special paste/as _quotation"),
584 NULL, compose_paste_as_quote_cb, 0, NULL},
585 {N_("/_Edit/Special paste/_wrapped"),
586 NULL, compose_paste_wrap_cb, 0, NULL},
587 {N_("/_Edit/Special paste/_unwrapped"),
588 NULL, compose_paste_no_wrap_cb, 0, NULL},
589 {N_("/_Edit/Select _all"), "<control>A", compose_allsel_cb, 0, NULL},
590 {N_("/_Edit/A_dvanced"), NULL, NULL, 0, "<Branch>"},
591 {N_("/_Edit/A_dvanced/Move a character backward"),
593 compose_advanced_action_cb,
594 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
596 {N_("/_Edit/A_dvanced/Move a character forward"),
598 compose_advanced_action_cb,
599 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
601 {N_("/_Edit/A_dvanced/Move a word backward"),
603 compose_advanced_action_cb,
604 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
606 {N_("/_Edit/A_dvanced/Move a word forward"),
608 compose_advanced_action_cb,
609 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
611 {N_("/_Edit/A_dvanced/Move to beginning of line"),
612 NULL, /* "<control>A" */
613 compose_advanced_action_cb,
614 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
616 {N_("/_Edit/A_dvanced/Move to end of line"),
618 compose_advanced_action_cb,
619 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
621 {N_("/_Edit/A_dvanced/Move to previous line"),
623 compose_advanced_action_cb,
624 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
626 {N_("/_Edit/A_dvanced/Move to next line"),
628 compose_advanced_action_cb,
629 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
631 {N_("/_Edit/A_dvanced/Delete a character backward"),
633 compose_advanced_action_cb,
634 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
636 {N_("/_Edit/A_dvanced/Delete a character forward"),
638 compose_advanced_action_cb,
639 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
641 {N_("/_Edit/A_dvanced/Delete a word backward"),
642 NULL, /* "<control>W" */
643 compose_advanced_action_cb,
644 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
646 {N_("/_Edit/A_dvanced/Delete a word forward"),
647 NULL, /* "<alt>D", */
648 compose_advanced_action_cb,
649 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
651 {N_("/_Edit/A_dvanced/Delete line"),
653 compose_advanced_action_cb,
654 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
656 {N_("/_Edit/A_dvanced/Delete entire line"),
658 compose_advanced_action_cb,
659 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE_N,
661 {N_("/_Edit/A_dvanced/Delete to end of line"),
663 compose_advanced_action_cb,
664 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END,
666 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
668 "<control>F", compose_find_cb, 0, NULL},
669 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
670 {N_("/_Edit/_Wrap current paragraph"),
671 "<control>L", compose_wrap_cb, 0, NULL},
672 {N_("/_Edit/Wrap all long _lines"),
673 "<control><alt>L", compose_wrap_cb, 1, NULL},
674 {N_("/_Edit/Aut_o wrapping"), "<shift><control>L", compose_toggle_autowrap_cb, 0, "<ToggleItem>"},
675 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
676 {N_("/_Edit/Edit with e_xternal editor"),
677 "<shift><control>X", compose_ext_editor_cb, 0, NULL},
679 {N_("/_Spelling"), NULL, NULL, 0, "<Branch>"},
680 {N_("/_Spelling/_Check all or check selection"),
681 NULL, compose_check_all, 0, NULL},
682 {N_("/_Spelling/_Highlight all misspelled words"),
683 NULL, compose_highlight_all, 0, NULL},
684 {N_("/_Spelling/Check _backwards misspelled word"),
685 NULL, compose_check_backwards , 0, NULL},
686 {N_("/_Spelling/_Forward to next misspelled word"),
687 NULL, compose_check_forwards_go, 0, NULL},
688 {N_("/_Spelling/---"), NULL, NULL, 0, "<Separator>"},
689 {N_("/_Spelling/Options"),
690 NULL, NULL, 0, "<Branch>"},
692 {N_("/_Options"), NULL, NULL, 0, "<Branch>"},
693 {N_("/_Options/Reply _mode"), NULL, NULL, 0, "<Branch>"},
694 {N_("/_Options/Reply _mode/_Normal"), NULL, compose_reply_change_mode, COMPOSE_REPLY, "<RadioItem>"},
695 {N_("/_Options/Reply _mode/_All"), NULL, compose_reply_change_mode, COMPOSE_REPLY_TO_ALL, "/Options/Reply mode/Normal"},
696 {N_("/_Options/Reply _mode/_Sender"), NULL, compose_reply_change_mode, COMPOSE_REPLY_TO_SENDER, "/Options/Reply mode/Normal"},
697 {N_("/_Options/Reply _mode/_Mailing-list"), NULL, compose_reply_change_mode, COMPOSE_REPLY_TO_LIST, "/Options/Reply mode/Normal"},
698 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
699 {N_("/_Options/Privacy _System"), NULL, NULL, 0, "<Branch>"},
700 {N_("/_Options/Privacy _System/None"), NULL, NULL, 0, "<RadioItem>"},
701 {N_("/_Options/Si_gn"), NULL, compose_toggle_sign_cb , 0, "<ToggleItem>"},
702 {N_("/_Options/_Encrypt"), NULL, compose_toggle_encrypt_cb, 0, "<ToggleItem>"},
703 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
704 {N_("/_Options/_Priority"), NULL, NULL, 0, "<Branch>"},
705 {N_("/_Options/Priority/_Highest"), NULL, compose_set_priority_cb, PRIORITY_HIGHEST, "<RadioItem>"},
706 {N_("/_Options/Priority/Hi_gh"), NULL, compose_set_priority_cb, PRIORITY_HIGH, "/Options/Priority/Highest"},
707 {N_("/_Options/Priority/_Normal"), NULL, compose_set_priority_cb, PRIORITY_NORMAL, "/Options/Priority/Highest"},
708 {N_("/_Options/Priority/Lo_w"), NULL, compose_set_priority_cb, PRIORITY_LOW, "/Options/Priority/Highest"},
709 {N_("/_Options/Priority/_Lowest"), NULL, compose_set_priority_cb, PRIORITY_LOWEST, "/Options/Priority/Highest"},
710 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
711 {N_("/_Options/_Request Return Receipt"), NULL, compose_toggle_return_receipt_cb, 0, "<ToggleItem>"},
712 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
713 {N_("/_Options/Remo_ve references"), NULL, compose_toggle_remove_refs_cb, 0, "<ToggleItem>"},
714 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
716 #define ENC_ACTION(action) \
717 NULL, compose_set_encoding_cb, action, \
718 "/Options/Character encoding/Automatic"
720 {N_("/_Options/Character _encoding"), NULL, NULL, 0, "<Branch>"},
721 {N_("/_Options/Character _encoding/_Automatic"),
722 NULL, compose_set_encoding_cb, C_AUTO, "<RadioItem>"},
723 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
725 {N_("/_Options/Character _encoding/7bit ascii (US-ASC_II)"),
726 ENC_ACTION(C_US_ASCII)},
727 {N_("/_Options/Character _encoding/Unicode (_UTF-8)"),
728 ENC_ACTION(C_UTF_8)},
729 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
731 {N_("/_Options/Character _encoding/Western European (ISO-8859-_1)"),
732 ENC_ACTION(C_ISO_8859_1)},
733 {N_("/_Options/Character _encoding/Western European (ISO-8859-15)"),
734 ENC_ACTION(C_ISO_8859_15)},
735 {N_("/_Options/Character _encoding/Western European (Windows-1252)"),
736 ENC_ACTION(C_WINDOWS_1252)},
737 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
739 {N_("/_Options/Character _encoding/Central European (ISO-8859-_2)"),
740 ENC_ACTION(C_ISO_8859_2)},
741 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
743 {N_("/_Options/Character _encoding/_Baltic (ISO-8859-13)"),
744 ENC_ACTION(C_ISO_8859_13)},
745 {N_("/_Options/Character _encoding/Baltic (ISO-8859-_4)"),
746 ENC_ACTION(C_ISO_8859_4)},
747 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
749 {N_("/_Options/Character _encoding/Greek (ISO-8859-_7)"),
750 ENC_ACTION(C_ISO_8859_7)},
751 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
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)},
757 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
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)},
763 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
765 {N_("/_Options/Character _encoding/Turkish (ISO-8859-_9)"),
766 ENC_ACTION(C_ISO_8859_9)},
767 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
769 {N_("/_Options/Character _encoding/Cyrillic (ISO-8859-_5)"),
770 ENC_ACTION(C_ISO_8859_5)},
771 {N_("/_Options/Character _encoding/Cyrillic (KOI8-_R)"),
772 ENC_ACTION(C_KOI8_R)},
773 {N_("/_Options/Character _encoding/Cyrillic (KOI8-U)"),
774 ENC_ACTION(C_KOI8_U)},
775 {N_("/_Options/Character _encoding/Cyrillic (Windows-1251)"),
776 ENC_ACTION(C_WINDOWS_1251)},
777 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
779 {N_("/_Options/Character _encoding/Japanese (ISO-2022-_JP)"),
780 ENC_ACTION(C_ISO_2022_JP)},
781 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
783 {N_("/_Options/Character _encoding/Simplified Chinese (_GB2312)"),
784 ENC_ACTION(C_GB2312)},
785 {N_("/_Options/Character _encoding/Simplified Chinese (GBK)"),
787 {N_("/_Options/Character _encoding/Traditional Chinese (_Big5)"),
789 {N_("/_Options/Character _encoding/Traditional Chinese (EUC-_TW)"),
790 ENC_ACTION(C_EUC_TW)},
791 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
793 {N_("/_Options/Character _encoding/Korean (EUC-_KR)"),
794 ENC_ACTION(C_EUC_KR)},
795 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
797 {N_("/_Options/Character _encoding/Thai (TIS-620)"),
798 ENC_ACTION(C_TIS_620)},
799 {N_("/_Options/Character _encoding/Thai (Windows-874)"),
800 ENC_ACTION(C_WINDOWS_874)},
802 {N_("/_Tools"), NULL, NULL, 0, "<Branch>"},
803 {N_("/_Tools/Show _ruler"), NULL, compose_toggle_ruler_cb, 0, "<ToggleItem>"},
804 {N_("/_Tools/_Address book"), "<shift><control>A", compose_address_cb , 0, NULL},
805 {N_("/_Tools/_Template"), NULL, NULL, 0, "<Branch>"},
806 {N_("/_Tools/Actio_ns"), NULL, NULL, 0, "<Branch>"},
807 {N_("/_Help"), NULL, NULL, 0, "<Branch>"},
808 {N_("/_Help/_About"), NULL, about_show, 0, NULL}
811 static GtkTargetEntry compose_mime_types[] =
813 {"text/uri-list", 0, 0},
814 {"UTF8_STRING", 0, 0},
818 static gboolean compose_put_existing_to_front(MsgInfo *info)
820 GList *compose_list = compose_get_compose_list();
824 for (elem = compose_list; elem != NULL && elem->data != NULL;
826 Compose *c = (Compose*)elem->data;
828 if (!c->targetinfo || !c->targetinfo->msgid ||
832 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
833 gtkut_window_popup(c->window);
841 static GdkColor quote_color1 =
842 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
843 static GdkColor quote_color2 =
844 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
845 static GdkColor quote_color3 =
846 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
848 static GdkColor quote_bgcolor1 =
849 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
850 static GdkColor quote_bgcolor2 =
851 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
852 static GdkColor quote_bgcolor3 =
853 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
855 static GdkColor signature_color = {
862 static GdkColor uri_color = {
869 static void compose_create_tags(GtkTextView *text, Compose *compose)
871 GtkTextBuffer *buffer;
872 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
878 buffer = gtk_text_view_get_buffer(text);
880 if (prefs_common.enable_color) {
881 /* grab the quote colors, converting from an int to a GdkColor */
882 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
884 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
886 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
888 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
890 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
892 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
894 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
896 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
899 signature_color = quote_color1 = quote_color2 = quote_color3 =
900 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
903 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
904 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
905 "foreground-gdk", "e_color1,
906 "paragraph-background-gdk", "e_bgcolor1,
908 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
909 "foreground-gdk", "e_color2,
910 "paragraph-background-gdk", "e_bgcolor2,
912 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
913 "foreground-gdk", "e_color3,
914 "paragraph-background-gdk", "e_bgcolor3,
917 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
918 "foreground-gdk", "e_color1,
920 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
921 "foreground-gdk", "e_color2,
923 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
924 "foreground-gdk", "e_color3,
928 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
929 "foreground-gdk", &signature_color,
932 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
933 "foreground-gdk", &uri_color,
935 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
936 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
938 color[0] = quote_color1;
939 color[1] = quote_color2;
940 color[2] = quote_color3;
941 color[3] = quote_bgcolor1;
942 color[4] = quote_bgcolor2;
943 color[5] = quote_bgcolor3;
944 color[6] = signature_color;
945 color[7] = uri_color;
946 cmap = gdk_drawable_get_colormap(compose->window->window);
947 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
949 for (i = 0; i < 8; i++) {
950 if (success[i] == FALSE) {
953 g_warning("Compose: color allocation failed.\n");
954 style = gtk_widget_get_style(GTK_WIDGET(text));
955 quote_color1 = quote_color2 = quote_color3 =
956 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
957 signature_color = uri_color = black;
962 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
963 GPtrArray *attach_files)
965 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
968 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
970 return compose_generic_new(account, mailto, item, NULL, NULL);
973 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
975 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
978 #define SCROLL_TO_CURSOR(compose) { \
979 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
980 gtk_text_view_get_buffer( \
981 GTK_TEXT_VIEW(compose->text))); \
982 gtk_text_view_scroll_mark_onscreen( \
983 GTK_TEXT_VIEW(compose->text), \
987 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
988 GPtrArray *attach_files, GList *listAddress )
991 GtkTextView *textview;
992 GtkTextBuffer *textbuf;
994 GtkItemFactory *ifactory;
995 const gchar *subject_format = NULL;
996 const gchar *body_format = NULL;
998 if (item && item->prefs && item->prefs->enable_default_account)
999 account = account_find_from_id(item->prefs->default_account);
1001 if (!account) account = cur_account;
1002 g_return_val_if_fail(account != NULL, NULL);
1004 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1006 ifactory = gtk_item_factory_from_widget(compose->menubar);
1008 compose->replyinfo = NULL;
1009 compose->fwdinfo = NULL;
1011 textview = GTK_TEXT_VIEW(compose->text);
1012 textbuf = gtk_text_view_get_buffer(textview);
1013 compose_create_tags(textview, compose);
1015 undo_block(compose->undostruct);
1017 compose_set_dictionaries_from_folder_prefs(compose, item);
1020 if (account->auto_sig)
1021 compose_insert_sig(compose, FALSE);
1022 gtk_text_buffer_get_start_iter(textbuf, &iter);
1023 gtk_text_buffer_place_cursor(textbuf, &iter);
1025 if (account->protocol != A_NNTP) {
1026 if (mailto && *mailto != '\0') {
1027 compose_entries_set(compose, mailto);
1029 } else if (item && item->prefs->enable_default_to) {
1030 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
1031 compose_entry_mark_default_to(compose, item->prefs->default_to);
1033 if (item && item->ret_rcpt) {
1034 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
1038 compose_entry_append(compose, mailto, COMPOSE_NEWSGROUPS);
1039 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1040 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1043 * CLAWS: just don't allow return receipt request, even if the user
1044 * may want to send an email. simple but foolproof.
1046 menu_set_sensitive(ifactory, "/Options/Request Return Receipt", FALSE);
1048 compose_add_field_list( compose, listAddress );
1050 if (item && item->prefs && item->prefs->compose_with_format) {
1051 subject_format = item->prefs->compose_subject_format;
1052 body_format = item->prefs->compose_body_format;
1053 } else if (account->compose_with_format) {
1054 subject_format = account->compose_subject_format;
1055 body_format = account->compose_body_format;
1056 } else if (prefs_common.compose_with_format) {
1057 subject_format = prefs_common.compose_subject_format;
1058 body_format = prefs_common.compose_body_format;
1061 if (subject_format || body_format) {
1062 MsgInfo* dummyinfo = NULL;
1065 && *subject_format != '\0' )
1067 gchar *subject = NULL;
1071 dummyinfo = compose_msginfo_new_from_compose(compose);
1073 /* decode \-escape sequences in the internal representation of the quote format */
1074 tmp = malloc(strlen(subject_format)+1);
1075 pref_get_unescaped_pref(tmp, subject_format);
1077 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1079 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account,
1080 compose->gtkaspell);
1082 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account);
1084 quote_fmt_scan_string(tmp);
1087 buf = quote_fmt_get_buffer();
1089 alertpanel_error(_("New message subject format error."));
1091 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1092 quote_fmt_reset_vartable();
1099 && *body_format != '\0' )
1102 GtkTextBuffer *buffer;
1103 GtkTextIter start, end;
1106 if ( dummyinfo == NULL )
1107 dummyinfo = compose_msginfo_new_from_compose(compose);
1109 text = GTK_TEXT_VIEW(compose->text);
1110 buffer = gtk_text_view_get_buffer(text);
1111 gtk_text_buffer_get_start_iter(buffer, &start);
1112 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1113 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1115 compose_quote_fmt(compose, dummyinfo,
1117 NULL, tmp, FALSE, TRUE,
1118 _("New message body format error at line %d."));
1119 quote_fmt_reset_vartable();
1124 procmsg_msginfo_free( dummyinfo );
1131 for (i = 0; i < attach_files->len; i++) {
1132 file = g_ptr_array_index(attach_files, i);
1133 compose_attach_append(compose, file, file, NULL);
1137 compose_show_first_last_header(compose, TRUE);
1139 /* Set save folder */
1140 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1141 gchar *folderidentifier;
1143 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1144 folderidentifier = folder_item_get_identifier(item);
1145 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1146 g_free(folderidentifier);
1149 gtk_widget_grab_focus(compose->header_last->entry);
1151 undo_unblock(compose->undostruct);
1153 if (prefs_common.auto_exteditor)
1154 compose_exec_ext_editor(compose);
1156 compose->draft_timeout_tag = -1;
1157 SCROLL_TO_CURSOR(compose);
1159 compose->modified = FALSE;
1160 compose_set_title(compose);
1164 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1165 gboolean override_pref)
1167 gchar *privacy = NULL;
1169 g_return_if_fail(compose != NULL);
1170 g_return_if_fail(account != NULL);
1172 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1175 if (account->default_privacy_system
1176 && strlen(account->default_privacy_system)) {
1177 privacy = account->default_privacy_system;
1179 GSList *privacy_avail = privacy_get_system_ids();
1180 if (privacy_avail && g_slist_length(privacy_avail)) {
1181 privacy = (gchar *)(privacy_avail->data);
1184 if (privacy != NULL) {
1185 if (compose->privacy_system == NULL)
1186 compose->privacy_system = g_strdup(privacy);
1187 compose_update_privacy_system_menu_item(compose, FALSE);
1188 compose_use_encryption(compose, TRUE);
1192 static void compose_force_signing(Compose *compose, PrefsAccount *account)
1194 gchar *privacy = NULL;
1196 if (account->default_privacy_system
1197 && strlen(account->default_privacy_system)) {
1198 privacy = account->default_privacy_system;
1200 GSList *privacy_avail = privacy_get_system_ids();
1201 if (privacy_avail && g_slist_length(privacy_avail)) {
1202 privacy = (gchar *)(privacy_avail->data);
1205 if (privacy != NULL) {
1206 if (compose->privacy_system == NULL)
1207 compose->privacy_system = g_strdup(privacy);
1208 compose_update_privacy_system_menu_item(compose, FALSE);
1209 compose_use_signing(compose, TRUE);
1213 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1217 Compose *compose = NULL;
1218 GtkItemFactory *ifactory = NULL;
1220 g_return_val_if_fail(msginfo_list != NULL, NULL);
1222 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1223 g_return_val_if_fail(msginfo != NULL, NULL);
1225 list_len = g_slist_length(msginfo_list);
1229 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1230 FALSE, prefs_common.default_reply_list, FALSE, body);
1232 case COMPOSE_REPLY_WITH_QUOTE:
1233 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1234 FALSE, prefs_common.default_reply_list, FALSE, body);
1236 case COMPOSE_REPLY_WITHOUT_QUOTE:
1237 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1238 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1240 case COMPOSE_REPLY_TO_SENDER:
1241 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1242 FALSE, FALSE, TRUE, body);
1244 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1245 compose = compose_followup_and_reply_to(msginfo,
1246 COMPOSE_QUOTE_CHECK,
1247 FALSE, FALSE, body);
1249 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1250 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1251 FALSE, FALSE, TRUE, body);
1253 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1254 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1255 FALSE, FALSE, TRUE, NULL);
1257 case COMPOSE_REPLY_TO_ALL:
1258 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1259 TRUE, FALSE, FALSE, body);
1261 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1262 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1263 TRUE, FALSE, FALSE, body);
1265 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1266 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1267 TRUE, FALSE, FALSE, NULL);
1269 case COMPOSE_REPLY_TO_LIST:
1270 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1271 FALSE, TRUE, FALSE, body);
1273 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1274 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1275 FALSE, TRUE, FALSE, body);
1277 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1278 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1279 FALSE, TRUE, FALSE, NULL);
1281 case COMPOSE_FORWARD:
1282 if (prefs_common.forward_as_attachment) {
1283 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1286 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1290 case COMPOSE_FORWARD_INLINE:
1291 /* check if we reply to more than one Message */
1292 if (list_len == 1) {
1293 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1296 /* more messages FALL THROUGH */
1297 case COMPOSE_FORWARD_AS_ATTACH:
1298 compose = compose_forward_multiple(NULL, msginfo_list);
1300 case COMPOSE_REDIRECT:
1301 compose = compose_redirect(NULL, msginfo, FALSE);
1304 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1307 if (compose == NULL) {
1308 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1311 ifactory = gtk_item_factory_from_widget(compose->menubar);
1313 compose->rmode = mode;
1314 switch (compose->rmode) {
1316 case COMPOSE_REPLY_WITH_QUOTE:
1317 case COMPOSE_REPLY_WITHOUT_QUOTE:
1318 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1319 debug_print("reply mode Normal\n");
1320 menu_set_active(ifactory, "/Options/Reply mode/Normal", TRUE);
1321 compose_reply_change_mode(compose, COMPOSE_REPLY, NULL); /* force update */
1323 case COMPOSE_REPLY_TO_SENDER:
1324 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1325 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1326 debug_print("reply mode Sender\n");
1327 menu_set_active(ifactory, "/Options/Reply mode/Sender", TRUE);
1329 case COMPOSE_REPLY_TO_ALL:
1330 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1331 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1332 debug_print("reply mode All\n");
1333 menu_set_active(ifactory, "/Options/Reply mode/All", TRUE);
1335 case COMPOSE_REPLY_TO_LIST:
1336 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1337 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1338 debug_print("reply mode List\n");
1339 menu_set_active(ifactory, "/Options/Reply mode/Mailing-list", TRUE);
1347 static Compose *compose_reply(MsgInfo *msginfo,
1348 ComposeQuoteMode quote_mode,
1354 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1355 to_sender, FALSE, body);
1358 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1359 ComposeQuoteMode quote_mode,
1364 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1365 to_sender, TRUE, body);
1368 static void compose_extract_original_charset(Compose *compose)
1370 MsgInfo *info = NULL;
1371 if (compose->replyinfo) {
1372 info = compose->replyinfo;
1373 } else if (compose->fwdinfo) {
1374 info = compose->fwdinfo;
1375 } else if (compose->targetinfo) {
1376 info = compose->targetinfo;
1379 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1380 MimeInfo *partinfo = mimeinfo;
1381 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1382 partinfo = procmime_mimeinfo_next(partinfo);
1384 compose->orig_charset =
1385 g_strdup(procmime_mimeinfo_get_parameter(
1386 partinfo, "charset"));
1388 procmime_mimeinfo_free_all(mimeinfo);
1392 #define SIGNAL_BLOCK(buffer) { \
1393 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1394 G_CALLBACK(compose_changed_cb), \
1396 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1397 G_CALLBACK(text_inserted), \
1401 #define SIGNAL_UNBLOCK(buffer) { \
1402 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1403 G_CALLBACK(compose_changed_cb), \
1405 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1406 G_CALLBACK(text_inserted), \
1410 static Compose *compose_generic_reply(MsgInfo *msginfo,
1411 ComposeQuoteMode quote_mode,
1412 gboolean to_all, gboolean to_ml,
1414 gboolean followup_and_reply_to,
1417 GtkItemFactory *ifactory;
1419 PrefsAccount *account = NULL;
1420 GtkTextView *textview;
1421 GtkTextBuffer *textbuf;
1422 gboolean quote = FALSE;
1423 const gchar *qmark = NULL;
1424 const gchar *body_fmt = NULL;
1426 g_return_val_if_fail(msginfo != NULL, NULL);
1427 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1429 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1431 g_return_val_if_fail(account != NULL, NULL);
1433 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1435 compose->updating = TRUE;
1437 ifactory = gtk_item_factory_from_widget(compose->menubar);
1439 menu_set_active(ifactory, "/Options/Remove references", FALSE);
1440 menu_set_sensitive(ifactory, "/Options/Remove references", TRUE);
1442 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1443 if (!compose->replyinfo)
1444 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1446 compose_extract_original_charset(compose);
1448 if (msginfo->folder && msginfo->folder->ret_rcpt)
1449 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
1451 /* Set save folder */
1452 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1453 gchar *folderidentifier;
1455 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1456 folderidentifier = folder_item_get_identifier(msginfo->folder);
1457 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1458 g_free(folderidentifier);
1461 if (compose_parse_header(compose, msginfo) < 0) return NULL;
1463 textview = (GTK_TEXT_VIEW(compose->text));
1464 textbuf = gtk_text_view_get_buffer(textview);
1465 compose_create_tags(textview, compose);
1467 undo_block(compose->undostruct);
1469 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1472 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1473 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1474 /* use the reply format of folder (if enabled), or the account's one
1475 (if enabled) or fallback to the global reply format, which is always
1476 enabled (even if empty), and use the relevant quotemark */
1478 if (msginfo->folder && msginfo->folder->prefs &&
1479 msginfo->folder->prefs->reply_with_format) {
1480 qmark = msginfo->folder->prefs->reply_quotemark;
1481 body_fmt = msginfo->folder->prefs->reply_body_format;
1483 } else if (account->reply_with_format) {
1484 qmark = account->reply_quotemark;
1485 body_fmt = account->reply_body_format;
1488 qmark = prefs_common.quotemark;
1489 body_fmt = prefs_common.quotefmt;
1494 /* empty quotemark is not allowed */
1495 if (qmark == NULL || *qmark == '\0')
1497 compose_quote_fmt(compose, compose->replyinfo,
1498 body_fmt, qmark, body, FALSE, TRUE,
1499 _("Message reply format error at line %d."));
1500 quote_fmt_reset_vartable();
1503 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1504 compose_force_encryption(compose, account, FALSE);
1507 SIGNAL_BLOCK(textbuf);
1509 if (account->auto_sig)
1510 compose_insert_sig(compose, FALSE);
1512 compose_wrap_all(compose);
1514 SIGNAL_UNBLOCK(textbuf);
1516 gtk_widget_grab_focus(compose->text);
1518 undo_unblock(compose->undostruct);
1520 if (prefs_common.auto_exteditor)
1521 compose_exec_ext_editor(compose);
1523 compose->modified = FALSE;
1524 compose_set_title(compose);
1526 compose->updating = FALSE;
1527 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1528 SCROLL_TO_CURSOR(compose);
1530 if (compose->deferred_destroy) {
1531 compose_destroy(compose);
1538 #define INSERT_FW_HEADER(var, hdr) \
1539 if (msginfo->var && *msginfo->var) { \
1540 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1541 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1542 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1545 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1546 gboolean as_attach, const gchar *body,
1547 gboolean no_extedit,
1551 GtkTextView *textview;
1552 GtkTextBuffer *textbuf;
1555 g_return_val_if_fail(msginfo != NULL, NULL);
1556 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1559 !(account = compose_guess_forward_account_from_msginfo
1561 account = cur_account;
1563 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1565 compose->updating = TRUE;
1566 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1567 if (!compose->fwdinfo)
1568 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1570 compose_extract_original_charset(compose);
1572 if (msginfo->subject && *msginfo->subject) {
1573 gchar *buf, *buf2, *p;
1575 buf = p = g_strdup(msginfo->subject);
1576 p += subject_get_prefix_length(p);
1577 memmove(buf, p, strlen(p) + 1);
1579 buf2 = g_strdup_printf("Fw: %s", buf);
1580 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1586 textview = GTK_TEXT_VIEW(compose->text);
1587 textbuf = gtk_text_view_get_buffer(textview);
1588 compose_create_tags(textview, compose);
1590 undo_block(compose->undostruct);
1594 msgfile = procmsg_get_message_file(msginfo);
1595 if (!is_file_exist(msgfile))
1596 g_warning("%s: file not exist\n", msgfile);
1598 compose_attach_append(compose, msgfile, msgfile,
1603 const gchar *qmark = NULL;
1604 const gchar *body_fmt = prefs_common.fw_quotefmt;
1605 MsgInfo *full_msginfo;
1607 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1609 full_msginfo = procmsg_msginfo_copy(msginfo);
1611 /* use the forward format of folder (if enabled), or the account's one
1612 (if enabled) or fallback to the global forward format, which is always
1613 enabled (even if empty), and use the relevant quotemark */
1614 if (msginfo->folder && msginfo->folder->prefs &&
1615 msginfo->folder->prefs->forward_with_format) {
1616 qmark = msginfo->folder->prefs->forward_quotemark;
1617 body_fmt = msginfo->folder->prefs->forward_body_format;
1619 } else if (account->forward_with_format) {
1620 qmark = account->forward_quotemark;
1621 body_fmt = account->forward_body_format;
1624 qmark = prefs_common.fw_quotemark;
1625 body_fmt = prefs_common.fw_quotefmt;
1628 /* empty quotemark is not allowed */
1629 if (qmark == NULL || *qmark == '\0')
1632 compose_quote_fmt(compose, full_msginfo,
1633 body_fmt, qmark, body, FALSE, TRUE,
1634 _("Message forward format error at line %d."));
1635 quote_fmt_reset_vartable();
1636 compose_attach_parts(compose, msginfo);
1638 procmsg_msginfo_free(full_msginfo);
1641 SIGNAL_BLOCK(textbuf);
1643 if (account->auto_sig)
1644 compose_insert_sig(compose, FALSE);
1646 compose_wrap_all(compose);
1648 SIGNAL_UNBLOCK(textbuf);
1650 gtk_text_buffer_get_start_iter(textbuf, &iter);
1651 gtk_text_buffer_place_cursor(textbuf, &iter);
1653 gtk_widget_grab_focus(compose->header_last->entry);
1655 if (!no_extedit && prefs_common.auto_exteditor)
1656 compose_exec_ext_editor(compose);
1659 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1660 gchar *folderidentifier;
1662 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1663 folderidentifier = folder_item_get_identifier(msginfo->folder);
1664 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1665 g_free(folderidentifier);
1668 undo_unblock(compose->undostruct);
1670 compose->modified = FALSE;
1671 compose_set_title(compose);
1673 compose->updating = FALSE;
1674 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1675 SCROLL_TO_CURSOR(compose);
1677 if (compose->deferred_destroy) {
1678 compose_destroy(compose);
1685 #undef INSERT_FW_HEADER
1687 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1690 GtkTextView *textview;
1691 GtkTextBuffer *textbuf;
1695 gboolean single_mail = TRUE;
1697 g_return_val_if_fail(msginfo_list != NULL, NULL);
1699 if (g_slist_length(msginfo_list) > 1)
1700 single_mail = FALSE;
1702 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1703 if (((MsgInfo *)msginfo->data)->folder == NULL)
1706 /* guess account from first selected message */
1708 !(account = compose_guess_forward_account_from_msginfo
1709 (msginfo_list->data)))
1710 account = cur_account;
1712 g_return_val_if_fail(account != NULL, NULL);
1714 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1715 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1716 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1719 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1721 compose->updating = TRUE;
1723 textview = GTK_TEXT_VIEW(compose->text);
1724 textbuf = gtk_text_view_get_buffer(textview);
1725 compose_create_tags(textview, compose);
1727 undo_block(compose->undostruct);
1728 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1729 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1731 if (!is_file_exist(msgfile))
1732 g_warning("%s: file not exist\n", msgfile);
1734 compose_attach_append(compose, msgfile, msgfile,
1740 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1741 if (info->subject && *info->subject) {
1742 gchar *buf, *buf2, *p;
1744 buf = p = g_strdup(info->subject);
1745 p += subject_get_prefix_length(p);
1746 memmove(buf, p, strlen(p) + 1);
1748 buf2 = g_strdup_printf("Fw: %s", buf);
1749 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1755 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1756 _("Fw: multiple emails"));
1759 SIGNAL_BLOCK(textbuf);
1761 if (account->auto_sig)
1762 compose_insert_sig(compose, FALSE);
1764 compose_wrap_all(compose);
1766 SIGNAL_UNBLOCK(textbuf);
1768 gtk_text_buffer_get_start_iter(textbuf, &iter);
1769 gtk_text_buffer_place_cursor(textbuf, &iter);
1771 gtk_widget_grab_focus(compose->header_last->entry);
1772 undo_unblock(compose->undostruct);
1773 compose->modified = FALSE;
1774 compose_set_title(compose);
1776 compose->updating = FALSE;
1777 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1778 SCROLL_TO_CURSOR(compose);
1780 if (compose->deferred_destroy) {
1781 compose_destroy(compose);
1788 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1790 GtkTextIter start = *iter;
1791 GtkTextIter end_iter;
1792 int start_pos = gtk_text_iter_get_offset(&start);
1794 if (!compose->account->sig_sep)
1797 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1798 start_pos+strlen(compose->account->sig_sep));
1800 /* check sig separator */
1801 str = gtk_text_iter_get_text(&start, &end_iter);
1802 if (!strcmp(str, compose->account->sig_sep)) {
1804 /* check end of line (\n) */
1805 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1806 start_pos+strlen(compose->account->sig_sep));
1807 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1808 start_pos+strlen(compose->account->sig_sep)+1);
1809 tmp = gtk_text_iter_get_text(&start, &end_iter);
1810 if (!strcmp(tmp,"\n")) {
1822 static void compose_colorize_signature(Compose *compose)
1824 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1826 GtkTextIter end_iter;
1827 gtk_text_buffer_get_start_iter(buffer, &iter);
1828 while (gtk_text_iter_forward_line(&iter))
1829 if (compose_is_sig_separator(compose, buffer, &iter)) {
1830 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1831 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1835 #define BLOCK_WRAP() { \
1836 prev_autowrap = compose->autowrap; \
1837 buffer = gtk_text_view_get_buffer( \
1838 GTK_TEXT_VIEW(compose->text)); \
1839 compose->autowrap = FALSE; \
1841 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1842 G_CALLBACK(compose_changed_cb), \
1844 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1845 G_CALLBACK(text_inserted), \
1848 #define UNBLOCK_WRAP() { \
1849 compose->autowrap = prev_autowrap; \
1850 if (compose->autowrap) { \
1851 gint old = compose->draft_timeout_tag; \
1852 compose->draft_timeout_tag = -2; \
1853 compose_wrap_all(compose); \
1854 compose->draft_timeout_tag = old; \
1857 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1858 G_CALLBACK(compose_changed_cb), \
1860 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1861 G_CALLBACK(text_inserted), \
1865 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
1867 Compose *compose = NULL;
1868 PrefsAccount *account = NULL;
1869 GtkTextView *textview;
1870 GtkTextBuffer *textbuf;
1874 gchar buf[BUFFSIZE];
1875 gboolean use_signing = FALSE;
1876 gboolean use_encryption = FALSE;
1877 gchar *privacy_system = NULL;
1878 int priority = PRIORITY_NORMAL;
1879 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
1881 g_return_val_if_fail(msginfo != NULL, NULL);
1882 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1884 if (compose_put_existing_to_front(msginfo)) {
1888 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
1889 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
1890 gchar queueheader_buf[BUFFSIZE];
1893 /* Select Account from queue headers */
1894 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1895 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
1896 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
1897 account = account_find_from_id(id);
1899 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1900 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
1901 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
1902 account = account_find_from_id(id);
1904 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1905 sizeof(queueheader_buf), "NAID:")) {
1906 id = atoi(&queueheader_buf[strlen("NAID:")]);
1907 account = account_find_from_id(id);
1909 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1910 sizeof(queueheader_buf), "MAID:")) {
1911 id = atoi(&queueheader_buf[strlen("MAID:")]);
1912 account = account_find_from_id(id);
1914 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1915 sizeof(queueheader_buf), "S:")) {
1916 account = account_find_from_address(queueheader_buf);
1918 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1919 sizeof(queueheader_buf), "X-Claws-Sign:")) {
1920 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
1921 use_signing = param;
1924 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1925 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
1926 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
1927 use_signing = param;
1930 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1931 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
1932 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
1933 use_encryption = param;
1935 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1936 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
1937 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
1938 use_encryption = param;
1940 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1941 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
1942 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
1944 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1945 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
1946 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
1948 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1949 sizeof(queueheader_buf), "X-Priority: ")) {
1950 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
1953 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1954 sizeof(queueheader_buf), "RMID:")) {
1955 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
1956 if (tokens[0] && tokens[1] && tokens[2]) {
1957 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
1958 if (orig_item != NULL) {
1959 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
1964 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1965 sizeof(queueheader_buf), "FMID:")) {
1966 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
1967 if (tokens[0] && tokens[1] && tokens[2]) {
1968 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
1969 if (orig_item != NULL) {
1970 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
1976 account = msginfo->folder->folder->account;
1979 if (!account && prefs_common.reedit_account_autosel) {
1980 gchar from[BUFFSIZE];
1981 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
1982 extract_address(from);
1983 account = account_find_from_address(from);
1987 account = cur_account;
1989 g_return_val_if_fail(account != NULL, NULL);
1991 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
1993 compose->replyinfo = replyinfo;
1994 compose->fwdinfo = fwdinfo;
1996 compose->updating = TRUE;
1997 compose->priority = priority;
1999 if (privacy_system != NULL) {
2000 compose->privacy_system = privacy_system;
2001 compose_use_signing(compose, use_signing);
2002 compose_use_encryption(compose, use_encryption);
2003 compose_update_privacy_system_menu_item(compose, FALSE);
2005 activate_privacy_system(compose, account, FALSE);
2008 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2010 compose_extract_original_charset(compose);
2012 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2013 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2014 gchar queueheader_buf[BUFFSIZE];
2016 /* Set message save folder */
2017 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2020 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2021 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
2022 gtk_editable_insert_text(GTK_EDITABLE(compose->savemsg_entry), &queueheader_buf[4], strlen(&queueheader_buf[4]), &startpos);
2024 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2025 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2027 GtkItemFactory *ifactory;
2028 ifactory = gtk_item_factory_from_widget(compose->menubar);
2029 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
2034 if (compose_parse_header(compose, msginfo) < 0) {
2035 compose->updating = FALSE;
2036 compose_destroy(compose);
2039 compose_reedit_set_entry(compose, msginfo);
2041 textview = GTK_TEXT_VIEW(compose->text);
2042 textbuf = gtk_text_view_get_buffer(textview);
2043 compose_create_tags(textview, compose);
2045 mark = gtk_text_buffer_get_insert(textbuf);
2046 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2048 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2049 G_CALLBACK(compose_changed_cb),
2052 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2053 fp = procmime_get_first_encrypted_text_content(msginfo);
2055 compose_force_encryption(compose, account, TRUE);
2058 fp = procmime_get_first_text_content(msginfo);
2061 g_warning("Can't get text part\n");
2065 gboolean prev_autowrap = compose->autowrap;
2066 GtkTextBuffer *buffer = textbuf;
2068 while (fgets(buf, sizeof(buf), fp) != NULL) {
2070 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2076 compose_attach_parts(compose, msginfo);
2078 compose_colorize_signature(compose);
2080 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2081 G_CALLBACK(compose_changed_cb),
2084 gtk_widget_grab_focus(compose->text);
2086 if (prefs_common.auto_exteditor) {
2087 compose_exec_ext_editor(compose);
2089 compose->modified = FALSE;
2090 compose_set_title(compose);
2092 compose->updating = FALSE;
2093 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2094 SCROLL_TO_CURSOR(compose);
2096 if (compose->deferred_destroy) {
2097 compose_destroy(compose);
2101 compose->sig_str = compose_get_signature_str(compose);
2106 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2111 GtkItemFactory *ifactory;
2114 g_return_val_if_fail(msginfo != NULL, NULL);
2117 account = account_get_reply_account(msginfo,
2118 prefs_common.reply_account_autosel);
2119 g_return_val_if_fail(account != NULL, NULL);
2121 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2123 compose->updating = TRUE;
2125 ifactory = gtk_item_factory_from_widget(compose->menubar);
2126 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2127 compose->replyinfo = NULL;
2128 compose->fwdinfo = NULL;
2130 compose_show_first_last_header(compose, TRUE);
2132 gtk_widget_grab_focus(compose->header_last->entry);
2134 filename = procmsg_get_message_file(msginfo);
2136 if (filename == NULL) {
2137 compose->updating = FALSE;
2138 compose_destroy(compose);
2143 compose->redirect_filename = filename;
2145 /* Set save folder */
2146 item = msginfo->folder;
2147 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2148 gchar *folderidentifier;
2150 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2151 folderidentifier = folder_item_get_identifier(item);
2152 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
2153 g_free(folderidentifier);
2156 compose_attach_parts(compose, msginfo);
2158 if (msginfo->subject)
2159 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2161 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2163 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2164 _("Message redirect format error at line %d."));
2165 quote_fmt_reset_vartable();
2166 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2168 compose_colorize_signature(compose);
2170 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
2171 menu_set_sensitive(ifactory, "/Add...", FALSE);
2172 menu_set_sensitive(ifactory, "/Remove", FALSE);
2173 menu_set_sensitive(ifactory, "/Properties...", FALSE);
2175 ifactory = gtk_item_factory_from_widget(compose->menubar);
2176 menu_set_sensitive(ifactory, "/Message/Save", FALSE);
2177 menu_set_sensitive(ifactory, "/Message/Insert file", FALSE);
2178 menu_set_sensitive(ifactory, "/Message/Attach file", FALSE);
2179 menu_set_sensitive(ifactory, "/Message/Insert signature", FALSE);
2180 menu_set_sensitive(ifactory, "/Edit", FALSE);
2181 menu_set_sensitive(ifactory, "/Options", FALSE);
2182 menu_set_sensitive(ifactory, "/Tools/Show ruler", FALSE);
2183 menu_set_sensitive(ifactory, "/Tools/Actions", FALSE);
2185 if (compose->toolbar->draft_btn)
2186 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2187 if (compose->toolbar->insert_btn)
2188 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2189 if (compose->toolbar->attach_btn)
2190 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2191 if (compose->toolbar->sig_btn)
2192 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2193 if (compose->toolbar->exteditor_btn)
2194 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2195 if (compose->toolbar->linewrap_current_btn)
2196 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2197 if (compose->toolbar->linewrap_all_btn)
2198 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2200 compose->modified = FALSE;
2201 compose_set_title(compose);
2202 compose->updating = FALSE;
2203 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2204 SCROLL_TO_CURSOR(compose);
2206 if (compose->deferred_destroy) {
2207 compose_destroy(compose);
2214 GList *compose_get_compose_list(void)
2216 return compose_list;
2219 void compose_entry_append(Compose *compose, const gchar *address,
2220 ComposeEntryType type)
2222 const gchar *header;
2224 gboolean in_quote = FALSE;
2225 if (!address || *address == '\0') return;
2232 header = N_("Bcc:");
2234 case COMPOSE_REPLYTO:
2235 header = N_("Reply-To:");
2237 case COMPOSE_NEWSGROUPS:
2238 header = N_("Newsgroups:");
2240 case COMPOSE_FOLLOWUPTO:
2241 header = N_( "Followup-To:");
2248 header = prefs_common_translated_header_name(header);
2250 cur = begin = (gchar *)address;
2252 /* we separate the line by commas, but not if we're inside a quoted
2254 while (*cur != '\0') {
2256 in_quote = !in_quote;
2257 if (*cur == ',' && !in_quote) {
2258 gchar *tmp = g_strdup(begin);
2260 tmp[cur-begin]='\0';
2263 while (*tmp == ' ' || *tmp == '\t')
2265 compose_add_header_entry(compose, header, tmp);
2272 gchar *tmp = g_strdup(begin);
2274 tmp[cur-begin]='\0';
2277 while (*tmp == ' ' || *tmp == '\t')
2279 compose_add_header_entry(compose, header, tmp);
2284 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2286 static GdkColor yellow;
2287 static GdkColor black;
2288 static gboolean yellow_initialised = FALSE;
2292 if (!yellow_initialised) {
2293 gdk_color_parse("#f5f6be", &yellow);
2294 gdk_color_parse("#000000", &black);
2295 yellow_initialised = gdk_colormap_alloc_color(
2296 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2297 yellow_initialised &= gdk_colormap_alloc_color(
2298 gdk_colormap_get_system(), &black, FALSE, TRUE);
2301 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2302 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2303 if (gtk_entry_get_text(entry) &&
2304 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2305 if (yellow_initialised) {
2306 gtk_widget_modify_base(
2307 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2308 GTK_STATE_NORMAL, &yellow);
2309 gtk_widget_modify_text(
2310 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2311 GTK_STATE_NORMAL, &black);
2317 void compose_toolbar_cb(gint action, gpointer data)
2319 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2320 Compose *compose = (Compose*)toolbar_item->parent;
2322 g_return_if_fail(compose != NULL);
2326 compose_send_cb(compose, 0, NULL);
2329 compose_send_later_cb(compose, 0, NULL);
2332 compose_draft_cb(compose, COMPOSE_QUIT_EDITING, NULL);
2335 compose_insert_file_cb(compose, 0, NULL);
2338 compose_attach_cb(compose, 0, NULL);
2341 compose_insert_sig(compose, FALSE);
2344 compose_ext_editor_cb(compose, 0, NULL);
2346 case A_LINEWRAP_CURRENT:
2347 compose_beautify_paragraph(compose, NULL, TRUE);
2349 case A_LINEWRAP_ALL:
2350 compose_wrap_all_full(compose, TRUE);
2353 compose_address_cb(compose, 0, NULL);
2356 case A_CHECK_SPELLING:
2357 compose_check_all(compose);
2365 static void compose_entries_set(Compose *compose, const gchar *mailto)
2369 gchar *subject = NULL;
2373 gchar *attach = NULL;
2375 scan_mailto_url(mailto, &to, &cc, NULL, &subject, &body, &attach);
2378 compose_entry_append(compose, to, COMPOSE_TO);
2380 compose_entry_append(compose, cc, COMPOSE_CC);
2382 if (!g_utf8_validate (subject, -1, NULL)) {
2383 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2384 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2387 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2391 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2392 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2395 gboolean prev_autowrap = compose->autowrap;
2397 compose->autowrap = FALSE;
2399 mark = gtk_text_buffer_get_insert(buffer);
2400 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2402 if (!g_utf8_validate (body, -1, NULL)) {
2403 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2404 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2407 gtk_text_buffer_insert(buffer, &iter, body, -1);
2409 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2411 compose->autowrap = prev_autowrap;
2412 if (compose->autowrap)
2413 compose_wrap_all(compose);
2417 gchar *utf8_filename = conv_filename_to_utf8(attach);
2418 if (utf8_filename) {
2419 if (compose_attach_append(compose, attach, utf8_filename, NULL)) {
2420 alertpanel_notice(_("The file '%s' has been attached."), attach);
2422 g_free(utf8_filename);
2424 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2434 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2436 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2437 {"Cc:", NULL, TRUE},
2438 {"References:", NULL, FALSE},
2439 {"Bcc:", NULL, TRUE},
2440 {"Newsgroups:", NULL, TRUE},
2441 {"Followup-To:", NULL, TRUE},
2442 {"List-Post:", NULL, FALSE},
2443 {"X-Priority:", NULL, FALSE},
2444 {NULL, NULL, FALSE}};
2460 g_return_val_if_fail(msginfo != NULL, -1);
2462 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2463 procheader_get_header_fields(fp, hentry);
2466 if (hentry[H_REPLY_TO].body != NULL) {
2467 if (hentry[H_REPLY_TO].body[0] != '\0') {
2469 conv_unmime_header(hentry[H_REPLY_TO].body,
2472 g_free(hentry[H_REPLY_TO].body);
2473 hentry[H_REPLY_TO].body = NULL;
2475 if (hentry[H_CC].body != NULL) {
2476 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2477 g_free(hentry[H_CC].body);
2478 hentry[H_CC].body = NULL;
2480 if (hentry[H_REFERENCES].body != NULL) {
2481 if (compose->mode == COMPOSE_REEDIT)
2482 compose->references = hentry[H_REFERENCES].body;
2484 compose->references = compose_parse_references
2485 (hentry[H_REFERENCES].body, msginfo->msgid);
2486 g_free(hentry[H_REFERENCES].body);
2488 hentry[H_REFERENCES].body = NULL;
2490 if (hentry[H_BCC].body != NULL) {
2491 if (compose->mode == COMPOSE_REEDIT)
2493 conv_unmime_header(hentry[H_BCC].body, NULL);
2494 g_free(hentry[H_BCC].body);
2495 hentry[H_BCC].body = NULL;
2497 if (hentry[H_NEWSGROUPS].body != NULL) {
2498 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2499 hentry[H_NEWSGROUPS].body = NULL;
2501 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2502 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2503 compose->followup_to =
2504 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2507 g_free(hentry[H_FOLLOWUP_TO].body);
2508 hentry[H_FOLLOWUP_TO].body = NULL;
2510 if (hentry[H_LIST_POST].body != NULL) {
2513 extract_address(hentry[H_LIST_POST].body);
2514 if (hentry[H_LIST_POST].body[0] != '\0') {
2515 scan_mailto_url(hentry[H_LIST_POST].body,
2516 &to, NULL, NULL, NULL, NULL, NULL);
2518 g_free(compose->ml_post);
2519 compose->ml_post = to;
2522 g_free(hentry[H_LIST_POST].body);
2523 hentry[H_LIST_POST].body = NULL;
2526 /* CLAWS - X-Priority */
2527 if (compose->mode == COMPOSE_REEDIT)
2528 if (hentry[H_X_PRIORITY].body != NULL) {
2531 priority = atoi(hentry[H_X_PRIORITY].body);
2532 g_free(hentry[H_X_PRIORITY].body);
2534 hentry[H_X_PRIORITY].body = NULL;
2536 if (priority < PRIORITY_HIGHEST ||
2537 priority > PRIORITY_LOWEST)
2538 priority = PRIORITY_NORMAL;
2540 compose->priority = priority;
2543 if (compose->mode == COMPOSE_REEDIT) {
2544 if (msginfo->inreplyto && *msginfo->inreplyto)
2545 compose->inreplyto = g_strdup(msginfo->inreplyto);
2549 if (msginfo->msgid && *msginfo->msgid)
2550 compose->inreplyto = g_strdup(msginfo->msgid);
2552 if (!compose->references) {
2553 if (msginfo->msgid && *msginfo->msgid) {
2554 if (msginfo->inreplyto && *msginfo->inreplyto)
2555 compose->references =
2556 g_strdup_printf("<%s>\n\t<%s>",
2560 compose->references =
2561 g_strconcat("<", msginfo->msgid, ">",
2563 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2564 compose->references =
2565 g_strconcat("<", msginfo->inreplyto, ">",
2573 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2575 GSList *ref_id_list, *cur;
2579 ref_id_list = references_list_append(NULL, ref);
2580 if (!ref_id_list) return NULL;
2581 if (msgid && *msgid)
2582 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2587 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2588 /* "<" + Message-ID + ">" + CR+LF+TAB */
2589 len += strlen((gchar *)cur->data) + 5;
2591 if (len > MAX_REFERENCES_LEN) {
2592 /* remove second message-ID */
2593 if (ref_id_list && ref_id_list->next &&
2594 ref_id_list->next->next) {
2595 g_free(ref_id_list->next->data);
2596 ref_id_list = g_slist_remove
2597 (ref_id_list, ref_id_list->next->data);
2599 slist_free_strings(ref_id_list);
2600 g_slist_free(ref_id_list);
2607 new_ref = g_string_new("");
2608 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2609 if (new_ref->len > 0)
2610 g_string_append(new_ref, "\n\t");
2611 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2614 slist_free_strings(ref_id_list);
2615 g_slist_free(ref_id_list);
2617 new_ref_str = new_ref->str;
2618 g_string_free(new_ref, FALSE);
2623 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2624 const gchar *fmt, const gchar *qmark,
2625 const gchar *body, gboolean rewrap,
2626 gboolean need_unescape,
2627 const gchar *err_msg)
2629 MsgInfo* dummyinfo = NULL;
2630 gchar *quote_str = NULL;
2632 gboolean prev_autowrap;
2633 const gchar *trimmed_body = body;
2634 gint cursor_pos = -1;
2635 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2636 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2641 SIGNAL_BLOCK(buffer);
2644 dummyinfo = compose_msginfo_new_from_compose(compose);
2645 msginfo = dummyinfo;
2648 if (qmark != NULL) {
2650 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
2651 compose->gtkaspell);
2653 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
2655 quote_fmt_scan_string(qmark);
2658 buf = quote_fmt_get_buffer();
2660 alertpanel_error(_("Quote mark format error."));
2662 Xstrdup_a(quote_str, buf, goto error)
2665 if (fmt && *fmt != '\0') {
2668 while (*trimmed_body == '\n')
2672 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account,
2673 compose->gtkaspell);
2675 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account);
2677 if (need_unescape) {
2680 /* decode \-escape sequences in the internal representation of the quote format */
2681 tmp = malloc(strlen(fmt)+1);
2682 pref_get_unescaped_pref(tmp, fmt);
2683 quote_fmt_scan_string(tmp);
2687 quote_fmt_scan_string(fmt);
2691 buf = quote_fmt_get_buffer();
2693 gint line = quote_fmt_get_line();
2694 alertpanel_error(err_msg, line);
2700 prev_autowrap = compose->autowrap;
2701 compose->autowrap = FALSE;
2703 mark = gtk_text_buffer_get_insert(buffer);
2704 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2705 if (g_utf8_validate(buf, -1, NULL)) {
2706 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2708 gchar *tmpout = NULL;
2709 tmpout = conv_codeset_strdup
2710 (buf, conv_get_locale_charset_str_no_utf8(),
2712 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2714 tmpout = g_malloc(strlen(buf)*2+1);
2715 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2717 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2721 cursor_pos = quote_fmt_get_cursor_pos();
2722 compose->set_cursor_pos = cursor_pos;
2723 if (cursor_pos == -1) {
2726 gtk_text_buffer_get_start_iter(buffer, &iter);
2727 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2728 gtk_text_buffer_place_cursor(buffer, &iter);
2730 compose->autowrap = prev_autowrap;
2731 if (compose->autowrap && rewrap)
2732 compose_wrap_all(compose);
2739 SIGNAL_UNBLOCK(buffer);
2741 procmsg_msginfo_free( dummyinfo );
2746 /* if ml_post is of type addr@host and from is of type
2747 * addr-anything@host, return TRUE
2749 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2751 gchar *left_ml = NULL;
2752 gchar *right_ml = NULL;
2753 gchar *left_from = NULL;
2754 gchar *right_from = NULL;
2755 gboolean result = FALSE;
2757 if (!ml_post || !from)
2760 left_ml = g_strdup(ml_post);
2761 if (strstr(left_ml, "@")) {
2762 right_ml = strstr(left_ml, "@")+1;
2763 *(strstr(left_ml, "@")) = '\0';
2766 left_from = g_strdup(from);
2767 if (strstr(left_from, "@")) {
2768 right_from = strstr(left_from, "@")+1;
2769 *(strstr(left_from, "@")) = '\0';
2772 if (left_ml && left_from && right_ml && right_from
2773 && !strncmp(left_from, left_ml, strlen(left_ml))
2774 && !strcmp(right_from, right_ml)) {
2783 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2785 gchar *my_addr1, *my_addr2;
2787 if (!addr1 || !addr2)
2790 Xstrdup_a(my_addr1, addr1, return FALSE);
2791 Xstrdup_a(my_addr2, addr2, return FALSE);
2793 extract_address(my_addr1);
2794 extract_address(my_addr2);
2796 return !strcasecmp(my_addr1, my_addr2);
2799 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2800 gboolean to_all, gboolean to_ml,
2802 gboolean followup_and_reply_to)
2804 GSList *cc_list = NULL;
2807 gchar *replyto = NULL;
2808 GHashTable *to_table;
2810 gboolean reply_to_ml = FALSE;
2811 gboolean default_reply_to = FALSE;
2813 g_return_if_fail(compose->account != NULL);
2814 g_return_if_fail(msginfo != NULL);
2816 reply_to_ml = to_ml && compose->ml_post;
2818 default_reply_to = msginfo->folder &&
2819 msginfo->folder->prefs->enable_default_reply_to;
2821 if (compose->account->protocol != A_NNTP) {
2822 if (reply_to_ml && !default_reply_to) {
2824 gboolean is_subscr = is_subscription(compose->ml_post,
2827 /* normal answer to ml post with a reply-to */
2828 compose_entry_append(compose,
2831 if (compose->replyto
2832 && !same_address(compose->ml_post, compose->replyto))
2833 compose_entry_append(compose,
2837 /* answer to subscription confirmation */
2838 if (compose->replyto)
2839 compose_entry_append(compose,
2842 else if (msginfo->from)
2843 compose_entry_append(compose,
2848 else if (!(to_all || to_sender) && default_reply_to) {
2849 compose_entry_append(compose,
2850 msginfo->folder->prefs->default_reply_to,
2852 compose_entry_mark_default_to(compose,
2853 msginfo->folder->prefs->default_reply_to);
2858 Xstrdup_a(tmp1, msginfo->from, return);
2859 extract_address(tmp1);
2860 if (to_all || to_sender ||
2861 !account_find_from_address(tmp1))
2862 compose_entry_append(compose,
2863 (compose->replyto && !to_sender)
2864 ? compose->replyto :
2865 msginfo->from ? msginfo->from : "",
2867 else if (!to_all && !to_sender) {
2868 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2869 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
2870 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2871 compose_entry_append(compose,
2872 msginfo->from ? msginfo->from : "",
2875 /* replying to own mail, use original recp */
2876 compose_entry_append(compose,
2877 msginfo->to ? msginfo->to : "",
2879 compose_entry_append(compose,
2880 msginfo->cc ? msginfo->cc : "",
2886 if (to_sender || (compose->followup_to &&
2887 !strncmp(compose->followup_to, "poster", 6)))
2888 compose_entry_append
2890 (compose->replyto ? compose->replyto :
2891 msginfo->from ? msginfo->from : ""),
2894 else if (followup_and_reply_to || to_all) {
2895 compose_entry_append
2897 (compose->replyto ? compose->replyto :
2898 msginfo->from ? msginfo->from : ""),
2901 compose_entry_append
2903 compose->followup_to ? compose->followup_to :
2904 compose->newsgroups ? compose->newsgroups : "",
2905 COMPOSE_NEWSGROUPS);
2908 compose_entry_append
2910 compose->followup_to ? compose->followup_to :
2911 compose->newsgroups ? compose->newsgroups : "",
2912 COMPOSE_NEWSGROUPS);
2915 if (msginfo->subject && *msginfo->subject) {
2919 buf = p = g_strdup(msginfo->subject);
2920 p += subject_get_prefix_length(p);
2921 memmove(buf, p, strlen(p) + 1);
2923 buf2 = g_strdup_printf("Re: %s", buf);
2924 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2929 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
2931 if (to_ml && compose->ml_post) return;
2932 if (!to_all || compose->account->protocol == A_NNTP) return;
2934 if (compose->replyto) {
2935 Xstrdup_a(replyto, compose->replyto, return);
2936 extract_address(replyto);
2938 if (msginfo->from) {
2939 Xstrdup_a(from, msginfo->from, return);
2940 extract_address(from);
2943 if (replyto && from)
2944 cc_list = address_list_append_with_comments(cc_list, from);
2945 if (to_all && msginfo->folder &&
2946 msginfo->folder->prefs->enable_default_reply_to)
2947 cc_list = address_list_append_with_comments(cc_list,
2948 msginfo->folder->prefs->default_reply_to);
2949 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
2950 cc_list = address_list_append_with_comments(cc_list, compose->cc);
2952 to_table = g_hash_table_new(g_str_hash, g_str_equal);
2954 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
2955 if (compose->account) {
2956 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
2957 GINT_TO_POINTER(1));
2959 /* remove address on To: and that of current account */
2960 for (cur = cc_list; cur != NULL; ) {
2961 GSList *next = cur->next;
2964 addr = g_utf8_strdown(cur->data, -1);
2965 extract_address(addr);
2967 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
2968 cc_list = g_slist_remove(cc_list, cur->data);
2970 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
2974 hash_free_strings(to_table);
2975 g_hash_table_destroy(to_table);
2978 for (cur = cc_list; cur != NULL; cur = cur->next)
2979 compose_entry_append(compose, (gchar *)cur->data,
2981 slist_free_strings(cc_list);
2982 g_slist_free(cc_list);
2987 #define SET_ENTRY(entry, str) \
2990 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
2993 #define SET_ADDRESS(type, str) \
2996 compose_entry_append(compose, str, type); \
2999 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3001 g_return_if_fail(msginfo != NULL);
3003 SET_ENTRY(subject_entry, msginfo->subject);
3004 SET_ENTRY(from_name, msginfo->from);
3005 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3006 SET_ADDRESS(COMPOSE_CC, compose->cc);
3007 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3008 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3009 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3010 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3012 compose_update_priority_menu_item(compose);
3013 compose_update_privacy_system_menu_item(compose, FALSE);
3014 compose_show_first_last_header(compose, TRUE);
3020 static void compose_insert_sig(Compose *compose, gboolean replace)
3022 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3023 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3025 GtkTextIter iter, iter_end;
3027 gboolean prev_autowrap;
3028 gboolean found = FALSE;
3029 gboolean exists = FALSE;
3031 g_return_if_fail(compose->account != NULL);
3035 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3036 G_CALLBACK(compose_changed_cb),
3039 mark = gtk_text_buffer_get_insert(buffer);
3040 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3041 cur_pos = gtk_text_iter_get_offset (&iter);
3043 gtk_text_buffer_get_end_iter(buffer, &iter);
3045 exists = (compose->sig_str != NULL);
3048 GtkTextIter first_iter, start_iter, end_iter;
3050 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3052 if (!exists || compose->sig_str[0] == '\0')
3055 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3056 compose->signature_tag);
3059 /* include previous \n\n */
3060 gtk_text_iter_backward_chars(&first_iter, 2);
3061 start_iter = first_iter;
3062 end_iter = first_iter;
3064 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3065 compose->signature_tag);
3066 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3067 compose->signature_tag);
3069 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3075 g_free(compose->sig_str);
3076 compose->sig_str = compose_get_signature_str(compose);
3078 cur_pos = gtk_text_iter_get_offset(&iter);
3080 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3081 g_free(compose->sig_str);
3082 compose->sig_str = NULL;
3084 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3086 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3087 gtk_text_iter_forward_chars(&iter, 2);
3088 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3089 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3091 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3092 cur_pos = gtk_text_buffer_get_char_count (buffer);
3094 /* put the cursor where it should be
3095 * either where the quote_fmt says, either before the signature */
3096 if (compose->set_cursor_pos < 0)
3097 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3099 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3100 compose->set_cursor_pos);
3102 gtk_text_buffer_place_cursor(buffer, &iter);
3103 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3104 G_CALLBACK(compose_changed_cb),
3110 static gchar *compose_get_signature_str(Compose *compose)
3112 gchar *sig_body = NULL;
3113 gchar *sig_str = NULL;
3114 gchar *utf8_sig_str = NULL;
3116 g_return_val_if_fail(compose->account != NULL, NULL);
3118 if (!compose->account->sig_path)
3121 if (compose->account->sig_type == SIG_FILE) {
3122 if (!is_file_or_fifo_exist(compose->account->sig_path)) {
3123 g_warning("can't open signature file: %s\n",
3124 compose->account->sig_path);
3129 if (compose->account->sig_type == SIG_COMMAND)
3130 sig_body = get_command_output(compose->account->sig_path);
3134 tmp = file_read_to_str(compose->account->sig_path);
3137 sig_body = normalize_newlines(tmp);
3141 if (compose->account->sig_sep) {
3142 sig_str = g_strconcat("\n\n", compose->account->sig_sep, "\n", sig_body,
3146 sig_str = g_strconcat("\n\n", sig_body, NULL);
3149 if (g_utf8_validate(sig_str, -1, NULL) == TRUE)
3150 utf8_sig_str = sig_str;
3152 utf8_sig_str = conv_codeset_strdup
3153 (sig_str, conv_get_locale_charset_str_no_utf8(),
3159 return utf8_sig_str;
3162 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3165 GtkTextBuffer *buffer;
3168 const gchar *cur_encoding;
3169 gchar buf[BUFFSIZE];
3172 gboolean prev_autowrap;
3173 gboolean badtxt = FALSE;
3175 g_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3177 if ((fp = g_fopen(file, "rb")) == NULL) {
3178 FILE_OP_ERROR(file, "fopen");
3179 return COMPOSE_INSERT_READ_ERROR;
3182 prev_autowrap = compose->autowrap;
3183 compose->autowrap = FALSE;
3185 text = GTK_TEXT_VIEW(compose->text);
3186 buffer = gtk_text_view_get_buffer(text);
3187 mark = gtk_text_buffer_get_insert(buffer);
3188 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3190 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3191 G_CALLBACK(text_inserted),
3194 cur_encoding = conv_get_locale_charset_str_no_utf8();
3196 while (fgets(buf, sizeof(buf), fp) != NULL) {
3199 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3200 str = g_strdup(buf);
3202 str = conv_codeset_strdup
3203 (buf, cur_encoding, CS_INTERNAL);
3206 /* strip <CR> if DOS/Windows file,
3207 replace <CR> with <LF> if Macintosh file. */
3210 if (len > 0 && str[len - 1] != '\n') {
3212 if (str[len] == '\r') str[len] = '\n';
3215 gtk_text_buffer_insert(buffer, &iter, str, -1);
3219 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3220 G_CALLBACK(text_inserted),
3222 compose->autowrap = prev_autowrap;
3223 if (compose->autowrap)
3224 compose_wrap_all(compose);
3229 return COMPOSE_INSERT_INVALID_CHARACTER;
3231 return COMPOSE_INSERT_SUCCESS;
3234 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3235 const gchar *filename,
3236 const gchar *content_type)
3244 GtkListStore *store;
3246 gboolean has_binary = FALSE;
3248 if (!is_file_exist(file)) {
3249 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3250 gboolean result = FALSE;
3251 if (file_from_uri && is_file_exist(file_from_uri)) {
3252 result = compose_attach_append(
3253 compose, file_from_uri,
3257 g_free(file_from_uri);
3260 alertpanel_error("File %s doesn't exist\n", filename);
3263 if ((size = get_file_size(file)) < 0) {
3264 alertpanel_error("Can't get file size of %s\n", filename);
3268 alertpanel_error(_("File %s is empty."), filename);
3271 if ((fp = g_fopen(file, "rb")) == NULL) {
3272 alertpanel_error(_("Can't read %s."), filename);
3277 ainfo = g_new0(AttachInfo, 1);
3278 auto_ainfo = g_auto_pointer_new_with_free
3279 (ainfo, (GFreeFunc) compose_attach_info_free);
3280 ainfo->file = g_strdup(file);
3283 ainfo->content_type = g_strdup(content_type);
3284 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3286 MsgFlags flags = {0, 0};
3288 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3289 ainfo->encoding = ENC_7BIT;
3291 ainfo->encoding = ENC_8BIT;
3293 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3294 if (msginfo && msginfo->subject)
3295 name = g_strdup(msginfo->subject);
3297 name = g_path_get_basename(filename ? filename : file);
3299 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3301 procmsg_msginfo_free(msginfo);
3303 if (!g_ascii_strncasecmp(content_type, "text", 4))
3304 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3306 ainfo->encoding = ENC_BASE64;
3307 name = g_path_get_basename(filename ? filename : file);
3308 ainfo->name = g_strdup(name);
3312 ainfo->content_type = procmime_get_mime_type(file);
3313 if (!ainfo->content_type) {
3314 ainfo->content_type =
3315 g_strdup("application/octet-stream");
3316 ainfo->encoding = ENC_BASE64;
3317 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3319 procmime_get_encoding_for_text_file(file, &has_binary);
3321 ainfo->encoding = ENC_BASE64;
3322 name = g_path_get_basename(filename ? filename : file);
3323 ainfo->name = g_strdup(name);
3327 if (ainfo->name != NULL
3328 && !strcmp(ainfo->name, ".")) {
3329 g_free(ainfo->name);
3333 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3334 g_free(ainfo->content_type);
3335 ainfo->content_type = g_strdup("application/octet-stream");
3339 size_text = to_human_readable(size);
3341 store = GTK_LIST_STORE(gtk_tree_view_get_model
3342 (GTK_TREE_VIEW(compose->attach_clist)));
3344 gtk_list_store_append(store, &iter);
3345 gtk_list_store_set(store, &iter,
3346 COL_MIMETYPE, ainfo->content_type,
3347 COL_SIZE, size_text,
3348 COL_NAME, ainfo->name,
3350 COL_AUTODATA, auto_ainfo,
3353 g_auto_pointer_free(auto_ainfo);
3357 static void compose_use_signing(Compose *compose, gboolean use_signing)
3359 GtkItemFactory *ifactory;
3360 GtkWidget *menuitem = NULL;
3362 compose->use_signing = use_signing;
3363 ifactory = gtk_item_factory_from_widget(compose->menubar);
3364 menuitem = gtk_item_factory_get_item
3365 (ifactory, "/Options/Sign");
3366 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
3370 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3372 GtkItemFactory *ifactory;
3373 GtkWidget *menuitem = NULL;
3375 compose->use_encryption = use_encryption;
3376 ifactory = gtk_item_factory_from_widget(compose->menubar);
3377 menuitem = gtk_item_factory_get_item
3378 (ifactory, "/Options/Encrypt");
3380 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
3384 #define NEXT_PART_NOT_CHILD(info) \
3386 node = info->node; \
3387 while (node->children) \
3388 node = g_node_last_child(node); \
3389 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3392 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3396 MimeInfo *firsttext = NULL;
3397 MimeInfo *encrypted = NULL;
3400 const gchar *partname = NULL;
3402 mimeinfo = procmime_scan_message(msginfo);
3403 if (!mimeinfo) return;
3405 if (mimeinfo->node->children == NULL) {
3406 procmime_mimeinfo_free_all(mimeinfo);
3410 /* find first content part */
3411 child = (MimeInfo *) mimeinfo->node->children->data;
3412 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3413 child = (MimeInfo *)child->node->children->data;
3415 if (child->type == MIMETYPE_TEXT) {
3417 debug_print("First text part found\n");
3418 } else if (compose->mode == COMPOSE_REEDIT &&
3419 child->type == MIMETYPE_APPLICATION &&
3420 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3421 encrypted = (MimeInfo *)child->node->parent->data;
3424 child = (MimeInfo *) mimeinfo->node->children->data;
3425 while (child != NULL) {
3428 if (child == encrypted) {
3429 /* skip this part of tree */
3430 NEXT_PART_NOT_CHILD(child);
3434 if (child->type == MIMETYPE_MULTIPART) {
3435 /* get the actual content */
3436 child = procmime_mimeinfo_next(child);
3440 if (child == firsttext) {
3441 child = procmime_mimeinfo_next(child);
3445 outfile = procmime_get_tmp_file_name(child);
3446 if ((err = procmime_get_part(outfile, child)) < 0)
3447 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3449 gchar *content_type;
3451 content_type = procmime_get_content_type_str(child->type, child->subtype);
3453 /* if we meet a pgp signature, we don't attach it, but
3454 * we force signing. */
3455 if ((strcmp(content_type, "application/pgp-signature") &&
3456 strcmp(content_type, "application/pkcs7-signature") &&
3457 strcmp(content_type, "application/x-pkcs7-signature"))
3458 || compose->mode == COMPOSE_REDIRECT) {
3459 partname = procmime_mimeinfo_get_parameter(child, "filename");
3460 if (partname == NULL)
3461 partname = procmime_mimeinfo_get_parameter(child, "name");
3462 if (partname == NULL)
3464 compose_attach_append(compose, outfile,
3465 partname, content_type);
3467 compose_force_signing(compose, compose->account);
3469 g_free(content_type);
3472 NEXT_PART_NOT_CHILD(child);
3474 procmime_mimeinfo_free_all(mimeinfo);
3477 #undef NEXT_PART_NOT_CHILD
3482 WAIT_FOR_INDENT_CHAR,
3483 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3486 /* return indent length, we allow:
3487 indent characters followed by indent characters or spaces/tabs,
3488 alphabets and numbers immediately followed by indent characters,
3489 and the repeating sequences of the above
3490 If quote ends with multiple spaces, only the first one is included. */
3491 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3492 const GtkTextIter *start, gint *len)
3494 GtkTextIter iter = *start;
3498 IndentState state = WAIT_FOR_INDENT_CHAR;
3501 gint alnum_count = 0;
3502 gint space_count = 0;
3505 if (prefs_common.quote_chars == NULL) {
3509 while (!gtk_text_iter_ends_line(&iter)) {
3510 wc = gtk_text_iter_get_char(&iter);
3511 if (g_unichar_iswide(wc))
3513 clen = g_unichar_to_utf8(wc, ch);
3517 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3518 is_space = g_unichar_isspace(wc);
3520 if (state == WAIT_FOR_INDENT_CHAR) {
3521 if (!is_indent && !g_unichar_isalnum(wc))
3524 quote_len += alnum_count + space_count + 1;
3525 alnum_count = space_count = 0;
3526 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3529 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3530 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3534 else if (is_indent) {
3535 quote_len += alnum_count + space_count + 1;
3536 alnum_count = space_count = 0;
3539 state = WAIT_FOR_INDENT_CHAR;
3543 gtk_text_iter_forward_char(&iter);
3546 if (quote_len > 0 && space_count > 0)
3552 if (quote_len > 0) {
3554 gtk_text_iter_forward_chars(&iter, quote_len);
3555 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3561 /* return TRUE if the line is itemized */
3562 static gboolean compose_is_itemized(GtkTextBuffer *buffer,
3563 const GtkTextIter *start)
3565 GtkTextIter iter = *start;
3570 if (gtk_text_iter_ends_line(&iter))
3574 wc = gtk_text_iter_get_char(&iter);
3575 if (!g_unichar_isspace(wc))
3577 gtk_text_iter_forward_char(&iter);
3578 if (gtk_text_iter_ends_line(&iter))
3582 clen = g_unichar_to_utf8(wc, ch);
3586 if (!strchr("*-+", ch[0]))
3589 gtk_text_iter_forward_char(&iter);
3590 if (gtk_text_iter_ends_line(&iter))
3592 wc = gtk_text_iter_get_char(&iter);
3593 if (g_unichar_isspace(wc))
3599 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3600 const GtkTextIter *start,
3601 GtkTextIter *break_pos,
3605 GtkTextIter iter = *start, line_end = *start;
3606 PangoLogAttr *attrs;
3613 gboolean can_break = FALSE;
3614 gboolean do_break = FALSE;
3615 gboolean was_white = FALSE;
3616 gboolean prev_dont_break = FALSE;
3618 gtk_text_iter_forward_to_line_end(&line_end);
3619 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3620 len = g_utf8_strlen(str, -1);
3624 g_warning("compose_get_line_break_pos: len = 0!\n");
3628 /* g_print("breaking line: %d: %s (len = %d)\n",
3629 gtk_text_iter_get_line(&iter), str, len); */
3631 attrs = g_new(PangoLogAttr, len + 1);
3633 pango_default_break(str, -1, NULL, attrs, len + 1);
3637 /* skip quote and leading spaces */
3638 for (i = 0; *p != '\0' && i < len; i++) {
3641 wc = g_utf8_get_char(p);
3642 if (i >= quote_len && !g_unichar_isspace(wc))
3644 if (g_unichar_iswide(wc))
3646 else if (*p == '\t')
3650 p = g_utf8_next_char(p);
3653 for (; *p != '\0' && i < len; i++) {
3654 PangoLogAttr *attr = attrs + i;
3658 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3661 was_white = attr->is_white;
3663 /* don't wrap URI */
3664 if ((uri_len = get_uri_len(p)) > 0) {
3666 if (pos > 0 && col > max_col) {
3676 wc = g_utf8_get_char(p);
3677 if (g_unichar_iswide(wc)) {
3679 if (prev_dont_break && can_break && attr->is_line_break)
3681 } else if (*p == '\t')
3685 if (pos > 0 && col > max_col) {
3690 if (*p == '-' || *p == '/')
3691 prev_dont_break = TRUE;
3693 prev_dont_break = FALSE;
3695 p = g_utf8_next_char(p);
3699 debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);