2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2011 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>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #include "addressbook.h"
64 #include "folderview.h"
67 #include "stock_pixmap.h"
68 #include "send_message.h"
71 #include "customheader.h"
72 #include "prefs_common.h"
73 #include "prefs_account.h"
77 #include "procheader.h"
79 #include "statusbar.h"
82 #include "quoted-printable.h"
86 #include "gtkshruler.h"
88 #include "alertpanel.h"
89 #include "manage_window.h"
91 #include "addr_compl.h"
92 #include "quote_fmt.h"
94 #include "foldersel.h"
97 #include "message_search.h"
102 #include "autofaces.h"
103 #include "spell_entry.h"
116 #define N_ATTACH_COLS (N_COL_COLUMNS)
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
132 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
134 } ComposeCallAdvancedAction;
138 PRIORITY_HIGHEST = 1,
147 COMPOSE_INSERT_SUCCESS,
148 COMPOSE_INSERT_READ_ERROR,
149 COMPOSE_INSERT_INVALID_CHARACTER,
150 COMPOSE_INSERT_NO_FILE
151 } ComposeInsertResult;
155 COMPOSE_WRITE_FOR_SEND,
156 COMPOSE_WRITE_FOR_STORE
161 COMPOSE_QUOTE_FORCED,
168 SUBJECT_FIELD_PRESENT,
173 #define B64_LINE_SIZE 57
174 #define B64_BUFFSIZE 77
176 #define MAX_REFERENCES_LEN 999
178 static GList *compose_list = NULL;
180 static Compose *compose_generic_new (PrefsAccount *account,
183 GPtrArray *attach_files,
184 GList *listAddress );
186 static Compose *compose_create (PrefsAccount *account,
191 static void compose_entry_mark_default_to (Compose *compose,
192 const gchar *address);
193 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
194 ComposeQuoteMode quote_mode,
198 static Compose *compose_forward_multiple (PrefsAccount *account,
199 GSList *msginfo_list);
200 static Compose *compose_reply (MsgInfo *msginfo,
201 ComposeQuoteMode quote_mode,
206 static Compose *compose_reply_mode (ComposeMode mode,
207 GSList *msginfo_list,
209 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
210 static void compose_update_privacy_systems_menu(Compose *compose);
212 static GtkWidget *compose_account_option_menu_create
214 static void compose_set_out_encoding (Compose *compose);
215 static void compose_set_template_menu (Compose *compose);
216 static void compose_destroy (Compose *compose);
218 static MailField compose_entries_set (Compose *compose,
220 ComposeEntryType to_type);
221 static gint compose_parse_header (Compose *compose,
223 static gchar *compose_parse_references (const gchar *ref,
226 static gchar *compose_quote_fmt (Compose *compose,
232 gboolean need_unescape,
233 const gchar *err_msg);
235 static void compose_reply_set_entry (Compose *compose,
241 followup_and_reply_to);
242 static void compose_reedit_set_entry (Compose *compose,
245 static void compose_insert_sig (Compose *compose,
247 static ComposeInsertResult compose_insert_file (Compose *compose,
250 static gboolean compose_attach_append (Compose *compose,
253 const gchar *content_type,
254 const gchar *charset);
255 static void compose_attach_parts (Compose *compose,
258 static gboolean compose_beautify_paragraph (Compose *compose,
259 GtkTextIter *par_iter,
261 static void compose_wrap_all (Compose *compose);
262 static void compose_wrap_all_full (Compose *compose,
265 static void compose_set_title (Compose *compose);
266 static void compose_select_account (Compose *compose,
267 PrefsAccount *account,
270 static PrefsAccount *compose_current_mail_account(void);
271 /* static gint compose_send (Compose *compose); */
272 static gboolean compose_check_for_valid_recipient
274 static gboolean compose_check_entries (Compose *compose,
275 gboolean check_everything);
276 static gint compose_write_to_file (Compose *compose,
279 gboolean attach_parts);
280 static gint compose_write_body_to_file (Compose *compose,
282 static gint compose_remove_reedit_target (Compose *compose,
284 static void compose_remove_draft (Compose *compose);
285 static gint compose_queue_sub (Compose *compose,
289 gboolean check_subject,
290 gboolean remove_reedit_target);
291 static int compose_add_attachments (Compose *compose,
293 static gchar *compose_get_header (Compose *compose);
295 static void compose_convert_header (Compose *compose,
300 gboolean addr_field);
302 static void compose_attach_info_free (AttachInfo *ainfo);
303 static void compose_attach_remove_selected (GtkAction *action,
306 static void compose_template_apply (Compose *compose,
309 static void compose_attach_property (GtkAction *action,
311 static void compose_attach_property_create (gboolean *cancelled);
312 static void attach_property_ok (GtkWidget *widget,
313 gboolean *cancelled);
314 static void attach_property_cancel (GtkWidget *widget,
315 gboolean *cancelled);
316 static gint attach_property_delete_event (GtkWidget *widget,
318 gboolean *cancelled);
319 static gboolean attach_property_key_pressed (GtkWidget *widget,
321 gboolean *cancelled);
323 static void compose_exec_ext_editor (Compose *compose);
325 static gint compose_exec_ext_editor_real (const gchar *file);
326 static gboolean compose_ext_editor_kill (Compose *compose);
327 static gboolean compose_input_cb (GIOChannel *source,
328 GIOCondition condition,
330 static void compose_set_ext_editor_sensitive (Compose *compose,
332 #endif /* G_OS_UNIX */
334 static void compose_undo_state_changed (UndoMain *undostruct,
339 static void compose_create_header_entry (Compose *compose);
340 static void compose_add_header_entry (Compose *compose, const gchar *header,
341 gchar *text, ComposePrefType pref_type);
342 static void compose_remove_header_entries(Compose *compose);
344 static void compose_update_priority_menu_item(Compose * compose);
346 static void compose_spell_menu_changed (void *data);
347 static void compose_dict_changed (void *data);
349 static void compose_add_field_list ( Compose *compose,
350 GList *listAddress );
352 /* callback functions */
354 static gboolean compose_edit_size_alloc (GtkEditable *widget,
355 GtkAllocation *allocation,
356 GtkSHRuler *shruler);
357 static void account_activated (GtkComboBox *optmenu,
359 static void attach_selected (GtkTreeView *tree_view,
360 GtkTreePath *tree_path,
361 GtkTreeViewColumn *column,
363 static gboolean attach_button_pressed (GtkWidget *widget,
364 GdkEventButton *event,
366 static gboolean attach_key_pressed (GtkWidget *widget,
369 static void compose_send_cb (GtkAction *action, gpointer data);
370 static void compose_send_later_cb (GtkAction *action, gpointer data);
372 static void compose_save_cb (GtkAction *action,
375 static void compose_attach_cb (GtkAction *action,
377 static void compose_insert_file_cb (GtkAction *action,
379 static void compose_insert_sig_cb (GtkAction *action,
382 static void compose_close_cb (GtkAction *action,
385 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
387 static void compose_address_cb (GtkAction *action,
389 static void about_show_cb (GtkAction *action,
391 static void compose_template_activate_cb(GtkWidget *widget,
394 static void compose_ext_editor_cb (GtkAction *action,
397 static gint compose_delete_cb (GtkWidget *widget,
401 static void compose_undo_cb (GtkAction *action,
403 static void compose_redo_cb (GtkAction *action,
405 static void compose_cut_cb (GtkAction *action,
407 static void compose_copy_cb (GtkAction *action,
409 static void compose_paste_cb (GtkAction *action,
411 static void compose_paste_as_quote_cb (GtkAction *action,
413 static void compose_paste_no_wrap_cb (GtkAction *action,
415 static void compose_paste_wrap_cb (GtkAction *action,
417 static void compose_allsel_cb (GtkAction *action,
420 static void compose_advanced_action_cb (GtkAction *action,
423 static void compose_grab_focus_cb (GtkWidget *widget,
426 static void compose_changed_cb (GtkTextBuffer *textbuf,
429 static void compose_wrap_cb (GtkAction *action,
431 static void compose_wrap_all_cb (GtkAction *action,
433 static void compose_find_cb (GtkAction *action,
435 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
437 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
440 static void compose_toggle_ruler_cb (GtkToggleAction *action,
442 static void compose_toggle_sign_cb (GtkToggleAction *action,
444 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
446 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
447 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
448 static void activate_privacy_system (Compose *compose,
449 PrefsAccount *account,
451 static void compose_use_signing(Compose *compose, gboolean use_signing);
452 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
453 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
455 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
457 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
458 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
459 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
461 static void compose_attach_drag_received_cb (GtkWidget *widget,
462 GdkDragContext *drag_context,
465 GtkSelectionData *data,
469 static void compose_insert_drag_received_cb (GtkWidget *widget,
470 GdkDragContext *drag_context,
473 GtkSelectionData *data,
477 static void compose_header_drag_received_cb (GtkWidget *widget,
478 GdkDragContext *drag_context,
481 GtkSelectionData *data,
486 static gboolean compose_drag_drop (GtkWidget *widget,
487 GdkDragContext *drag_context,
489 guint time, gpointer user_data);
491 static void text_inserted (GtkTextBuffer *buffer,
496 static Compose *compose_generic_reply(MsgInfo *msginfo,
497 ComposeQuoteMode quote_mode,
501 gboolean followup_and_reply_to,
504 static void compose_headerentry_changed_cb (GtkWidget *entry,
505 ComposeHeaderEntry *headerentry);
506 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
508 ComposeHeaderEntry *headerentry);
509 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
510 ComposeHeaderEntry *headerentry);
512 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
514 static void compose_allow_user_actions (Compose *compose, gboolean allow);
516 static void compose_nothing_cb (GtkAction *action, gpointer data)
522 static void compose_check_all (GtkAction *action, gpointer data);
523 static void compose_highlight_all (GtkAction *action, gpointer data);
524 static void compose_check_backwards (GtkAction *action, gpointer data);
525 static void compose_check_forwards_go (GtkAction *action, gpointer data);
528 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
530 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
533 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
534 FolderItem *folder_item);
536 static void compose_attach_update_label(Compose *compose);
537 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
538 gboolean respect_default_to);
540 static GtkActionEntry compose_popup_entries[] =
542 {"Compose", NULL, "Compose" },
543 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
544 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
545 {"Compose/---", NULL, "---", NULL, NULL, NULL },
546 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
549 static GtkActionEntry compose_entries[] =
551 {"Menu", NULL, "Menu" },
553 {"Message", NULL, N_("_Message") },
554 {"Edit", NULL, N_("_Edit") },
556 {"Spelling", NULL, N_("_Spelling") },
558 {"Options", NULL, N_("_Options") },
559 {"Tools", NULL, N_("_Tools") },
560 {"Help", NULL, N_("_Help") },
562 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
563 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
564 {"Message/---", NULL, "---" },
566 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
567 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
568 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
569 /* {"Message/---", NULL, "---" }, */
570 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
571 /* {"Message/---", NULL, "---" }, */
572 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
575 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
576 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
577 {"Edit/---", NULL, "---" },
579 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
580 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
581 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
583 {"Edit/SpecialPaste", NULL, N_("Special paste") },
584 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
585 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
586 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
588 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
590 {"Edit/Advanced", NULL, N_("A_dvanced") },
591 {"Edit/Advanced/BackChar", NULL, N_("Move a character backward"), "<shift><control>B", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER*/
592 {"Edit/Advanced/ForwChar", NULL, N_("Move a character forward"), "<shift><control>F", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER*/
593 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
594 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
595 {"Edit/Advanced/BegLine", NULL, N_("Move to beginning of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE*/
596 {"Edit/Advanced/EndLine", NULL, N_("Move to end of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE*/
597 {"Edit/Advanced/PrevLine", NULL, N_("Move to previous line"), "<control>P", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE*/
598 {"Edit/Advanced/NextLine", NULL, N_("Move to next line"), "<control>N", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE*/
599 {"Edit/Advanced/DelBackChar", NULL, N_("Delete a character backward"), "<control>H", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER*/
600 {"Edit/Advanced/DelForwChar", NULL, N_("Delete a character forward"), "<control>D", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER*/
601 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
602 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
603 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
604 {"Edit/Advanced/DelEndLine", NULL, N_("Delete to end of line"), "<control>K", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END*/
606 /* {"Edit/---", NULL, "---" }, */
607 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
609 /* {"Edit/---", NULL, "---" }, */
610 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
611 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
612 /* {"Edit/---", NULL, "---" }, */
613 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
616 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
617 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
618 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
619 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
621 {"Spelling/---", NULL, "---" },
622 {"Spelling/Options", NULL, N_("_Options") },
627 {"Options/ReplyMode", NULL, N_("Reply _mode") },
628 {"Options/---", NULL, "---" },
629 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
630 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
632 /* {"Options/---", NULL, "---" }, */
634 {"Options/Priority", NULL, N_("_Priority") },
636 {"Options/Encoding", NULL, N_("Character _encoding") },
637 {"Options/Encoding/---", NULL, "---" },
638 #define ENC_ACTION(cs_char,c_char,string) \
639 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
641 {"Options/Encoding/Western", NULL, N_("Western European") },
642 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
643 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
644 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
645 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
646 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
647 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
648 {"Options/Encoding/Korean", NULL, N_("Korean") },
649 {"Options/Encoding/Thai", NULL, N_("Thai") },
652 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
654 {"Tools/Template", NULL, N_("_Template") },
655 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
656 {"Tools/Actions", NULL, N_("Actio_ns") },
657 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
660 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
663 static GtkToggleActionEntry compose_toggle_entries[] =
665 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
666 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
667 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
668 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
669 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
670 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
671 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
674 static GtkRadioActionEntry compose_radio_rm_entries[] =
676 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
677 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
678 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
679 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
682 static GtkRadioActionEntry compose_radio_prio_entries[] =
684 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
685 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
686 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
687 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
688 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
691 static GtkRadioActionEntry compose_radio_enc_entries[] =
693 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
727 static GtkTargetEntry compose_mime_types[] =
729 {"text/uri-list", 0, 0},
730 {"UTF8_STRING", 0, 0},
734 static gboolean compose_put_existing_to_front(MsgInfo *info)
736 GList *compose_list = compose_get_compose_list();
740 for (elem = compose_list; elem != NULL && elem->data != NULL;
742 Compose *c = (Compose*)elem->data;
744 if (!c->targetinfo || !c->targetinfo->msgid ||
748 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
749 gtkut_window_popup(c->window);
757 static GdkColor quote_color1 =
758 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
759 static GdkColor quote_color2 =
760 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
761 static GdkColor quote_color3 =
762 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
764 static GdkColor quote_bgcolor1 =
765 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
766 static GdkColor quote_bgcolor2 =
767 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
768 static GdkColor quote_bgcolor3 =
769 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
771 static GdkColor signature_color = {
778 static GdkColor uri_color = {
785 static void compose_create_tags(GtkTextView *text, Compose *compose)
787 GtkTextBuffer *buffer;
788 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
789 #if !GTK_CHECK_VERSION(2, 24, 0)
796 buffer = gtk_text_view_get_buffer(text);
798 if (prefs_common.enable_color) {
799 /* grab the quote colors, converting from an int to a GdkColor */
800 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
802 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
804 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
806 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
808 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
810 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
812 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
814 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
817 signature_color = quote_color1 = quote_color2 = quote_color3 =
818 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
821 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
822 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
823 "foreground-gdk", "e_color1,
824 "paragraph-background-gdk", "e_bgcolor1,
826 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
827 "foreground-gdk", "e_color2,
828 "paragraph-background-gdk", "e_bgcolor2,
830 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
831 "foreground-gdk", "e_color3,
832 "paragraph-background-gdk", "e_bgcolor3,
835 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
836 "foreground-gdk", "e_color1,
838 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
839 "foreground-gdk", "e_color2,
841 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
842 "foreground-gdk", "e_color3,
846 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
847 "foreground-gdk", &signature_color,
850 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
851 "foreground-gdk", &uri_color,
853 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
854 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
856 color[0] = quote_color1;
857 color[1] = quote_color2;
858 color[2] = quote_color3;
859 color[3] = quote_bgcolor1;
860 color[4] = quote_bgcolor2;
861 color[5] = quote_bgcolor3;
862 color[6] = signature_color;
863 color[7] = uri_color;
864 #if !GTK_CHECK_VERSION(2, 24, 0)
865 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
866 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
868 for (i = 0; i < 8; i++) {
869 if (success[i] == FALSE) {
872 g_warning("Compose: color allocation failed.\n");
873 style = gtk_widget_get_style(GTK_WIDGET(text));
874 quote_color1 = quote_color2 = quote_color3 =
875 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
876 signature_color = uri_color = black;
882 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
883 GPtrArray *attach_files)
885 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
888 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
890 return compose_generic_new(account, mailto, item, NULL, NULL);
893 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
895 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
898 #define SCROLL_TO_CURSOR(compose) { \
899 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
900 gtk_text_view_get_buffer( \
901 GTK_TEXT_VIEW(compose->text))); \
902 gtk_text_view_scroll_mark_onscreen( \
903 GTK_TEXT_VIEW(compose->text), \
907 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
910 if (folderidentifier) {
911 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
912 prefs_common.compose_save_to_history = add_history(
913 prefs_common.compose_save_to_history, folderidentifier);
914 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
915 prefs_common.compose_save_to_history);
918 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
919 if (folderidentifier)
920 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
922 gtk_entry_set_text(GTK_ENTRY(entry), "");
925 static gchar *compose_get_save_to(Compose *compose)
928 gchar *result = NULL;
929 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
930 result = gtk_editable_get_chars(entry, 0, -1);
933 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
934 prefs_common.compose_save_to_history = add_history(
935 prefs_common.compose_save_to_history, result);
936 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
937 prefs_common.compose_save_to_history);
942 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
943 GPtrArray *attach_files, GList *listAddress )
946 GtkTextView *textview;
947 GtkTextBuffer *textbuf;
949 const gchar *subject_format = NULL;
950 const gchar *body_format = NULL;
951 gchar *mailto_from = NULL;
952 PrefsAccount *mailto_account = NULL;
953 MsgInfo* dummyinfo = NULL;
954 gint cursor_pos = -1;
955 MailField mfield = NO_FIELD_PRESENT;
959 /* check if mailto defines a from */
960 if (mailto && *mailto != '\0') {
961 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
962 /* mailto defines a from, check if we can get account prefs from it,
963 if not, the account prefs will be guessed using other ways, but we'll keep
966 mailto_account = account_find_from_address(mailto_from, TRUE);
968 account = mailto_account;
971 /* if no account prefs set from mailto, set if from folder prefs (if any) */
972 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
973 account = account_find_from_id(item->prefs->default_account);
975 /* if no account prefs set, fallback to the current one */
976 if (!account) account = cur_account;
977 cm_return_val_if_fail(account != NULL, NULL);
979 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
981 /* override from name if mailto asked for it */
983 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
986 /* override from name according to folder properties */
987 if (item && item->prefs &&
988 item->prefs->compose_with_format &&
989 item->prefs->compose_override_from_format &&
990 *item->prefs->compose_override_from_format != '\0') {
995 dummyinfo = compose_msginfo_new_from_compose(compose);
997 /* decode \-escape sequences in the internal representation of the quote format */
998 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
999 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1002 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1003 compose->gtkaspell);
1005 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1007 quote_fmt_scan_string(tmp);
1010 buf = quote_fmt_get_buffer();
1012 alertpanel_error(_("New message From format error."));
1014 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1015 quote_fmt_reset_vartable();
1020 compose->replyinfo = NULL;
1021 compose->fwdinfo = NULL;
1023 textview = GTK_TEXT_VIEW(compose->text);
1024 textbuf = gtk_text_view_get_buffer(textview);
1025 compose_create_tags(textview, compose);
1027 undo_block(compose->undostruct);
1029 compose_set_dictionaries_from_folder_prefs(compose, item);
1032 if (account->auto_sig)
1033 compose_insert_sig(compose, FALSE);
1034 gtk_text_buffer_get_start_iter(textbuf, &iter);
1035 gtk_text_buffer_place_cursor(textbuf, &iter);
1037 if (account->protocol != A_NNTP) {
1038 if (mailto && *mailto != '\0') {
1039 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1042 compose_set_folder_prefs(compose, item, TRUE);
1044 if (item && item->ret_rcpt) {
1045 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1048 if (mailto && *mailto != '\0') {
1049 if (!strchr(mailto, '@'))
1050 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1052 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1053 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1054 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1055 mfield = TO_FIELD_PRESENT;
1058 * CLAWS: just don't allow return receipt request, even if the user
1059 * may want to send an email. simple but foolproof.
1061 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1063 compose_add_field_list( compose, listAddress );
1065 if (item && item->prefs && item->prefs->compose_with_format) {
1066 subject_format = item->prefs->compose_subject_format;
1067 body_format = item->prefs->compose_body_format;
1068 } else if (account->compose_with_format) {
1069 subject_format = account->compose_subject_format;
1070 body_format = account->compose_body_format;
1071 } else if (prefs_common.compose_with_format) {
1072 subject_format = prefs_common.compose_subject_format;
1073 body_format = prefs_common.compose_body_format;
1076 if (subject_format || body_format) {
1079 && *subject_format != '\0' )
1081 gchar *subject = NULL;
1086 dummyinfo = compose_msginfo_new_from_compose(compose);
1088 /* decode \-escape sequences in the internal representation of the quote format */
1089 tmp = g_malloc(strlen(subject_format)+1);
1090 pref_get_unescaped_pref(tmp, subject_format);
1092 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1094 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1095 compose->gtkaspell);
1097 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1099 quote_fmt_scan_string(tmp);
1102 buf = quote_fmt_get_buffer();
1104 alertpanel_error(_("New message subject format error."));
1106 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1107 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1108 quote_fmt_reset_vartable();
1112 mfield = SUBJECT_FIELD_PRESENT;
1116 && *body_format != '\0' )
1119 GtkTextBuffer *buffer;
1120 GtkTextIter start, end;
1124 dummyinfo = compose_msginfo_new_from_compose(compose);
1126 text = GTK_TEXT_VIEW(compose->text);
1127 buffer = gtk_text_view_get_buffer(text);
1128 gtk_text_buffer_get_start_iter(buffer, &start);
1129 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1130 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1132 compose_quote_fmt(compose, dummyinfo,
1134 NULL, tmp, FALSE, TRUE,
1135 _("The body of the \"New message\" template has an error at line %d."));
1136 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1137 quote_fmt_reset_vartable();
1141 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1142 gtkaspell_highlight_all(compose->gtkaspell);
1144 mfield = BODY_FIELD_PRESENT;
1148 procmsg_msginfo_free( dummyinfo );
1154 for (i = 0; i < attach_files->len; i++) {
1155 file = g_ptr_array_index(attach_files, i);
1156 compose_attach_append(compose, file, file, NULL, NULL);
1160 compose_show_first_last_header(compose, TRUE);
1162 /* Set save folder */
1163 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1164 gchar *folderidentifier;
1166 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1167 folderidentifier = folder_item_get_identifier(item);
1168 compose_set_save_to(compose, folderidentifier);
1169 g_free(folderidentifier);
1172 /* Place cursor according to provided input (mfield) */
1174 case NO_FIELD_PRESENT:
1175 if (compose->header_last)
1176 gtk_widget_grab_focus(compose->header_last->entry);
1178 case TO_FIELD_PRESENT:
1179 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1181 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1184 gtk_widget_grab_focus(compose->subject_entry);
1186 case SUBJECT_FIELD_PRESENT:
1187 textview = GTK_TEXT_VIEW(compose->text);
1190 textbuf = gtk_text_view_get_buffer(textview);
1193 mark = gtk_text_buffer_get_insert(textbuf);
1194 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1195 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1197 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1198 * only defers where it comes to the variable body
1199 * is not null. If no body is present compose->text
1200 * will be null in which case you cannot place the
1201 * cursor inside the component so. An empty component
1202 * is therefore created before placing the cursor
1204 case BODY_FIELD_PRESENT:
1205 cursor_pos = quote_fmt_get_cursor_pos();
1206 if (cursor_pos == -1)
1207 gtk_widget_grab_focus(compose->header_last->entry);
1209 gtk_widget_grab_focus(compose->text);
1213 undo_unblock(compose->undostruct);
1215 if (prefs_common.auto_exteditor)
1216 compose_exec_ext_editor(compose);
1218 compose->draft_timeout_tag = -1;
1219 SCROLL_TO_CURSOR(compose);
1221 compose->modified = FALSE;
1222 compose_set_title(compose);
1224 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1229 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1230 gboolean override_pref, const gchar *system)
1232 const gchar *privacy = NULL;
1234 cm_return_if_fail(compose != NULL);
1235 cm_return_if_fail(account != NULL);
1237 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1242 else if (account->default_privacy_system
1243 && strlen(account->default_privacy_system)) {
1244 privacy = account->default_privacy_system;
1246 GSList *privacy_avail = privacy_get_system_ids();
1247 if (privacy_avail && g_slist_length(privacy_avail)) {
1248 privacy = (gchar *)(privacy_avail->data);
1251 if (privacy != NULL) {
1253 g_free(compose->privacy_system);
1254 compose->privacy_system = NULL;
1256 if (compose->privacy_system == NULL)
1257 compose->privacy_system = g_strdup(privacy);
1258 else if (*(compose->privacy_system) == '\0') {
1259 g_free(compose->privacy_system);
1260 compose->privacy_system = g_strdup(privacy);
1262 compose_update_privacy_system_menu_item(compose, FALSE);
1263 compose_use_encryption(compose, TRUE);
1267 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1269 const gchar *privacy = NULL;
1273 else if (account->default_privacy_system
1274 && strlen(account->default_privacy_system)) {
1275 privacy = account->default_privacy_system;
1277 GSList *privacy_avail = privacy_get_system_ids();
1278 if (privacy_avail && g_slist_length(privacy_avail)) {
1279 privacy = (gchar *)(privacy_avail->data);
1283 if (privacy != NULL) {
1285 g_free(compose->privacy_system);
1286 compose->privacy_system = NULL;
1288 if (compose->privacy_system == NULL)
1289 compose->privacy_system = g_strdup(privacy);
1290 compose_update_privacy_system_menu_item(compose, FALSE);
1291 compose_use_signing(compose, TRUE);
1295 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1299 Compose *compose = NULL;
1301 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1303 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1304 cm_return_val_if_fail(msginfo != NULL, NULL);
1306 list_len = g_slist_length(msginfo_list);
1310 case COMPOSE_REPLY_TO_ADDRESS:
1311 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1312 FALSE, prefs_common.default_reply_list, FALSE, body);
1314 case COMPOSE_REPLY_WITH_QUOTE:
1315 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1316 FALSE, prefs_common.default_reply_list, FALSE, body);
1318 case COMPOSE_REPLY_WITHOUT_QUOTE:
1319 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1320 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1322 case COMPOSE_REPLY_TO_SENDER:
1323 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1324 FALSE, FALSE, TRUE, body);
1326 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1327 compose = compose_followup_and_reply_to(msginfo,
1328 COMPOSE_QUOTE_CHECK,
1329 FALSE, FALSE, body);
1331 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1332 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1333 FALSE, FALSE, TRUE, body);
1335 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1336 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1337 FALSE, FALSE, TRUE, NULL);
1339 case COMPOSE_REPLY_TO_ALL:
1340 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1341 TRUE, FALSE, FALSE, body);
1343 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1344 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1345 TRUE, FALSE, FALSE, body);
1347 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1348 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1349 TRUE, FALSE, FALSE, NULL);
1351 case COMPOSE_REPLY_TO_LIST:
1352 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1353 FALSE, TRUE, FALSE, body);
1355 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1356 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1357 FALSE, TRUE, FALSE, body);
1359 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1360 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1361 FALSE, TRUE, FALSE, NULL);
1363 case COMPOSE_FORWARD:
1364 if (prefs_common.forward_as_attachment) {
1365 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1368 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1372 case COMPOSE_FORWARD_INLINE:
1373 /* check if we reply to more than one Message */
1374 if (list_len == 1) {
1375 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1378 /* more messages FALL THROUGH */
1379 case COMPOSE_FORWARD_AS_ATTACH:
1380 compose = compose_forward_multiple(NULL, msginfo_list);
1382 case COMPOSE_REDIRECT:
1383 compose = compose_redirect(NULL, msginfo, FALSE);
1386 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1389 if (compose == NULL) {
1390 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1394 compose->rmode = mode;
1395 switch (compose->rmode) {
1397 case COMPOSE_REPLY_WITH_QUOTE:
1398 case COMPOSE_REPLY_WITHOUT_QUOTE:
1399 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1400 debug_print("reply mode Normal\n");
1401 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1402 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1404 case COMPOSE_REPLY_TO_SENDER:
1405 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1406 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1407 debug_print("reply mode Sender\n");
1408 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1410 case COMPOSE_REPLY_TO_ALL:
1411 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1412 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1413 debug_print("reply mode All\n");
1414 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1416 case COMPOSE_REPLY_TO_LIST:
1417 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1418 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1419 debug_print("reply mode List\n");
1420 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1422 case COMPOSE_REPLY_TO_ADDRESS:
1423 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1431 static Compose *compose_reply(MsgInfo *msginfo,
1432 ComposeQuoteMode quote_mode,
1438 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1439 to_sender, FALSE, body);
1442 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1443 ComposeQuoteMode quote_mode,
1448 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1449 to_sender, TRUE, body);
1452 static void compose_extract_original_charset(Compose *compose)
1454 MsgInfo *info = NULL;
1455 if (compose->replyinfo) {
1456 info = compose->replyinfo;
1457 } else if (compose->fwdinfo) {
1458 info = compose->fwdinfo;
1459 } else if (compose->targetinfo) {
1460 info = compose->targetinfo;
1463 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1464 MimeInfo *partinfo = mimeinfo;
1465 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1466 partinfo = procmime_mimeinfo_next(partinfo);
1468 compose->orig_charset =
1469 g_strdup(procmime_mimeinfo_get_parameter(
1470 partinfo, "charset"));
1472 procmime_mimeinfo_free_all(mimeinfo);
1476 #define SIGNAL_BLOCK(buffer) { \
1477 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1478 G_CALLBACK(compose_changed_cb), \
1480 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1481 G_CALLBACK(text_inserted), \
1485 #define SIGNAL_UNBLOCK(buffer) { \
1486 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1487 G_CALLBACK(compose_changed_cb), \
1489 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1490 G_CALLBACK(text_inserted), \
1494 static Compose *compose_generic_reply(MsgInfo *msginfo,
1495 ComposeQuoteMode quote_mode,
1496 gboolean to_all, gboolean to_ml,
1498 gboolean followup_and_reply_to,
1502 PrefsAccount *account = NULL;
1503 GtkTextView *textview;
1504 GtkTextBuffer *textbuf;
1505 gboolean quote = FALSE;
1506 const gchar *qmark = NULL;
1507 const gchar *body_fmt = NULL;
1508 gchar *s_system = NULL;
1510 cm_return_val_if_fail(msginfo != NULL, NULL);
1511 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1513 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1515 cm_return_val_if_fail(account != NULL, NULL);
1517 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1519 compose->updating = TRUE;
1521 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1522 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1524 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1525 if (!compose->replyinfo)
1526 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1528 compose_extract_original_charset(compose);
1530 if (msginfo->folder && msginfo->folder->ret_rcpt)
1531 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1533 /* Set save folder */
1534 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1535 gchar *folderidentifier;
1537 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1538 folderidentifier = folder_item_get_identifier(msginfo->folder);
1539 compose_set_save_to(compose, folderidentifier);
1540 g_free(folderidentifier);
1543 if (compose_parse_header(compose, msginfo) < 0) {
1544 compose->updating = FALSE;
1545 compose_destroy(compose);
1549 /* override from name according to folder properties */
1550 if (msginfo->folder && msginfo->folder->prefs &&
1551 msginfo->folder->prefs->reply_with_format &&
1552 msginfo->folder->prefs->reply_override_from_format &&
1553 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1558 /* decode \-escape sequences in the internal representation of the quote format */
1559 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1560 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1563 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1564 compose->gtkaspell);
1566 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1568 quote_fmt_scan_string(tmp);
1571 buf = quote_fmt_get_buffer();
1573 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1575 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1576 quote_fmt_reset_vartable();
1581 textview = (GTK_TEXT_VIEW(compose->text));
1582 textbuf = gtk_text_view_get_buffer(textview);
1583 compose_create_tags(textview, compose);
1585 undo_block(compose->undostruct);
1587 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1590 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1591 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1592 /* use the reply format of folder (if enabled), or the account's one
1593 (if enabled) or fallback to the global reply format, which is always
1594 enabled (even if empty), and use the relevant quotemark */
1596 if (msginfo->folder && msginfo->folder->prefs &&
1597 msginfo->folder->prefs->reply_with_format) {
1598 qmark = msginfo->folder->prefs->reply_quotemark;
1599 body_fmt = msginfo->folder->prefs->reply_body_format;
1601 } else if (account->reply_with_format) {
1602 qmark = account->reply_quotemark;
1603 body_fmt = account->reply_body_format;
1606 qmark = prefs_common.quotemark;
1607 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1608 body_fmt = gettext(prefs_common.quotefmt);
1615 /* empty quotemark is not allowed */
1616 if (qmark == NULL || *qmark == '\0')
1618 compose_quote_fmt(compose, compose->replyinfo,
1619 body_fmt, qmark, body, FALSE, TRUE,
1620 _("The body of the \"Reply\" template has an error at line %d."));
1621 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1622 quote_fmt_reset_vartable();
1624 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1625 gtkaspell_highlight_all(compose->gtkaspell);
1629 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1630 compose_force_encryption(compose, account, FALSE, s_system);
1633 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1634 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1635 compose_force_signing(compose, account, s_system);
1639 SIGNAL_BLOCK(textbuf);
1641 if (account->auto_sig)
1642 compose_insert_sig(compose, FALSE);
1644 compose_wrap_all(compose);
1646 SIGNAL_UNBLOCK(textbuf);
1648 gtk_widget_grab_focus(compose->text);
1650 undo_unblock(compose->undostruct);
1652 if (prefs_common.auto_exteditor)
1653 compose_exec_ext_editor(compose);
1655 compose->modified = FALSE;
1656 compose_set_title(compose);
1658 compose->updating = FALSE;
1659 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1660 SCROLL_TO_CURSOR(compose);
1662 if (compose->deferred_destroy) {
1663 compose_destroy(compose);
1671 #define INSERT_FW_HEADER(var, hdr) \
1672 if (msginfo->var && *msginfo->var) { \
1673 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1674 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1675 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1678 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1679 gboolean as_attach, const gchar *body,
1680 gboolean no_extedit,
1684 GtkTextView *textview;
1685 GtkTextBuffer *textbuf;
1686 gint cursor_pos = -1;
1689 cm_return_val_if_fail(msginfo != NULL, NULL);
1690 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1693 !(account = compose_guess_forward_account_from_msginfo
1695 account = cur_account;
1697 if (!prefs_common.forward_as_attachment)
1698 mode = COMPOSE_FORWARD_INLINE;
1700 mode = COMPOSE_FORWARD;
1701 compose = compose_create(account, msginfo->folder, mode, batch);
1703 compose->updating = TRUE;
1704 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1705 if (!compose->fwdinfo)
1706 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1708 compose_extract_original_charset(compose);
1710 if (msginfo->subject && *msginfo->subject) {
1711 gchar *buf, *buf2, *p;
1713 buf = p = g_strdup(msginfo->subject);
1714 p += subject_get_prefix_length(p);
1715 memmove(buf, p, strlen(p) + 1);
1717 buf2 = g_strdup_printf("Fw: %s", buf);
1718 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1724 /* override from name according to folder properties */
1725 if (msginfo->folder && msginfo->folder->prefs &&
1726 msginfo->folder->prefs->forward_with_format &&
1727 msginfo->folder->prefs->forward_override_from_format &&
1728 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1732 MsgInfo *full_msginfo = NULL;
1735 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1737 full_msginfo = procmsg_msginfo_copy(msginfo);
1739 /* decode \-escape sequences in the internal representation of the quote format */
1740 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1741 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1744 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1745 compose->gtkaspell);
1747 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1749 quote_fmt_scan_string(tmp);
1752 buf = quote_fmt_get_buffer();
1754 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1756 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1757 quote_fmt_reset_vartable();
1760 procmsg_msginfo_free(full_msginfo);
1763 textview = GTK_TEXT_VIEW(compose->text);
1764 textbuf = gtk_text_view_get_buffer(textview);
1765 compose_create_tags(textview, compose);
1767 undo_block(compose->undostruct);
1771 msgfile = procmsg_get_message_file(msginfo);
1772 if (!is_file_exist(msgfile))
1773 g_warning("%s: file not exist\n", msgfile);
1775 compose_attach_append(compose, msgfile, msgfile,
1776 "message/rfc822", NULL);
1780 const gchar *qmark = NULL;
1781 const gchar *body_fmt = NULL;
1782 MsgInfo *full_msginfo;
1784 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1785 body_fmt = gettext(prefs_common.fw_quotefmt);
1789 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1791 full_msginfo = procmsg_msginfo_copy(msginfo);
1793 /* use the forward format of folder (if enabled), or the account's one
1794 (if enabled) or fallback to the global forward format, which is always
1795 enabled (even if empty), and use the relevant quotemark */
1796 if (msginfo->folder && msginfo->folder->prefs &&
1797 msginfo->folder->prefs->forward_with_format) {
1798 qmark = msginfo->folder->prefs->forward_quotemark;
1799 body_fmt = msginfo->folder->prefs->forward_body_format;
1801 } else if (account->forward_with_format) {
1802 qmark = account->forward_quotemark;
1803 body_fmt = account->forward_body_format;
1806 qmark = prefs_common.fw_quotemark;
1807 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1808 body_fmt = gettext(prefs_common.fw_quotefmt);
1813 /* empty quotemark is not allowed */
1814 if (qmark == NULL || *qmark == '\0')
1817 compose_quote_fmt(compose, full_msginfo,
1818 body_fmt, qmark, body, FALSE, TRUE,
1819 _("The body of the \"Forward\" template has an error at line %d."));
1820 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1821 quote_fmt_reset_vartable();
1822 compose_attach_parts(compose, msginfo);
1824 procmsg_msginfo_free(full_msginfo);
1826 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1827 gtkaspell_highlight_all(compose->gtkaspell);
1831 SIGNAL_BLOCK(textbuf);
1833 if (account->auto_sig)
1834 compose_insert_sig(compose, FALSE);
1836 compose_wrap_all(compose);
1838 SIGNAL_UNBLOCK(textbuf);
1840 cursor_pos = quote_fmt_get_cursor_pos();
1841 if (cursor_pos == -1)
1842 gtk_widget_grab_focus(compose->header_last->entry);
1844 gtk_widget_grab_focus(compose->text);
1846 if (!no_extedit && prefs_common.auto_exteditor)
1847 compose_exec_ext_editor(compose);
1850 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1851 gchar *folderidentifier;
1853 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1854 folderidentifier = folder_item_get_identifier(msginfo->folder);
1855 compose_set_save_to(compose, folderidentifier);
1856 g_free(folderidentifier);
1859 undo_unblock(compose->undostruct);
1861 compose->modified = FALSE;
1862 compose_set_title(compose);
1864 compose->updating = FALSE;
1865 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1866 SCROLL_TO_CURSOR(compose);
1868 if (compose->deferred_destroy) {
1869 compose_destroy(compose);
1873 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1878 #undef INSERT_FW_HEADER
1880 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1883 GtkTextView *textview;
1884 GtkTextBuffer *textbuf;
1888 gboolean single_mail = TRUE;
1890 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1892 if (g_slist_length(msginfo_list) > 1)
1893 single_mail = FALSE;
1895 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1896 if (((MsgInfo *)msginfo->data)->folder == NULL)
1899 /* guess account from first selected message */
1901 !(account = compose_guess_forward_account_from_msginfo
1902 (msginfo_list->data)))
1903 account = cur_account;
1905 cm_return_val_if_fail(account != NULL, NULL);
1907 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1908 if (msginfo->data) {
1909 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1910 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1914 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1915 g_warning("no msginfo_list");
1919 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1921 compose->updating = TRUE;
1923 /* override from name according to folder properties */
1924 if (msginfo_list->data) {
1925 MsgInfo *msginfo = msginfo_list->data;
1927 if (msginfo->folder && msginfo->folder->prefs &&
1928 msginfo->folder->prefs->forward_with_format &&
1929 msginfo->folder->prefs->forward_override_from_format &&
1930 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1935 /* decode \-escape sequences in the internal representation of the quote format */
1936 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1937 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1940 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1941 compose->gtkaspell);
1943 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1945 quote_fmt_scan_string(tmp);
1948 buf = quote_fmt_get_buffer();
1950 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1952 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1953 quote_fmt_reset_vartable();
1959 textview = GTK_TEXT_VIEW(compose->text);
1960 textbuf = gtk_text_view_get_buffer(textview);
1961 compose_create_tags(textview, compose);
1963 undo_block(compose->undostruct);
1964 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1965 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1967 if (!is_file_exist(msgfile))
1968 g_warning("%s: file not exist\n", msgfile);
1970 compose_attach_append(compose, msgfile, msgfile,
1971 "message/rfc822", NULL);
1976 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1977 if (info->subject && *info->subject) {
1978 gchar *buf, *buf2, *p;
1980 buf = p = g_strdup(info->subject);
1981 p += subject_get_prefix_length(p);
1982 memmove(buf, p, strlen(p) + 1);
1984 buf2 = g_strdup_printf("Fw: %s", buf);
1985 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1991 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1992 _("Fw: multiple emails"));
1995 SIGNAL_BLOCK(textbuf);
1997 if (account->auto_sig)
1998 compose_insert_sig(compose, FALSE);
2000 compose_wrap_all(compose);
2002 SIGNAL_UNBLOCK(textbuf);
2004 gtk_text_buffer_get_start_iter(textbuf, &iter);
2005 gtk_text_buffer_place_cursor(textbuf, &iter);
2007 gtk_widget_grab_focus(compose->header_last->entry);
2008 undo_unblock(compose->undostruct);
2009 compose->modified = FALSE;
2010 compose_set_title(compose);
2012 compose->updating = FALSE;
2013 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2014 SCROLL_TO_CURSOR(compose);
2016 if (compose->deferred_destroy) {
2017 compose_destroy(compose);
2021 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2026 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2028 GtkTextIter start = *iter;
2029 GtkTextIter end_iter;
2030 int start_pos = gtk_text_iter_get_offset(&start);
2032 if (!compose->account->sig_sep)
2035 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2036 start_pos+strlen(compose->account->sig_sep));
2038 /* check sig separator */
2039 str = gtk_text_iter_get_text(&start, &end_iter);
2040 if (!strcmp(str, compose->account->sig_sep)) {
2042 /* check end of line (\n) */
2043 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2044 start_pos+strlen(compose->account->sig_sep));
2045 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2046 start_pos+strlen(compose->account->sig_sep)+1);
2047 tmp = gtk_text_iter_get_text(&start, &end_iter);
2048 if (!strcmp(tmp,"\n")) {
2060 static void compose_colorize_signature(Compose *compose)
2062 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2064 GtkTextIter end_iter;
2065 gtk_text_buffer_get_start_iter(buffer, &iter);
2066 while (gtk_text_iter_forward_line(&iter))
2067 if (compose_is_sig_separator(compose, buffer, &iter)) {
2068 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2069 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2073 #define BLOCK_WRAP() { \
2074 prev_autowrap = compose->autowrap; \
2075 buffer = gtk_text_view_get_buffer( \
2076 GTK_TEXT_VIEW(compose->text)); \
2077 compose->autowrap = FALSE; \
2079 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2080 G_CALLBACK(compose_changed_cb), \
2082 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2083 G_CALLBACK(text_inserted), \
2086 #define UNBLOCK_WRAP() { \
2087 compose->autowrap = prev_autowrap; \
2088 if (compose->autowrap) { \
2089 gint old = compose->draft_timeout_tag; \
2090 compose->draft_timeout_tag = -2; \
2091 compose_wrap_all(compose); \
2092 compose->draft_timeout_tag = old; \
2095 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2096 G_CALLBACK(compose_changed_cb), \
2098 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2099 G_CALLBACK(text_inserted), \
2103 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2105 Compose *compose = NULL;
2106 PrefsAccount *account = NULL;
2107 GtkTextView *textview;
2108 GtkTextBuffer *textbuf;
2112 gchar buf[BUFFSIZE];
2113 gboolean use_signing = FALSE;
2114 gboolean use_encryption = FALSE;
2115 gchar *privacy_system = NULL;
2116 int priority = PRIORITY_NORMAL;
2117 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2118 gboolean autowrap = prefs_common.autowrap;
2119 gboolean autoindent = prefs_common.auto_indent;
2121 cm_return_val_if_fail(msginfo != NULL, NULL);
2122 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2124 if (compose_put_existing_to_front(msginfo)) {
2128 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2129 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2130 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2131 gchar queueheader_buf[BUFFSIZE];
2134 /* Select Account from queue headers */
2135 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2136 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2137 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2138 account = account_find_from_id(id);
2140 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2141 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2142 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2143 account = account_find_from_id(id);
2145 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2146 sizeof(queueheader_buf), "NAID:")) {
2147 id = atoi(&queueheader_buf[strlen("NAID:")]);
2148 account = account_find_from_id(id);
2150 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2151 sizeof(queueheader_buf), "MAID:")) {
2152 id = atoi(&queueheader_buf[strlen("MAID:")]);
2153 account = account_find_from_id(id);
2155 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2156 sizeof(queueheader_buf), "S:")) {
2157 account = account_find_from_address(queueheader_buf, FALSE);
2159 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2160 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2161 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2162 use_signing = param;
2165 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2166 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2167 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2168 use_signing = param;
2171 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2172 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2173 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2174 use_encryption = param;
2176 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2177 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2178 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2179 use_encryption = param;
2181 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2182 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2183 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2186 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2187 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2188 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2191 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2192 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2193 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2195 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2196 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2197 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2199 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2200 sizeof(queueheader_buf), "X-Priority: ")) {
2201 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2204 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2205 sizeof(queueheader_buf), "RMID:")) {
2206 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2207 if (tokens[0] && tokens[1] && tokens[2]) {
2208 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2209 if (orig_item != NULL) {
2210 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2215 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2216 sizeof(queueheader_buf), "FMID:")) {
2217 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2218 if (tokens[0] && tokens[1] && tokens[2]) {
2219 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2220 if (orig_item != NULL) {
2221 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2227 account = msginfo->folder->folder->account;
2230 if (!account && prefs_common.reedit_account_autosel) {
2231 gchar from[BUFFSIZE];
2232 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2233 extract_address(from);
2234 account = account_find_from_address(from, FALSE);
2238 account = cur_account;
2240 cm_return_val_if_fail(account != NULL, NULL);
2242 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2244 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2245 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2246 compose->autowrap = autowrap;
2247 compose->replyinfo = replyinfo;
2248 compose->fwdinfo = fwdinfo;
2250 compose->updating = TRUE;
2251 compose->priority = priority;
2253 if (privacy_system != NULL) {
2254 compose->privacy_system = privacy_system;
2255 compose_use_signing(compose, use_signing);
2256 compose_use_encryption(compose, use_encryption);
2257 compose_update_privacy_system_menu_item(compose, FALSE);
2259 activate_privacy_system(compose, account, FALSE);
2262 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2264 compose_extract_original_charset(compose);
2266 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2267 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2268 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2269 gchar queueheader_buf[BUFFSIZE];
2271 /* Set message save folder */
2272 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2273 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2274 compose_set_save_to(compose, &queueheader_buf[4]);
2276 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2277 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2279 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2284 if (compose_parse_header(compose, msginfo) < 0) {
2285 compose->updating = FALSE;
2286 compose_destroy(compose);
2289 compose_reedit_set_entry(compose, msginfo);
2291 textview = GTK_TEXT_VIEW(compose->text);
2292 textbuf = gtk_text_view_get_buffer(textview);
2293 compose_create_tags(textview, compose);
2295 mark = gtk_text_buffer_get_insert(textbuf);
2296 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2298 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2299 G_CALLBACK(compose_changed_cb),
2302 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2303 fp = procmime_get_first_encrypted_text_content(msginfo);
2305 compose_force_encryption(compose, account, TRUE, NULL);
2308 fp = procmime_get_first_text_content(msginfo);
2311 g_warning("Can't get text part\n");
2315 gboolean prev_autowrap = compose->autowrap;
2316 GtkTextBuffer *buffer = textbuf;
2318 while (fgets(buf, sizeof(buf), fp) != NULL) {
2320 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2326 compose_attach_parts(compose, msginfo);
2328 compose_colorize_signature(compose);
2330 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2331 G_CALLBACK(compose_changed_cb),
2334 gtk_widget_grab_focus(compose->text);
2336 if (prefs_common.auto_exteditor) {
2337 compose_exec_ext_editor(compose);
2339 compose->modified = FALSE;
2340 compose_set_title(compose);
2342 compose->updating = FALSE;
2343 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2344 SCROLL_TO_CURSOR(compose);
2346 if (compose->deferred_destroy) {
2347 compose_destroy(compose);
2351 compose->sig_str = account_get_signature_str(compose->account);
2353 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2358 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2365 cm_return_val_if_fail(msginfo != NULL, NULL);
2368 account = account_get_reply_account(msginfo,
2369 prefs_common.reply_account_autosel);
2370 cm_return_val_if_fail(account != NULL, NULL);
2372 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2374 compose->updating = TRUE;
2376 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2377 compose->replyinfo = NULL;
2378 compose->fwdinfo = NULL;
2380 compose_show_first_last_header(compose, TRUE);
2382 gtk_widget_grab_focus(compose->header_last->entry);
2384 filename = procmsg_get_message_file(msginfo);
2386 if (filename == NULL) {
2387 compose->updating = FALSE;
2388 compose_destroy(compose);
2393 compose->redirect_filename = filename;
2395 /* Set save folder */
2396 item = msginfo->folder;
2397 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2398 gchar *folderidentifier;
2400 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2401 folderidentifier = folder_item_get_identifier(item);
2402 compose_set_save_to(compose, folderidentifier);
2403 g_free(folderidentifier);
2406 compose_attach_parts(compose, msginfo);
2408 if (msginfo->subject)
2409 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2411 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2413 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2414 _("The body of the \"Redirect\" template has an error at line %d."));
2415 quote_fmt_reset_vartable();
2416 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2418 compose_colorize_signature(compose);
2421 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2422 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2423 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2425 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2426 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2427 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2428 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2429 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2430 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2431 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2432 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2434 if (compose->toolbar->draft_btn)
2435 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2436 if (compose->toolbar->insert_btn)
2437 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2438 if (compose->toolbar->attach_btn)
2439 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2440 if (compose->toolbar->sig_btn)
2441 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2442 if (compose->toolbar->exteditor_btn)
2443 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2444 if (compose->toolbar->linewrap_current_btn)
2445 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2446 if (compose->toolbar->linewrap_all_btn)
2447 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2449 compose->modified = FALSE;
2450 compose_set_title(compose);
2451 compose->updating = FALSE;
2452 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2453 SCROLL_TO_CURSOR(compose);
2455 if (compose->deferred_destroy) {
2456 compose_destroy(compose);
2460 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2465 GList *compose_get_compose_list(void)
2467 return compose_list;
2470 void compose_entry_append(Compose *compose, const gchar *address,
2471 ComposeEntryType type, ComposePrefType pref_type)
2473 const gchar *header;
2475 gboolean in_quote = FALSE;
2476 if (!address || *address == '\0') return;
2483 header = N_("Bcc:");
2485 case COMPOSE_REPLYTO:
2486 header = N_("Reply-To:");
2488 case COMPOSE_NEWSGROUPS:
2489 header = N_("Newsgroups:");
2491 case COMPOSE_FOLLOWUPTO:
2492 header = N_( "Followup-To:");
2494 case COMPOSE_INREPLYTO:
2495 header = N_( "In-Reply-To:");
2502 header = prefs_common_translated_header_name(header);
2504 cur = begin = (gchar *)address;
2506 /* we separate the line by commas, but not if we're inside a quoted
2508 while (*cur != '\0') {
2510 in_quote = !in_quote;
2511 if (*cur == ',' && !in_quote) {
2512 gchar *tmp = g_strdup(begin);
2514 tmp[cur-begin]='\0';
2517 while (*tmp == ' ' || *tmp == '\t')
2519 compose_add_header_entry(compose, header, tmp, pref_type);
2526 gchar *tmp = g_strdup(begin);
2528 tmp[cur-begin]='\0';
2531 while (*tmp == ' ' || *tmp == '\t')
2533 compose_add_header_entry(compose, header, tmp, pref_type);
2538 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2540 #if !GTK_CHECK_VERSION(3, 0, 0)
2541 static GdkColor yellow;
2542 static GdkColor black;
2543 static gboolean yellow_initialised = FALSE;
2545 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2546 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2551 #if !GTK_CHECK_VERSION(3, 0, 0)
2552 if (!yellow_initialised) {
2553 gdk_color_parse("#f5f6be", &yellow);
2554 gdk_color_parse("#000000", &black);
2555 yellow_initialised = gdk_colormap_alloc_color(
2556 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2557 yellow_initialised &= gdk_colormap_alloc_color(
2558 gdk_colormap_get_system(), &black, FALSE, TRUE);
2562 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2563 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2564 if (gtk_entry_get_text(entry) &&
2565 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2566 #if !GTK_CHECK_VERSION(3, 0, 0)
2567 if (yellow_initialised) {
2569 gtk_widget_modify_base(
2570 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2571 GTK_STATE_NORMAL, &yellow);
2572 gtk_widget_modify_text(
2573 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2574 GTK_STATE_NORMAL, &black);
2575 #if !GTK_CHECK_VERSION(3, 0, 0)
2582 void compose_toolbar_cb(gint action, gpointer data)
2584 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2585 Compose *compose = (Compose*)toolbar_item->parent;
2587 cm_return_if_fail(compose != NULL);
2591 compose_send_cb(NULL, compose);
2594 compose_send_later_cb(NULL, compose);
2597 compose_draft(compose, COMPOSE_QUIT_EDITING);
2600 compose_insert_file_cb(NULL, compose);
2603 compose_attach_cb(NULL, compose);
2606 compose_insert_sig(compose, FALSE);
2609 compose_ext_editor_cb(NULL, compose);
2611 case A_LINEWRAP_CURRENT:
2612 compose_beautify_paragraph(compose, NULL, TRUE);
2614 case A_LINEWRAP_ALL:
2615 compose_wrap_all_full(compose, TRUE);
2618 compose_address_cb(NULL, compose);
2621 case A_CHECK_SPELLING:
2622 compose_check_all(NULL, compose);
2630 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2635 gchar *subject = NULL;
2639 gchar **attach = NULL;
2640 gchar *inreplyto = NULL;
2641 MailField mfield = NO_FIELD_PRESENT;
2643 /* get mailto parts but skip from */
2644 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2647 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2648 mfield = TO_FIELD_PRESENT;
2651 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2653 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2655 if (!g_utf8_validate (subject, -1, NULL)) {
2656 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2657 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2660 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2662 mfield = SUBJECT_FIELD_PRESENT;
2665 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2666 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2669 gboolean prev_autowrap = compose->autowrap;
2671 compose->autowrap = FALSE;
2673 mark = gtk_text_buffer_get_insert(buffer);
2674 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2676 if (!g_utf8_validate (body, -1, NULL)) {
2677 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2678 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2681 gtk_text_buffer_insert(buffer, &iter, body, -1);
2683 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2685 compose->autowrap = prev_autowrap;
2686 if (compose->autowrap)
2687 compose_wrap_all(compose);
2688 mfield = BODY_FIELD_PRESENT;
2692 gint i = 0, att = 0;
2693 gchar *warn_files = NULL;
2694 while (attach[i] != NULL) {
2695 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2696 if (utf8_filename) {
2697 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2698 gchar *tmp = g_strdup_printf("%s%s\n",
2699 warn_files?warn_files:"",
2705 g_free(utf8_filename);
2707 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2712 alertpanel_notice(ngettext(
2713 "The following file has been attached: \n%s",
2714 "The following files have been attached: \n%s", att), warn_files);
2719 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2732 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2734 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2735 {"Cc:", NULL, TRUE},
2736 {"References:", NULL, FALSE},
2737 {"Bcc:", NULL, TRUE},
2738 {"Newsgroups:", NULL, TRUE},
2739 {"Followup-To:", NULL, TRUE},
2740 {"List-Post:", NULL, FALSE},
2741 {"X-Priority:", NULL, FALSE},
2742 {NULL, NULL, FALSE}};
2758 cm_return_val_if_fail(msginfo != NULL, -1);
2760 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2761 procheader_get_header_fields(fp, hentry);
2764 if (hentry[H_REPLY_TO].body != NULL) {
2765 if (hentry[H_REPLY_TO].body[0] != '\0') {
2767 conv_unmime_header(hentry[H_REPLY_TO].body,
2770 g_free(hentry[H_REPLY_TO].body);
2771 hentry[H_REPLY_TO].body = NULL;
2773 if (hentry[H_CC].body != NULL) {
2774 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2775 g_free(hentry[H_CC].body);
2776 hentry[H_CC].body = NULL;
2778 if (hentry[H_REFERENCES].body != NULL) {
2779 if (compose->mode == COMPOSE_REEDIT)
2780 compose->references = hentry[H_REFERENCES].body;
2782 compose->references = compose_parse_references
2783 (hentry[H_REFERENCES].body, msginfo->msgid);
2784 g_free(hentry[H_REFERENCES].body);
2786 hentry[H_REFERENCES].body = NULL;
2788 if (hentry[H_BCC].body != NULL) {
2789 if (compose->mode == COMPOSE_REEDIT)
2791 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2792 g_free(hentry[H_BCC].body);
2793 hentry[H_BCC].body = NULL;
2795 if (hentry[H_NEWSGROUPS].body != NULL) {
2796 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2797 hentry[H_NEWSGROUPS].body = NULL;
2799 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2800 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2801 compose->followup_to =
2802 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2805 g_free(hentry[H_FOLLOWUP_TO].body);
2806 hentry[H_FOLLOWUP_TO].body = NULL;
2808 if (hentry[H_LIST_POST].body != NULL) {
2809 gchar *to = NULL, *start = NULL;
2811 extract_address(hentry[H_LIST_POST].body);
2812 if (hentry[H_LIST_POST].body[0] != '\0') {
2813 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2815 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2816 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2819 g_free(compose->ml_post);
2820 compose->ml_post = to;
2823 g_free(hentry[H_LIST_POST].body);
2824 hentry[H_LIST_POST].body = NULL;
2827 /* CLAWS - X-Priority */
2828 if (compose->mode == COMPOSE_REEDIT)
2829 if (hentry[H_X_PRIORITY].body != NULL) {
2832 priority = atoi(hentry[H_X_PRIORITY].body);
2833 g_free(hentry[H_X_PRIORITY].body);
2835 hentry[H_X_PRIORITY].body = NULL;
2837 if (priority < PRIORITY_HIGHEST ||
2838 priority > PRIORITY_LOWEST)
2839 priority = PRIORITY_NORMAL;
2841 compose->priority = priority;
2844 if (compose->mode == COMPOSE_REEDIT) {
2845 if (msginfo->inreplyto && *msginfo->inreplyto)
2846 compose->inreplyto = g_strdup(msginfo->inreplyto);
2850 if (msginfo->msgid && *msginfo->msgid)
2851 compose->inreplyto = g_strdup(msginfo->msgid);
2853 if (!compose->references) {
2854 if (msginfo->msgid && *msginfo->msgid) {
2855 if (msginfo->inreplyto && *msginfo->inreplyto)
2856 compose->references =
2857 g_strdup_printf("<%s>\n\t<%s>",
2861 compose->references =
2862 g_strconcat("<", msginfo->msgid, ">",
2864 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2865 compose->references =
2866 g_strconcat("<", msginfo->inreplyto, ">",
2874 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2876 GSList *ref_id_list, *cur;
2880 ref_id_list = references_list_append(NULL, ref);
2881 if (!ref_id_list) return NULL;
2882 if (msgid && *msgid)
2883 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2888 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2889 /* "<" + Message-ID + ">" + CR+LF+TAB */
2890 len += strlen((gchar *)cur->data) + 5;
2892 if (len > MAX_REFERENCES_LEN) {
2893 /* remove second message-ID */
2894 if (ref_id_list && ref_id_list->next &&
2895 ref_id_list->next->next) {
2896 g_free(ref_id_list->next->data);
2897 ref_id_list = g_slist_remove
2898 (ref_id_list, ref_id_list->next->data);
2900 slist_free_strings(ref_id_list);
2901 g_slist_free(ref_id_list);
2908 new_ref = g_string_new("");
2909 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2910 if (new_ref->len > 0)
2911 g_string_append(new_ref, "\n\t");
2912 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2915 slist_free_strings(ref_id_list);
2916 g_slist_free(ref_id_list);
2918 new_ref_str = new_ref->str;
2919 g_string_free(new_ref, FALSE);
2924 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2925 const gchar *fmt, const gchar *qmark,
2926 const gchar *body, gboolean rewrap,
2927 gboolean need_unescape,
2928 const gchar *err_msg)
2930 MsgInfo* dummyinfo = NULL;
2931 gchar *quote_str = NULL;
2933 gboolean prev_autowrap;
2934 const gchar *trimmed_body = body;
2935 gint cursor_pos = -1;
2936 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2937 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2942 SIGNAL_BLOCK(buffer);
2945 dummyinfo = compose_msginfo_new_from_compose(compose);
2946 msginfo = dummyinfo;
2949 if (qmark != NULL) {
2951 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2952 compose->gtkaspell);
2954 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2956 quote_fmt_scan_string(qmark);
2959 buf = quote_fmt_get_buffer();
2961 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2963 Xstrdup_a(quote_str, buf, goto error)
2966 if (fmt && *fmt != '\0') {
2969 while (*trimmed_body == '\n')
2973 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2974 compose->gtkaspell);
2976 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2978 if (need_unescape) {
2981 /* decode \-escape sequences in the internal representation of the quote format */
2982 tmp = g_malloc(strlen(fmt)+1);
2983 pref_get_unescaped_pref(tmp, fmt);
2984 quote_fmt_scan_string(tmp);
2988 quote_fmt_scan_string(fmt);
2992 buf = quote_fmt_get_buffer();
2994 gint line = quote_fmt_get_line();
2995 alertpanel_error(err_msg, line);
3001 prev_autowrap = compose->autowrap;
3002 compose->autowrap = FALSE;
3004 mark = gtk_text_buffer_get_insert(buffer);
3005 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3006 if (g_utf8_validate(buf, -1, NULL)) {
3007 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3009 gchar *tmpout = NULL;
3010 tmpout = conv_codeset_strdup
3011 (buf, conv_get_locale_charset_str_no_utf8(),
3013 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3015 tmpout = g_malloc(strlen(buf)*2+1);
3016 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3018 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3022 cursor_pos = quote_fmt_get_cursor_pos();
3023 if (cursor_pos == -1)
3024 cursor_pos = gtk_text_iter_get_offset(&iter);
3025 compose->set_cursor_pos = cursor_pos;
3027 gtk_text_buffer_get_start_iter(buffer, &iter);
3028 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3029 gtk_text_buffer_place_cursor(buffer, &iter);
3031 compose->autowrap = prev_autowrap;
3032 if (compose->autowrap && rewrap)
3033 compose_wrap_all(compose);
3040 SIGNAL_UNBLOCK(buffer);
3042 procmsg_msginfo_free( dummyinfo );
3047 /* if ml_post is of type addr@host and from is of type
3048 * addr-anything@host, return TRUE
3050 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3052 gchar *left_ml = NULL;
3053 gchar *right_ml = NULL;
3054 gchar *left_from = NULL;
3055 gchar *right_from = NULL;
3056 gboolean result = FALSE;
3058 if (!ml_post || !from)
3061 left_ml = g_strdup(ml_post);
3062 if (strstr(left_ml, "@")) {
3063 right_ml = strstr(left_ml, "@")+1;
3064 *(strstr(left_ml, "@")) = '\0';
3067 left_from = g_strdup(from);
3068 if (strstr(left_from, "@")) {
3069 right_from = strstr(left_from, "@")+1;
3070 *(strstr(left_from, "@")) = '\0';
3073 if (left_ml && left_from && right_ml && right_from
3074 && !strncmp(left_from, left_ml, strlen(left_ml))
3075 && !strcmp(right_from, right_ml)) {
3084 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3085 gboolean respect_default_to)
3089 if (!folder || !folder->prefs)
3092 if (respect_default_to && folder->prefs->enable_default_to) {
3093 compose_entry_append(compose, folder->prefs->default_to,
3094 COMPOSE_TO, PREF_FOLDER);
3095 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3097 if (folder->prefs->enable_default_cc)
3098 compose_entry_append(compose, folder->prefs->default_cc,
3099 COMPOSE_CC, PREF_FOLDER);
3100 if (folder->prefs->enable_default_bcc)
3101 compose_entry_append(compose, folder->prefs->default_bcc,
3102 COMPOSE_BCC, PREF_FOLDER);
3103 if (folder->prefs->enable_default_replyto)
3104 compose_entry_append(compose, folder->prefs->default_replyto,
3105 COMPOSE_REPLYTO, PREF_FOLDER);
3108 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3113 if (!compose || !msginfo)
3116 if (msginfo->subject && *msginfo->subject) {
3117 buf = p = g_strdup(msginfo->subject);
3118 p += subject_get_prefix_length(p);
3119 memmove(buf, p, strlen(p) + 1);
3121 buf2 = g_strdup_printf("Re: %s", buf);
3122 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3127 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3130 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3131 gboolean to_all, gboolean to_ml,
3133 gboolean followup_and_reply_to)
3135 GSList *cc_list = NULL;
3138 gchar *replyto = NULL;
3139 gchar *ac_email = NULL;
3141 gboolean reply_to_ml = FALSE;
3142 gboolean default_reply_to = FALSE;
3144 cm_return_if_fail(compose->account != NULL);
3145 cm_return_if_fail(msginfo != NULL);
3147 reply_to_ml = to_ml && compose->ml_post;
3149 default_reply_to = msginfo->folder &&
3150 msginfo->folder->prefs->enable_default_reply_to;
3152 if (compose->account->protocol != A_NNTP) {
3153 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3155 if (reply_to_ml && !default_reply_to) {
3157 gboolean is_subscr = is_subscription(compose->ml_post,
3160 /* normal answer to ml post with a reply-to */
3161 compose_entry_append(compose,
3163 COMPOSE_TO, PREF_ML);
3164 if (compose->replyto)
3165 compose_entry_append(compose,
3167 COMPOSE_CC, PREF_ML);
3169 /* answer to subscription confirmation */
3170 if (compose->replyto)
3171 compose_entry_append(compose,
3173 COMPOSE_TO, PREF_ML);
3174 else if (msginfo->from)
3175 compose_entry_append(compose,
3177 COMPOSE_TO, PREF_ML);
3180 else if (!(to_all || to_sender) && default_reply_to) {
3181 compose_entry_append(compose,
3182 msginfo->folder->prefs->default_reply_to,
3183 COMPOSE_TO, PREF_FOLDER);
3184 compose_entry_mark_default_to(compose,
3185 msginfo->folder->prefs->default_reply_to);
3190 Xstrdup_a(tmp1, msginfo->from, return);
3191 extract_address(tmp1);
3192 if (to_all || to_sender ||
3193 !account_find_from_address(tmp1, FALSE))
3194 compose_entry_append(compose,
3195 (compose->replyto && !to_sender)
3196 ? compose->replyto :
3197 msginfo->from ? msginfo->from : "",
3198 COMPOSE_TO, PREF_NONE);
3199 else if (!to_all && !to_sender) {
3200 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3201 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3202 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3203 if (compose->replyto) {
3204 compose_entry_append(compose,
3206 COMPOSE_TO, PREF_NONE);
3208 compose_entry_append(compose,
3209 msginfo->from ? msginfo->from : "",
3210 COMPOSE_TO, PREF_NONE);
3213 /* replying to own mail, use original recp */
3214 compose_entry_append(compose,
3215 msginfo->to ? msginfo->to : "",
3216 COMPOSE_TO, PREF_NONE);
3217 compose_entry_append(compose,
3218 msginfo->cc ? msginfo->cc : "",
3219 COMPOSE_CC, PREF_NONE);
3224 if (to_sender || (compose->followup_to &&
3225 !strncmp(compose->followup_to, "poster", 6)))
3226 compose_entry_append
3228 (compose->replyto ? compose->replyto :
3229 msginfo->from ? msginfo->from : ""),
3230 COMPOSE_TO, PREF_NONE);
3232 else if (followup_and_reply_to || to_all) {
3233 compose_entry_append
3235 (compose->replyto ? compose->replyto :
3236 msginfo->from ? msginfo->from : ""),
3237 COMPOSE_TO, PREF_NONE);
3239 compose_entry_append
3241 compose->followup_to ? compose->followup_to :
3242 compose->newsgroups ? compose->newsgroups : "",
3243 COMPOSE_NEWSGROUPS, PREF_NONE);
3246 compose_entry_append
3248 compose->followup_to ? compose->followup_to :
3249 compose->newsgroups ? compose->newsgroups : "",
3250 COMPOSE_NEWSGROUPS, PREF_NONE);
3252 compose_reply_set_subject(compose, msginfo);
3254 if (to_ml && compose->ml_post) return;
3255 if (!to_all || compose->account->protocol == A_NNTP) return;
3257 if (compose->replyto) {
3258 Xstrdup_a(replyto, compose->replyto, return);
3259 extract_address(replyto);
3261 if (msginfo->from) {
3262 Xstrdup_a(from, msginfo->from, return);
3263 extract_address(from);
3266 if (replyto && from)
3267 cc_list = address_list_append_with_comments(cc_list, from);
3268 if (to_all && msginfo->folder &&
3269 msginfo->folder->prefs->enable_default_reply_to)
3270 cc_list = address_list_append_with_comments(cc_list,
3271 msginfo->folder->prefs->default_reply_to);
3272 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3273 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3275 ac_email = g_utf8_strdown(compose->account->address, -1);
3278 for (cur = cc_list; cur != NULL; cur = cur->next) {
3279 gchar *addr = g_utf8_strdown(cur->data, -1);
3280 extract_address(addr);
3282 if (strcmp(ac_email, addr))
3283 compose_entry_append(compose, (gchar *)cur->data,
3284 COMPOSE_CC, PREF_NONE);
3286 debug_print("Cc address same as compose account's, ignoring\n");
3291 slist_free_strings(cc_list);
3292 g_slist_free(cc_list);
3298 #define SET_ENTRY(entry, str) \
3301 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3304 #define SET_ADDRESS(type, str) \
3307 compose_entry_append(compose, str, type, PREF_NONE); \
3310 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3312 cm_return_if_fail(msginfo != NULL);
3314 SET_ENTRY(subject_entry, msginfo->subject);
3315 SET_ENTRY(from_name, msginfo->from);
3316 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3317 SET_ADDRESS(COMPOSE_CC, compose->cc);
3318 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3319 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3320 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3321 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3323 compose_update_priority_menu_item(compose);
3324 compose_update_privacy_system_menu_item(compose, FALSE);
3325 compose_show_first_last_header(compose, TRUE);
3331 static void compose_insert_sig(Compose *compose, gboolean replace)
3333 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3334 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3336 GtkTextIter iter, iter_end;
3337 gint cur_pos, ins_pos;
3338 gboolean prev_autowrap;
3339 gboolean found = FALSE;
3340 gboolean exists = FALSE;
3342 cm_return_if_fail(compose->account != NULL);
3346 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3347 G_CALLBACK(compose_changed_cb),
3350 mark = gtk_text_buffer_get_insert(buffer);
3351 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3352 cur_pos = gtk_text_iter_get_offset (&iter);
3355 gtk_text_buffer_get_end_iter(buffer, &iter);
3357 exists = (compose->sig_str != NULL);
3360 GtkTextIter first_iter, start_iter, end_iter;
3362 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3364 if (!exists || compose->sig_str[0] == '\0')
3367 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3368 compose->signature_tag);
3371 /* include previous \n\n */
3372 gtk_text_iter_backward_chars(&first_iter, 1);
3373 start_iter = first_iter;
3374 end_iter = first_iter;
3376 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3377 compose->signature_tag);
3378 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3379 compose->signature_tag);
3381 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3387 g_free(compose->sig_str);
3388 compose->sig_str = account_get_signature_str(compose->account);
3390 cur_pos = gtk_text_iter_get_offset(&iter);
3392 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3393 g_free(compose->sig_str);
3394 compose->sig_str = NULL;
3396 if (compose->sig_inserted == FALSE)
3397 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3398 compose->sig_inserted = TRUE;
3400 cur_pos = gtk_text_iter_get_offset(&iter);
3401 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3403 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3404 gtk_text_iter_forward_chars(&iter, 1);
3405 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3406 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3408 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3409 cur_pos = gtk_text_buffer_get_char_count (buffer);
3412 /* put the cursor where it should be
3413 * either where the quote_fmt says, either where it was */
3414 if (compose->set_cursor_pos < 0)
3415 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3417 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3418 compose->set_cursor_pos);
3420 compose->set_cursor_pos = -1;
3421 gtk_text_buffer_place_cursor(buffer, &iter);
3422 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3423 G_CALLBACK(compose_changed_cb),
3429 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3432 GtkTextBuffer *buffer;
3435 const gchar *cur_encoding;
3436 gchar buf[BUFFSIZE];
3439 gboolean prev_autowrap;
3440 gboolean badtxt = FALSE;
3441 struct stat file_stat;
3444 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3446 /* get the size of the file we are about to insert */
3447 ret = g_stat(file, &file_stat);
3449 gchar *shortfile = g_path_get_basename(file);
3450 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3452 return COMPOSE_INSERT_NO_FILE;
3453 } else if (prefs_common.warn_large_insert == TRUE) {
3455 /* ask user for confirmation if the file is large */
3456 if (prefs_common.warn_large_insert_size < 0 ||
3457 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3461 msg = g_strdup_printf(_("You are about to insert a file of %s "
3462 "in the message body. Are you sure you want to do that?"),
3463 to_human_readable(file_stat.st_size));
3464 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3465 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3468 /* do we ask for confirmation next time? */
3469 if (aval & G_ALERTDISABLE) {
3470 /* no confirmation next time, disable feature in preferences */
3471 aval &= ~G_ALERTDISABLE;
3472 prefs_common.warn_large_insert = FALSE;
3475 /* abort file insertion if user canceled action */
3476 if (aval != G_ALERTALTERNATE) {
3477 return COMPOSE_INSERT_NO_FILE;
3483 if ((fp = g_fopen(file, "rb")) == NULL) {
3484 FILE_OP_ERROR(file, "fopen");
3485 return COMPOSE_INSERT_READ_ERROR;
3488 prev_autowrap = compose->autowrap;
3489 compose->autowrap = FALSE;
3491 text = GTK_TEXT_VIEW(compose->text);
3492 buffer = gtk_text_view_get_buffer(text);
3493 mark = gtk_text_buffer_get_insert(buffer);
3494 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3496 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3497 G_CALLBACK(text_inserted),
3500 cur_encoding = conv_get_locale_charset_str_no_utf8();
3502 while (fgets(buf, sizeof(buf), fp) != NULL) {
3505 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3506 str = g_strdup(buf);
3508 str = conv_codeset_strdup
3509 (buf, cur_encoding, CS_INTERNAL);
3512 /* strip <CR> if DOS/Windows file,
3513 replace <CR> with <LF> if Macintosh file. */
3516 if (len > 0 && str[len - 1] != '\n') {
3518 if (str[len] == '\r') str[len] = '\n';
3521 gtk_text_buffer_insert(buffer, &iter, str, -1);
3525 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3526 G_CALLBACK(text_inserted),
3528 compose->autowrap = prev_autowrap;
3529 if (compose->autowrap)
3530 compose_wrap_all(compose);
3535 return COMPOSE_INSERT_INVALID_CHARACTER;
3537 return COMPOSE_INSERT_SUCCESS;
3540 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3541 const gchar *filename,
3542 const gchar *content_type,
3543 const gchar *charset)
3551 GtkListStore *store;
3553 gboolean has_binary = FALSE;
3555 if (!is_file_exist(file)) {
3556 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3557 gboolean result = FALSE;
3558 if (file_from_uri && is_file_exist(file_from_uri)) {
3559 result = compose_attach_append(
3560 compose, file_from_uri,
3561 filename, content_type,
3564 g_free(file_from_uri);
3567 alertpanel_error("File %s doesn't exist\n", filename);
3570 if ((size = get_file_size(file)) < 0) {
3571 alertpanel_error("Can't get file size of %s\n", filename);
3575 alertpanel_error(_("File %s is empty."), filename);
3578 if ((fp = g_fopen(file, "rb")) == NULL) {
3579 alertpanel_error(_("Can't read %s."), filename);
3584 ainfo = g_new0(AttachInfo, 1);
3585 auto_ainfo = g_auto_pointer_new_with_free
3586 (ainfo, (GFreeFunc) compose_attach_info_free);
3587 ainfo->file = g_strdup(file);
3590 ainfo->content_type = g_strdup(content_type);
3591 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3593 MsgFlags flags = {0, 0};
3595 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3596 ainfo->encoding = ENC_7BIT;
3598 ainfo->encoding = ENC_8BIT;
3600 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3601 if (msginfo && msginfo->subject)
3602 name = g_strdup(msginfo->subject);
3604 name = g_path_get_basename(filename ? filename : file);
3606 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3608 procmsg_msginfo_free(msginfo);
3610 if (!g_ascii_strncasecmp(content_type, "text/", 5)) {
3611 ainfo->charset = g_strdup(charset);
3612 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3614 ainfo->encoding = ENC_BASE64;
3616 name = g_path_get_basename(filename ? filename : file);
3617 ainfo->name = g_strdup(name);
3621 ainfo->content_type = procmime_get_mime_type(file);
3622 if (!ainfo->content_type) {
3623 ainfo->content_type =
3624 g_strdup("application/octet-stream");
3625 ainfo->encoding = ENC_BASE64;
3626 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text/", 5))
3628 procmime_get_encoding_for_text_file(file, &has_binary);
3630 ainfo->encoding = ENC_BASE64;
3631 name = g_path_get_basename(filename ? filename : file);
3632 ainfo->name = g_strdup(name);
3636 if (ainfo->name != NULL
3637 && !strcmp(ainfo->name, ".")) {
3638 g_free(ainfo->name);
3642 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3643 g_free(ainfo->content_type);
3644 ainfo->content_type = g_strdup("application/octet-stream");
3645 g_free(ainfo->charset);
3646 ainfo->charset = NULL;
3649 ainfo->size = (goffset)size;
3650 size_text = to_human_readable((goffset)size);
3652 store = GTK_LIST_STORE(gtk_tree_view_get_model
3653 (GTK_TREE_VIEW(compose->attach_clist)));
3655 gtk_list_store_append(store, &iter);
3656 gtk_list_store_set(store, &iter,
3657 COL_MIMETYPE, ainfo->content_type,
3658 COL_SIZE, size_text,
3659 COL_NAME, ainfo->name,
3660 COL_CHARSET, ainfo->charset,
3662 COL_AUTODATA, auto_ainfo,
3665 g_auto_pointer_free(auto_ainfo);
3666 compose_attach_update_label(compose);
3670 static void compose_use_signing(Compose *compose, gboolean use_signing)
3672 compose->use_signing = use_signing;
3673 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3676 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3678 compose->use_encryption = use_encryption;
3679 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3682 #define NEXT_PART_NOT_CHILD(info) \
3684 node = info->node; \
3685 while (node->children) \
3686 node = g_node_last_child(node); \
3687 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3690 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3694 MimeInfo *firsttext = NULL;
3695 MimeInfo *encrypted = NULL;
3698 const gchar *partname = NULL;
3700 mimeinfo = procmime_scan_message(msginfo);
3701 if (!mimeinfo) return;
3703 if (mimeinfo->node->children == NULL) {
3704 procmime_mimeinfo_free_all(mimeinfo);
3708 /* find first content part */
3709 child = (MimeInfo *) mimeinfo->node->children->data;
3710 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3711 child = (MimeInfo *)child->node->children->data;
3714 if (child->type == MIMETYPE_TEXT) {
3716 debug_print("First text part found\n");
3717 } else if (compose->mode == COMPOSE_REEDIT &&
3718 child->type == MIMETYPE_APPLICATION &&
3719 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3720 encrypted = (MimeInfo *)child->node->parent->data;
3723 child = (MimeInfo *) mimeinfo->node->children->data;
3724 while (child != NULL) {
3727 if (child == encrypted) {
3728 /* skip this part of tree */
3729 NEXT_PART_NOT_CHILD(child);
3733 if (child->type == MIMETYPE_MULTIPART) {
3734 /* get the actual content */
3735 child = procmime_mimeinfo_next(child);
3739 if (child == firsttext) {
3740 child = procmime_mimeinfo_next(child);
3744 outfile = procmime_get_tmp_file_name(child);
3745 if ((err = procmime_get_part(outfile, child)) < 0)
3746 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3748 gchar *content_type;
3750 content_type = procmime_get_content_type_str(child->type, child->subtype);
3752 /* if we meet a pgp signature, we don't attach it, but
3753 * we force signing. */
3754 if ((strcmp(content_type, "application/pgp-signature") &&
3755 strcmp(content_type, "application/pkcs7-signature") &&
3756 strcmp(content_type, "application/x-pkcs7-signature"))
3757 || compose->mode == COMPOSE_REDIRECT) {
3758 partname = procmime_mimeinfo_get_parameter(child, "filename");
3759 if (partname == NULL)
3760 partname = procmime_mimeinfo_get_parameter(child, "name");
3761 if (partname == NULL)
3763 compose_attach_append(compose, outfile,
3764 partname, content_type,
3765 procmime_mimeinfo_get_parameter(child, "charset"));
3767 compose_force_signing(compose, compose->account, NULL);
3769 g_free(content_type);
3772 NEXT_PART_NOT_CHILD(child);
3774 procmime_mimeinfo_free_all(mimeinfo);
3777 #undef NEXT_PART_NOT_CHILD
3782 WAIT_FOR_INDENT_CHAR,
3783 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3786 /* return indent length, we allow:
3787 indent characters followed by indent characters or spaces/tabs,
3788 alphabets and numbers immediately followed by indent characters,
3789 and the repeating sequences of the above
3790 If quote ends with multiple spaces, only the first one is included. */
3791 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3792 const GtkTextIter *start, gint *len)
3794 GtkTextIter iter = *start;
3798 IndentState state = WAIT_FOR_INDENT_CHAR;
3801 gint alnum_count = 0;
3802 gint space_count = 0;
3805 if (prefs_common.quote_chars == NULL) {
3809 while (!gtk_text_iter_ends_line(&iter)) {
3810 wc = gtk_text_iter_get_char(&iter);
3811 if (g_unichar_iswide(wc))
3813 clen = g_unichar_to_utf8(wc, ch);
3817 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3818 is_space = g_unichar_isspace(wc);
3820 if (state == WAIT_FOR_INDENT_CHAR) {
3821 if (!is_indent && !g_unichar_isalnum(wc))
3824 quote_len += alnum_count + space_count + 1;
3825 alnum_count = space_count = 0;
3826 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3829 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3830 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3834 else if (is_indent) {
3835 quote_len += alnum_count + space_count + 1;
3836 alnum_count = space_count = 0;
3839 state = WAIT_FOR_INDENT_CHAR;
3843 gtk_text_iter_forward_char(&iter);
3846 if (quote_len > 0 && space_count > 0)
3852 if (quote_len > 0) {
3854 gtk_text_iter_forward_chars(&iter, quote_len);
3855 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3861 /* return >0 if the line is itemized */
3862 static int compose_itemized_length(GtkTextBuffer *buffer,
3863 const GtkTextIter *start)
3865 GtkTextIter iter = *start;
3870 if (gtk_text_iter_ends_line(&iter))
3875 wc = gtk_text_iter_get_char(&iter);
3876 if (!g_unichar_isspace(wc))
3878 gtk_text_iter_forward_char(&iter);
3879 if (gtk_text_iter_ends_line(&iter))
3883 clen = g_unichar_to_utf8(wc, ch);
3887 if (!strchr("*-+", ch[0]))
3890 gtk_text_iter_forward_char(&iter);
3891 if (gtk_text_iter_ends_line(&iter))
3893 wc = gtk_text_iter_get_char(&iter);
3894 if (g_unichar_isspace(wc)) {
3900 /* return the string at the start of the itemization */
3901 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3902 const GtkTextIter *start)
3904 GtkTextIter iter = *start;
3907 GString *item_chars = g_string_new("");
3910 if (gtk_text_iter_ends_line(&iter))
3915 wc = gtk_text_iter_get_char(&iter);
3916 if (!g_unichar_isspace(wc))
3918 gtk_text_iter_forward_char(&iter);
3919 if (gtk_text_iter_ends_line(&iter))
3921 g_string_append_unichar(item_chars, wc);
3924 str = item_chars->str;
3925 g_string_free(item_chars, FALSE);
3929 /* return the number of spaces at a line's start */
3930 static int compose_left_offset_length(GtkTextBuffer *buffer,
3931 const GtkTextIter *start)
3933 GtkTextIter iter = *start;
3936 if (gtk_text_iter_ends_line(&iter))
3940 wc = gtk_text_iter_get_char(&iter);
3941 if (!g_unichar_isspace(wc))
3944 gtk_text_iter_forward_char(&iter);
3945 if (gtk_text_iter_ends_line(&iter))
3949 gtk_text_iter_forward_char(&iter);
3950 if (gtk_text_iter_ends_line(&iter))
3955 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3956 const GtkTextIter *start,
3957 GtkTextIter *break_pos,
3961 GtkTextIter iter = *start, line_end = *start;
3962 PangoLogAttr *attrs;
3969 gboolean can_break = FALSE;
3970 gboolean do_break = FALSE;
3971 gboolean was_white = FALSE;
3972 gboolean prev_dont_break = FALSE;
3974 gtk_text_iter_forward_to_line_end(&line_end);
3975 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3976 len = g_utf8_strlen(str, -1);
3980 g_warning("compose_get_line_break_pos: len = 0!\n");
3984 /* g_print("breaking line: %d: %s (len = %d)\n",
3985 gtk_text_iter_get_line(&iter), str, len); */
3987 attrs = g_new(PangoLogAttr, len + 1);
3989 pango_default_break(str, -1, NULL, attrs, len + 1);
3993 /* skip quote and leading spaces */
3994 for (i = 0; *p != '\0' && i < len; i++) {
3997 wc = g_utf8_get_char(p);
3998 if (i >= quote_len && !g_unichar_isspace(wc))
4000 if (g_unichar_iswide(wc))
4002 else if (*p == '\t')
4006 p = g_utf8_next_char(p);
4009 for (; *p != '\0' && i < len; i++) {
4010 PangoLogAttr *attr = attrs + i;
4014 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
4017 was_white = attr->is_white;
4019 /* don't wrap URI */
4020 if ((uri_len = get_uri_len(p)) > 0) {
4022 if (pos > 0 && col > max_col) {
4032 wc = g_utf8_get_char(p);
4033 if (g_unichar_iswide(wc)) {
4035 if (prev_dont_break && can_break && attr->is_line_break)
4037 } else if (*p == '\t')
4041 if (pos > 0 && col > max_col) {
4046 if (*p == '-' || *p == '/')
4047 prev_dont_break = TRUE;
4049 prev_dont_break = FALSE;
4051 p = g_utf8_next_char(p);
4055 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4060 *break_pos = *start;
4061 gtk_text_iter_set_line_offset(break_pos, pos);
4066 static gboolean compose_join_next_line(Compose *compose,
4067 GtkTextBuffer *buffer,
4069 const gchar *quote_str)
4071 GtkTextIter iter_ = *iter, cur, prev, next, end;
4072 PangoLogAttr attrs[3];
4074 gchar *next_quote_str;
4077 gboolean keep_cursor = FALSE;
4079 if (!gtk_text_iter_forward_line(&iter_) ||
4080 gtk_text_iter_ends_line(&iter_)) {
4083 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4085 if ((quote_str || next_quote_str) &&
4086 strcmp2(quote_str, next_quote_str) != 0) {
4087 g_free(next_quote_str);
4090 g_free(next_quote_str);
4093 if (quote_len > 0) {
4094 gtk_text_iter_forward_chars(&end, quote_len);
4095 if (gtk_text_iter_ends_line(&end)) {
4100 /* don't join itemized lines */
4101 if (compose_itemized_length(buffer, &end) > 0) {
4105 /* don't join signature separator */
4106 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4109 /* delete quote str */
4111 gtk_text_buffer_delete(buffer, &iter_, &end);
4113 /* don't join line breaks put by the user */
4115 gtk_text_iter_backward_char(&cur);
4116 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4117 gtk_text_iter_forward_char(&cur);
4121 gtk_text_iter_forward_char(&cur);
4122 /* delete linebreak and extra spaces */
4123 while (gtk_text_iter_backward_char(&cur)) {
4124 wc1 = gtk_text_iter_get_char(&cur);
4125 if (!g_unichar_isspace(wc1))
4130 while (!gtk_text_iter_ends_line(&cur)) {
4131 wc1 = gtk_text_iter_get_char(&cur);
4132 if (!g_unichar_isspace(wc1))
4134 gtk_text_iter_forward_char(&cur);
4137 if (!gtk_text_iter_equal(&prev, &next)) {
4140 mark = gtk_text_buffer_get_insert(buffer);
4141 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4142 if (gtk_text_iter_equal(&prev, &cur))
4144 gtk_text_buffer_delete(buffer, &prev, &next);
4148 /* insert space if required */
4149 gtk_text_iter_backward_char(&prev);
4150 wc1 = gtk_text_iter_get_char(&prev);
4151 wc2 = gtk_text_iter_get_char(&next);
4152 gtk_text_iter_forward_char(&next);
4153 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4154 pango_default_break(str, -1, NULL, attrs, 3);
4155 if (!attrs[1].is_line_break ||
4156 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4157 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4159 gtk_text_iter_backward_char(&iter_);
4160 gtk_text_buffer_place_cursor(buffer, &iter_);
4169 #define ADD_TXT_POS(bp_, ep_, pti_) \
4170 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4171 last = last->next; \
4172 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4173 last->next = NULL; \
4175 g_warning("alloc error scanning URIs\n"); \
4178 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4180 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4181 GtkTextBuffer *buffer;
4182 GtkTextIter iter, break_pos, end_of_line;
4183 gchar *quote_str = NULL;
4185 gboolean wrap_quote = prefs_common.linewrap_quote;
4186 gboolean prev_autowrap = compose->autowrap;
4187 gint startq_offset = -1, noq_offset = -1;
4188 gint uri_start = -1, uri_stop = -1;
4189 gint nouri_start = -1, nouri_stop = -1;
4190 gint num_blocks = 0;
4191 gint quotelevel = -1;
4192 gboolean modified = force;
4193 gboolean removed = FALSE;
4194 gboolean modified_before_remove = FALSE;
4196 gboolean start = TRUE;
4197 gint itemized_len = 0, rem_item_len = 0;
4198 gchar *itemized_chars = NULL;
4199 gboolean item_continuation = FALSE;
4204 if (compose->draft_timeout_tag == -2) {
4208 compose->autowrap = FALSE;
4210 buffer = gtk_text_view_get_buffer(text);
4211 undo_wrapping(compose->undostruct, TRUE);
4216 mark = gtk_text_buffer_get_insert(buffer);
4217 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4221 if (compose->draft_timeout_tag == -2) {
4222 if (gtk_text_iter_ends_line(&iter)) {
4223 while (gtk_text_iter_ends_line(&iter) &&
4224 gtk_text_iter_forward_line(&iter))
4227 while (gtk_text_iter_backward_line(&iter)) {
4228 if (gtk_text_iter_ends_line(&iter)) {
4229 gtk_text_iter_forward_line(&iter);
4235 /* move to line start */
4236 gtk_text_iter_set_line_offset(&iter, 0);
4239 itemized_len = compose_itemized_length(buffer, &iter);
4241 if (!itemized_len) {
4242 itemized_len = compose_left_offset_length(buffer, &iter);
4243 item_continuation = TRUE;
4247 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4249 /* go until paragraph end (empty line) */
4250 while (start || !gtk_text_iter_ends_line(&iter)) {
4251 gchar *scanpos = NULL;
4252 /* parse table - in order of priority */
4254 const gchar *needle; /* token */
4256 /* token search function */
4257 gchar *(*search) (const gchar *haystack,
4258 const gchar *needle);
4259 /* part parsing function */
4260 gboolean (*parse) (const gchar *start,
4261 const gchar *scanpos,
4265 /* part to URI function */
4266 gchar *(*build_uri) (const gchar *bp,
4270 static struct table parser[] = {
4271 {"http://", strcasestr, get_uri_part, make_uri_string},
4272 {"https://", strcasestr, get_uri_part, make_uri_string},
4273 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4274 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4275 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4276 {"www.", strcasestr, get_uri_part, make_http_string},
4277 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4278 {"@", strcasestr, get_email_part, make_email_string}
4280 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4281 gint last_index = PARSE_ELEMS;
4283 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4287 if (!prev_autowrap && num_blocks == 0) {
4289 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4290 G_CALLBACK(text_inserted),
4293 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4296 uri_start = uri_stop = -1;
4298 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4301 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4302 if (startq_offset == -1)
4303 startq_offset = gtk_text_iter_get_offset(&iter);
4304 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4305 if (quotelevel > 2) {
4306 /* recycle colors */
4307 if (prefs_common.recycle_quote_colors)
4316 if (startq_offset == -1)
4317 noq_offset = gtk_text_iter_get_offset(&iter);
4321 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4324 if (gtk_text_iter_ends_line(&iter)) {
4326 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4327 prefs_common.linewrap_len,
4329 GtkTextIter prev, next, cur;
4330 if (prev_autowrap != FALSE || force) {
4331 compose->automatic_break = TRUE;
4333 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4334 compose->automatic_break = FALSE;
4335 if (itemized_len && compose->autoindent) {
4336 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4337 if (!item_continuation)
4338 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4340 } else if (quote_str && wrap_quote) {
4341 compose->automatic_break = TRUE;
4343 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4344 compose->automatic_break = FALSE;
4345 if (itemized_len && compose->autoindent) {
4346 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4347 if (!item_continuation)
4348 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4352 /* remove trailing spaces */
4354 rem_item_len = itemized_len;
4355 while (compose->autoindent && rem_item_len-- > 0)
4356 gtk_text_iter_backward_char(&cur);
4357 gtk_text_iter_backward_char(&cur);
4360 while (!gtk_text_iter_starts_line(&cur)) {
4363 gtk_text_iter_backward_char(&cur);
4364 wc = gtk_text_iter_get_char(&cur);
4365 if (!g_unichar_isspace(wc))
4369 if (!gtk_text_iter_equal(&prev, &next)) {
4370 gtk_text_buffer_delete(buffer, &prev, &next);
4372 gtk_text_iter_forward_char(&break_pos);
4376 gtk_text_buffer_insert(buffer, &break_pos,
4380 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4382 /* move iter to current line start */
4383 gtk_text_iter_set_line_offset(&iter, 0);
4390 /* move iter to next line start */
4396 if (!prev_autowrap && num_blocks > 0) {
4398 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4399 G_CALLBACK(text_inserted),
4403 while (!gtk_text_iter_ends_line(&end_of_line)) {
4404 gtk_text_iter_forward_char(&end_of_line);
4406 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4408 nouri_start = gtk_text_iter_get_offset(&iter);
4409 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4411 walk_pos = gtk_text_iter_get_offset(&iter);
4412 /* FIXME: this looks phony. scanning for anything in the parse table */
4413 for (n = 0; n < PARSE_ELEMS; n++) {
4416 tmp = parser[n].search(walk, parser[n].needle);
4418 if (scanpos == NULL || tmp < scanpos) {
4427 /* check if URI can be parsed */
4428 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4429 (const gchar **)&ep, FALSE)
4430 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4434 strlen(parser[last_index].needle);
4437 uri_start = walk_pos + (bp - o_walk);
4438 uri_stop = walk_pos + (ep - o_walk);
4442 gtk_text_iter_forward_line(&iter);
4445 if (startq_offset != -1) {
4446 GtkTextIter startquote, endquote;
4447 gtk_text_buffer_get_iter_at_offset(
4448 buffer, &startquote, startq_offset);
4451 switch (quotelevel) {
4453 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4454 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4455 gtk_text_buffer_apply_tag_by_name(
4456 buffer, "quote0", &startquote, &endquote);
4457 gtk_text_buffer_remove_tag_by_name(
4458 buffer, "quote1", &startquote, &endquote);
4459 gtk_text_buffer_remove_tag_by_name(
4460 buffer, "quote2", &startquote, &endquote);
4465 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4466 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4467 gtk_text_buffer_apply_tag_by_name(
4468 buffer, "quote1", &startquote, &endquote);
4469 gtk_text_buffer_remove_tag_by_name(
4470 buffer, "quote0", &startquote, &endquote);
4471 gtk_text_buffer_remove_tag_by_name(
4472 buffer, "quote2", &startquote, &endquote);
4477 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4478 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4479 gtk_text_buffer_apply_tag_by_name(
4480 buffer, "quote2", &startquote, &endquote);
4481 gtk_text_buffer_remove_tag_by_name(
4482 buffer, "quote0", &startquote, &endquote);
4483 gtk_text_buffer_remove_tag_by_name(
4484 buffer, "quote1", &startquote, &endquote);
4490 } else if (noq_offset != -1) {
4491 GtkTextIter startnoquote, endnoquote;
4492 gtk_text_buffer_get_iter_at_offset(
4493 buffer, &startnoquote, noq_offset);
4496 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4497 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4498 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4499 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4500 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4501 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4502 gtk_text_buffer_remove_tag_by_name(
4503 buffer, "quote0", &startnoquote, &endnoquote);
4504 gtk_text_buffer_remove_tag_by_name(
4505 buffer, "quote1", &startnoquote, &endnoquote);
4506 gtk_text_buffer_remove_tag_by_name(
4507 buffer, "quote2", &startnoquote, &endnoquote);
4513 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4514 GtkTextIter nouri_start_iter, nouri_end_iter;
4515 gtk_text_buffer_get_iter_at_offset(
4516 buffer, &nouri_start_iter, nouri_start);
4517 gtk_text_buffer_get_iter_at_offset(
4518 buffer, &nouri_end_iter, nouri_stop);
4519 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4520 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4521 gtk_text_buffer_remove_tag_by_name(
4522 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4523 modified_before_remove = modified;
4528 if (uri_start >= 0 && uri_stop > 0) {
4529 GtkTextIter uri_start_iter, uri_end_iter, back;
4530 gtk_text_buffer_get_iter_at_offset(
4531 buffer, &uri_start_iter, uri_start);
4532 gtk_text_buffer_get_iter_at_offset(
4533 buffer, &uri_end_iter, uri_stop);
4534 back = uri_end_iter;
4535 gtk_text_iter_backward_char(&back);
4536 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4537 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4538 gtk_text_buffer_apply_tag_by_name(
4539 buffer, "link", &uri_start_iter, &uri_end_iter);
4541 if (removed && !modified_before_remove) {
4547 // debug_print("not modified, out after %d lines\n", lines);
4551 // debug_print("modified, out after %d lines\n", lines);
4553 g_free(itemized_chars);
4556 undo_wrapping(compose->undostruct, FALSE);
4557 compose->autowrap = prev_autowrap;
4562 void compose_action_cb(void *data)
4564 Compose *compose = (Compose *)data;
4565 compose_wrap_all(compose);
4568 static void compose_wrap_all(Compose *compose)
4570 compose_wrap_all_full(compose, FALSE);
4573 static void compose_wrap_all_full(Compose *compose, gboolean force)
4575 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4576 GtkTextBuffer *buffer;
4578 gboolean modified = TRUE;
4580 buffer = gtk_text_view_get_buffer(text);
4582 gtk_text_buffer_get_start_iter(buffer, &iter);
4583 while (!gtk_text_iter_is_end(&iter) && modified)
4584 modified = compose_beautify_paragraph(compose, &iter, force);
4588 static void compose_set_title(Compose *compose)
4594 edited = compose->modified ? _(" [Edited]") : "";
4596 subject = gtk_editable_get_chars(
4597 GTK_EDITABLE(compose->subject_entry), 0, -1);
4599 #ifndef GENERIC_UMPC
4600 if (subject && strlen(subject))
4601 str = g_strdup_printf(_("%s - Compose message%s"),
4604 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4606 str = g_strdup(_("Compose message"));
4609 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4615 * compose_current_mail_account:
4617 * Find a current mail account (the currently selected account, or the
4618 * default account, if a news account is currently selected). If a
4619 * mail account cannot be found, display an error message.
4621 * Return value: Mail account, or NULL if not found.
4623 static PrefsAccount *
4624 compose_current_mail_account(void)
4628 if (cur_account && cur_account->protocol != A_NNTP)
4631 ac = account_get_default();
4632 if (!ac || ac->protocol == A_NNTP) {
4633 alertpanel_error(_("Account for sending mail is not specified.\n"
4634 "Please select a mail account before sending."));
4641 #define QUOTE_IF_REQUIRED(out, str) \
4643 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4647 len = strlen(str) + 3; \
4648 if ((__tmp = alloca(len)) == NULL) { \
4649 g_warning("can't allocate memory\n"); \
4650 g_string_free(header, TRUE); \
4653 g_snprintf(__tmp, len, "\"%s\"", str); \
4658 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4659 g_warning("can't allocate memory\n"); \
4660 g_string_free(header, TRUE); \
4663 strcpy(__tmp, str); \
4669 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4671 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4675 len = strlen(str) + 3; \
4676 if ((__tmp = alloca(len)) == NULL) { \
4677 g_warning("can't allocate memory\n"); \
4680 g_snprintf(__tmp, len, "\"%s\"", str); \
4685 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4686 g_warning("can't allocate memory\n"); \
4689 strcpy(__tmp, str); \
4695 static void compose_select_account(Compose *compose, PrefsAccount *account,
4698 gchar *from = NULL, *header;
4699 ComposeHeaderEntry *header_entry;
4701 cm_return_if_fail(account != NULL);
4703 compose->account = account;
4704 if (account->name && *account->name) {
4706 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4707 from = g_strdup_printf("%s <%s>",
4708 buf, account->address);
4709 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4711 from = g_strdup_printf("<%s>",
4713 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4718 compose_set_title(compose);
4720 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4721 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4723 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4724 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4725 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4727 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4729 activate_privacy_system(compose, account, FALSE);
4731 if (!init && compose->mode != COMPOSE_REDIRECT) {
4732 undo_block(compose->undostruct);
4733 compose_insert_sig(compose, TRUE);
4734 undo_unblock(compose->undostruct);
4737 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4738 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4740 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4741 if (account->protocol == A_NNTP) {
4742 if (!strcmp(header, _("To:")))
4743 combobox_select_by_text(
4744 GTK_COMBO_BOX(header_entry->combo),
4747 if (!strcmp(header, _("Newsgroups:")))
4748 combobox_select_by_text(
4749 GTK_COMBO_BOX(header_entry->combo),
4757 /* use account's dict info if set */
4758 if (compose->gtkaspell) {
4759 if (account->enable_default_dictionary)
4760 gtkaspell_change_dict(compose->gtkaspell,
4761 account->default_dictionary, FALSE);
4762 if (account->enable_default_alt_dictionary)
4763 gtkaspell_change_alt_dict(compose->gtkaspell,
4764 account->default_alt_dictionary);
4765 if (account->enable_default_dictionary
4766 || account->enable_default_alt_dictionary)
4767 compose_spell_menu_changed(compose);
4772 gboolean compose_check_for_valid_recipient(Compose *compose) {
4773 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4774 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4775 gboolean recipient_found = FALSE;
4779 /* free to and newsgroup list */
4780 slist_free_strings(compose->to_list);
4781 g_slist_free(compose->to_list);
4782 compose->to_list = NULL;
4784 slist_free_strings(compose->newsgroup_list);
4785 g_slist_free(compose->newsgroup_list);
4786 compose->newsgroup_list = NULL;
4788 /* search header entries for to and newsgroup entries */
4789 for (list = compose->header_list; list; list = list->next) {
4792 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4793 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4796 if (entry[0] != '\0') {
4797 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4798 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4799 compose->to_list = address_list_append(compose->to_list, entry);
4800 recipient_found = TRUE;
4803 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4804 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4805 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4806 recipient_found = TRUE;
4813 return recipient_found;
4816 static gboolean compose_check_for_set_recipients(Compose *compose)
4818 if (compose->account->set_autocc && compose->account->auto_cc) {
4819 gboolean found_other = FALSE;
4821 /* search header entries for to and newsgroup entries */
4822 for (list = compose->header_list; list; list = list->next) {
4825 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4826 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4829 if (strcmp(entry, compose->account->auto_cc)
4830 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4840 if (compose->batch) {
4841 gtk_widget_show_all(compose->window);
4843 aval = alertpanel(_("Send"),
4844 _("The only recipient is the default CC address. Send anyway?"),
4845 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4846 if (aval != G_ALERTALTERNATE)
4850 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4851 gboolean found_other = FALSE;
4853 /* search header entries for to and newsgroup entries */
4854 for (list = compose->header_list; list; list = list->next) {
4857 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4858 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4861 if (strcmp(entry, compose->account->auto_bcc)
4862 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4872 if (compose->batch) {
4873 gtk_widget_show_all(compose->window);
4875 aval = alertpanel(_("Send"),
4876 _("The only recipient is the default BCC address. Send anyway?"),
4877 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4878 if (aval != G_ALERTALTERNATE)
4885 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4889 if (compose_check_for_valid_recipient(compose) == FALSE) {
4890 if (compose->batch) {
4891 gtk_widget_show_all(compose->window);
4893 alertpanel_error(_("Recipient is not specified."));
4897 if (compose_check_for_set_recipients(compose) == FALSE) {
4901 if (!compose->batch) {
4902 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4903 if (*str == '\0' && check_everything == TRUE &&
4904 compose->mode != COMPOSE_REDIRECT) {
4906 gchar *button_label;
4909 if (compose->sending)
4910 button_label = _("+_Send");
4912 button_label = _("+_Queue");
4913 message = g_strdup_printf(_("Subject is empty. %s"),
4914 compose->sending?_("Send it anyway?"):
4915 _("Queue it anyway?"));
4917 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4918 GTK_STOCK_CANCEL, button_label, NULL);
4920 if (aval != G_ALERTALTERNATE)
4925 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4931 gint compose_send(Compose *compose)
4934 FolderItem *folder = NULL;
4936 gchar *msgpath = NULL;
4937 gboolean discard_window = FALSE;
4938 gchar *errstr = NULL;
4939 gchar *tmsgid = NULL;
4940 MainWindow *mainwin = mainwindow_get_mainwindow();
4941 gboolean queued_removed = FALSE;
4943 if (prefs_common.send_dialog_invisible
4944 || compose->batch == TRUE)
4945 discard_window = TRUE;
4947 compose_allow_user_actions (compose, FALSE);
4948 compose->sending = TRUE;
4950 if (compose_check_entries(compose, TRUE) == FALSE) {
4951 if (compose->batch) {
4952 gtk_widget_show_all(compose->window);
4958 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4961 if (compose->batch) {
4962 gtk_widget_show_all(compose->window);
4965 alertpanel_error(_("Could not queue message for sending:\n\n"
4966 "Charset conversion failed."));
4967 } else if (val == -5) {
4968 alertpanel_error(_("Could not queue message for sending:\n\n"
4969 "Couldn't get recipient encryption key."));
4970 } else if (val == -6) {
4972 } else if (val == -3) {
4973 if (privacy_peek_error())
4974 alertpanel_error(_("Could not queue message for sending:\n\n"
4975 "Signature failed: %s"), privacy_get_error());
4976 } else if (val == -2 && errno != 0) {
4977 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4979 alertpanel_error(_("Could not queue message for sending."));
4984 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4985 if (discard_window) {
4986 compose->sending = FALSE;
4987 compose_close(compose);
4988 /* No more compose access in the normal codepath
4989 * after this point! */
4994 alertpanel_error(_("The message was queued but could not be "
4995 "sent.\nUse \"Send queued messages\" from "
4996 "the main window to retry."));
4997 if (!discard_window) {
5004 if (msgpath == NULL) {
5005 msgpath = folder_item_fetch_msg(folder, msgnum);
5006 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
5009 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
5010 claws_unlink(msgpath);
5013 if (!discard_window) {
5015 if (!queued_removed)
5016 folder_item_remove_msg(folder, msgnum);
5017 folder_item_scan(folder);
5019 /* make sure we delete that */
5020 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5022 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5023 folder_item_remove_msg(folder, tmp->msgnum);
5024 procmsg_msginfo_free(tmp);
5031 if (!queued_removed)
5032 folder_item_remove_msg(folder, msgnum);
5033 folder_item_scan(folder);
5035 /* make sure we delete that */
5036 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5038 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5039 folder_item_remove_msg(folder, tmp->msgnum);
5040 procmsg_msginfo_free(tmp);
5043 if (!discard_window) {
5044 compose->sending = FALSE;
5045 compose_allow_user_actions (compose, TRUE);
5046 compose_close(compose);
5050 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5051 "the main window to retry."), errstr);
5054 alertpanel_error_log(_("The message was queued but could not be "
5055 "sent.\nUse \"Send queued messages\" from "
5056 "the main window to retry."));
5058 if (!discard_window) {
5067 toolbar_main_set_sensitive(mainwin);
5068 main_window_set_menu_sensitive(mainwin);
5074 compose_allow_user_actions (compose, TRUE);
5075 compose->sending = FALSE;
5076 compose->modified = TRUE;
5077 toolbar_main_set_sensitive(mainwin);
5078 main_window_set_menu_sensitive(mainwin);
5083 static gboolean compose_use_attach(Compose *compose)
5085 GtkTreeModel *model = gtk_tree_view_get_model
5086 (GTK_TREE_VIEW(compose->attach_clist));
5087 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5090 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5093 gchar buf[BUFFSIZE];
5095 gboolean first_to_address;
5096 gboolean first_cc_address;
5098 ComposeHeaderEntry *headerentry;
5099 const gchar *headerentryname;
5100 const gchar *cc_hdr;
5101 const gchar *to_hdr;
5102 gboolean err = FALSE;
5104 debug_print("Writing redirect header\n");
5106 cc_hdr = prefs_common_translated_header_name("Cc:");
5107 to_hdr = prefs_common_translated_header_name("To:");
5109 first_to_address = TRUE;
5110 for (list = compose->header_list; list; list = list->next) {
5111 headerentry = ((ComposeHeaderEntry *)list->data);
5112 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5114 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5115 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5116 Xstrdup_a(str, entstr, return -1);
5118 if (str[0] != '\0') {
5119 compose_convert_header
5120 (compose, buf, sizeof(buf), str,
5121 strlen("Resent-To") + 2, TRUE);
5123 if (first_to_address) {
5124 err |= (fprintf(fp, "Resent-To: ") < 0);
5125 first_to_address = FALSE;
5127 err |= (fprintf(fp, ",") < 0);
5129 err |= (fprintf(fp, "%s", buf) < 0);
5133 if (!first_to_address) {
5134 err |= (fprintf(fp, "\n") < 0);
5137 first_cc_address = TRUE;
5138 for (list = compose->header_list; list; list = list->next) {
5139 headerentry = ((ComposeHeaderEntry *)list->data);
5140 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5142 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5143 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5144 Xstrdup_a(str, strg, return -1);
5146 if (str[0] != '\0') {
5147 compose_convert_header
5148 (compose, buf, sizeof(buf), str,
5149 strlen("Resent-Cc") + 2, TRUE);
5151 if (first_cc_address) {
5152 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5153 first_cc_address = FALSE;
5155 err |= (fprintf(fp, ",") < 0);
5157 err |= (fprintf(fp, "%s", buf) < 0);
5161 if (!first_cc_address) {
5162 err |= (fprintf(fp, "\n") < 0);
5165 return (err ? -1:0);
5168 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5170 gchar buf[BUFFSIZE];
5172 const gchar *entstr;
5173 /* struct utsname utsbuf; */
5174 gboolean err = FALSE;
5176 cm_return_val_if_fail(fp != NULL, -1);
5177 cm_return_val_if_fail(compose->account != NULL, -1);
5178 cm_return_val_if_fail(compose->account->address != NULL, -1);
5181 get_rfc822_date(buf, sizeof(buf));
5182 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5185 if (compose->account->name && *compose->account->name) {
5186 compose_convert_header
5187 (compose, buf, sizeof(buf), compose->account->name,
5188 strlen("From: "), TRUE);
5189 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5190 buf, compose->account->address) < 0);
5192 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5195 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5196 if (*entstr != '\0') {
5197 Xstrdup_a(str, entstr, return -1);
5200 compose_convert_header(compose, buf, sizeof(buf), str,
5201 strlen("Subject: "), FALSE);
5202 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5206 /* Resent-Message-ID */
5207 if (compose->account->set_domain && compose->account->domain) {
5208 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5209 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5210 g_snprintf(buf, sizeof(buf), "%s",
5211 strchr(compose->account->address, '@') ?
5212 strchr(compose->account->address, '@')+1 :
5213 compose->account->address);
5215 g_snprintf(buf, sizeof(buf), "%s", "");
5218 if (compose->account->gen_msgid) {
5220 if (compose->account->msgid_with_addr) {
5221 addr = compose->account->address;
5223 generate_msgid(buf, sizeof(buf), addr);
5224 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5225 compose->msgid = g_strdup(buf);
5227 compose->msgid = NULL;
5230 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5233 /* separator between header and body */
5234 err |= (fputs("\n", fp) == EOF);
5236 return (err ? -1:0);
5239 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5243 gchar buf[BUFFSIZE];
5245 gboolean skip = FALSE;
5246 gboolean err = FALSE;
5247 gchar *not_included[]={
5248 "Return-Path:", "Delivered-To:", "Received:",
5249 "Subject:", "X-UIDL:", "AF:",
5250 "NF:", "PS:", "SRH:",
5251 "SFN:", "DSR:", "MID:",
5252 "CFG:", "PT:", "S:",
5253 "RQ:", "SSV:", "NSV:",
5254 "SSH:", "R:", "MAID:",
5255 "NAID:", "RMID:", "FMID:",
5256 "SCF:", "RRCPT:", "NG:",
5257 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5258 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5259 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5260 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5261 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5264 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5265 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5269 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5271 for (i = 0; not_included[i] != NULL; i++) {
5272 if (g_ascii_strncasecmp(buf, not_included[i],
5273 strlen(not_included[i])) == 0) {
5280 if (fputs(buf, fdest) == -1)
5283 if (!prefs_common.redirect_keep_from) {
5284 if (g_ascii_strncasecmp(buf, "From:",
5285 strlen("From:")) == 0) {
5286 err |= (fputs(" (by way of ", fdest) == EOF);
5287 if (compose->account->name
5288 && *compose->account->name) {
5289 compose_convert_header
5290 (compose, buf, sizeof(buf),
5291 compose->account->name,
5294 err |= (fprintf(fdest, "%s <%s>",
5296 compose->account->address) < 0);
5298 err |= (fprintf(fdest, "%s",
5299 compose->account->address) < 0);
5300 err |= (fputs(")", fdest) == EOF);
5304 if (fputs("\n", fdest) == -1)
5311 if (compose_redirect_write_headers(compose, fdest))
5314 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5315 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5328 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5330 GtkTextBuffer *buffer;
5331 GtkTextIter start, end;
5334 const gchar *out_codeset;
5335 EncodingType encoding = ENC_UNKNOWN;
5336 MimeInfo *mimemsg, *mimetext;
5338 const gchar *src_codeset = CS_INTERNAL;
5339 gchar *from_addr = NULL;
5340 gchar *from_name = NULL;
5342 if (action == COMPOSE_WRITE_FOR_SEND)
5343 attach_parts = TRUE;
5345 /* create message MimeInfo */
5346 mimemsg = procmime_mimeinfo_new();
5347 mimemsg->type = MIMETYPE_MESSAGE;
5348 mimemsg->subtype = g_strdup("rfc822");
5349 mimemsg->content = MIMECONTENT_MEM;
5350 mimemsg->tmp = TRUE; /* must free content later */
5351 mimemsg->data.mem = compose_get_header(compose);
5353 /* Create text part MimeInfo */
5354 /* get all composed text */
5355 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5356 gtk_text_buffer_get_start_iter(buffer, &start);
5357 gtk_text_buffer_get_end_iter(buffer, &end);
5358 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5360 out_codeset = conv_get_charset_str(compose->out_encoding);
5362 if (!out_codeset && is_ascii_str(chars)) {
5363 out_codeset = CS_US_ASCII;
5364 } else if (prefs_common.outgoing_fallback_to_ascii &&
5365 is_ascii_str(chars)) {
5366 out_codeset = CS_US_ASCII;
5367 encoding = ENC_7BIT;
5371 gchar *test_conv_global_out = NULL;
5372 gchar *test_conv_reply = NULL;
5374 /* automatic mode. be automatic. */
5375 codeconv_set_strict(TRUE);
5377 out_codeset = conv_get_outgoing_charset_str();
5379 debug_print("trying to convert to %s\n", out_codeset);
5380 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5383 if (!test_conv_global_out && compose->orig_charset
5384 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5385 out_codeset = compose->orig_charset;
5386 debug_print("failure; trying to convert to %s\n", out_codeset);
5387 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5390 if (!test_conv_global_out && !test_conv_reply) {
5392 out_codeset = CS_INTERNAL;
5393 debug_print("failure; finally using %s\n", out_codeset);
5395 g_free(test_conv_global_out);
5396 g_free(test_conv_reply);
5397 codeconv_set_strict(FALSE);
5400 if (encoding == ENC_UNKNOWN) {
5401 if (prefs_common.encoding_method == CTE_BASE64)
5402 encoding = ENC_BASE64;
5403 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5404 encoding = ENC_QUOTED_PRINTABLE;
5405 else if (prefs_common.encoding_method == CTE_8BIT)
5406 encoding = ENC_8BIT;
5408 encoding = procmime_get_encoding_for_charset(out_codeset);
5411 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5412 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5414 if (action == COMPOSE_WRITE_FOR_SEND) {
5415 codeconv_set_strict(TRUE);
5416 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5417 codeconv_set_strict(FALSE);
5423 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5424 "to the specified %s charset.\n"
5425 "Send it as %s?"), out_codeset, src_codeset);
5426 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5427 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5430 if (aval != G_ALERTALTERNATE) {
5435 out_codeset = src_codeset;
5441 out_codeset = src_codeset;
5446 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5447 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5448 strstr(buf, "\nFrom ") != NULL) {
5449 encoding = ENC_QUOTED_PRINTABLE;
5453 mimetext = procmime_mimeinfo_new();
5454 mimetext->content = MIMECONTENT_MEM;
5455 mimetext->tmp = TRUE; /* must free content later */
5456 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5457 * and free the data, which we need later. */
5458 mimetext->data.mem = g_strdup(buf);
5459 mimetext->type = MIMETYPE_TEXT;
5460 mimetext->subtype = g_strdup("plain");
5461 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5462 g_strdup(out_codeset));
5464 /* protect trailing spaces when signing message */
5465 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5466 privacy_system_can_sign(compose->privacy_system)) {
5467 encoding = ENC_QUOTED_PRINTABLE;
5470 debug_print("main text: %zd bytes encoded as %s in %d\n",
5471 strlen(buf), out_codeset, encoding);
5473 /* check for line length limit */
5474 if (action == COMPOSE_WRITE_FOR_SEND &&
5475 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5476 check_line_length(buf, 1000, &line) < 0) {
5480 msg = g_strdup_printf
5481 (_("Line %d exceeds the line length limit (998 bytes).\n"
5482 "The contents of the message might be broken on the way to the delivery.\n"
5484 "Send it anyway?"), line + 1);
5485 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5487 if (aval != G_ALERTALTERNATE) {
5493 if (encoding != ENC_UNKNOWN)
5494 procmime_encode_content(mimetext, encoding);
5496 /* append attachment parts */
5497 if (compose_use_attach(compose) && attach_parts) {
5498 MimeInfo *mimempart;
5499 gchar *boundary = NULL;
5500 mimempart = procmime_mimeinfo_new();
5501 mimempart->content = MIMECONTENT_EMPTY;
5502 mimempart->type = MIMETYPE_MULTIPART;
5503 mimempart->subtype = g_strdup("mixed");
5507 boundary = generate_mime_boundary(NULL);
5508 } while (strstr(buf, boundary) != NULL);
5510 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5513 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5515 g_node_append(mimempart->node, mimetext->node);
5516 g_node_append(mimemsg->node, mimempart->node);
5518 if (compose_add_attachments(compose, mimempart) < 0)
5521 g_node_append(mimemsg->node, mimetext->node);
5525 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5526 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5527 /* extract name and address */
5528 if (strstr(spec, " <") && strstr(spec, ">")) {
5529 from_addr = g_strdup(strrchr(spec, '<')+1);
5530 *(strrchr(from_addr, '>')) = '\0';
5531 from_name = g_strdup(spec);
5532 *(strrchr(from_name, '<')) = '\0';
5539 /* sign message if sending */
5540 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5541 privacy_system_can_sign(compose->privacy_system))
5542 if (!privacy_sign(compose->privacy_system, mimemsg,
5543 compose->account, from_addr)) {
5550 procmime_write_mimeinfo(mimemsg, fp);
5552 procmime_mimeinfo_free_all(mimemsg);
5557 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5559 GtkTextBuffer *buffer;
5560 GtkTextIter start, end;
5565 if ((fp = g_fopen(file, "wb")) == NULL) {
5566 FILE_OP_ERROR(file, "fopen");
5570 /* chmod for security */
5571 if (change_file_mode_rw(fp, file) < 0) {
5572 FILE_OP_ERROR(file, "chmod");
5573 g_warning("can't change file mode\n");
5576 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5577 gtk_text_buffer_get_start_iter(buffer, &start);
5578 gtk_text_buffer_get_end_iter(buffer, &end);
5579 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5581 chars = conv_codeset_strdup
5582 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5585 if (!chars) return -1;
5588 len = strlen(chars);
5589 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5590 FILE_OP_ERROR(file, "fwrite");
5599 if (fclose(fp) == EOF) {
5600 FILE_OP_ERROR(file, "fclose");
5607 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5610 MsgInfo *msginfo = compose->targetinfo;
5612 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5613 if (!msginfo) return -1;
5615 if (!force && MSG_IS_LOCKED(msginfo->flags))
5618 item = msginfo->folder;
5619 cm_return_val_if_fail(item != NULL, -1);
5621 if (procmsg_msg_exist(msginfo) &&
5622 (folder_has_parent_of_type(item, F_QUEUE) ||
5623 folder_has_parent_of_type(item, F_DRAFT)
5624 || msginfo == compose->autosaved_draft)) {
5625 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5626 g_warning("can't remove the old message\n");
5629 debug_print("removed reedit target %d\n", msginfo->msgnum);
5636 static void compose_remove_draft(Compose *compose)
5639 MsgInfo *msginfo = compose->targetinfo;
5640 drafts = account_get_special_folder(compose->account, F_DRAFT);
5642 if (procmsg_msg_exist(msginfo)) {
5643 folder_item_remove_msg(drafts, msginfo->msgnum);
5648 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5649 gboolean remove_reedit_target)
5651 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5654 static gboolean compose_warn_encryption(Compose *compose)
5656 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5657 AlertValue val = G_ALERTALTERNATE;
5659 if (warning == NULL)
5662 val = alertpanel_full(_("Encryption warning"), warning,
5663 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5664 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5665 if (val & G_ALERTDISABLE) {
5666 val &= ~G_ALERTDISABLE;
5667 if (val == G_ALERTALTERNATE)
5668 privacy_inhibit_encrypt_warning(compose->privacy_system,
5672 if (val == G_ALERTALTERNATE) {
5679 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5680 gchar **msgpath, gboolean check_subject,
5681 gboolean remove_reedit_target)
5688 static gboolean lock = FALSE;
5689 PrefsAccount *mailac = NULL, *newsac = NULL;
5690 gboolean err = FALSE;
5692 debug_print("queueing message...\n");
5693 cm_return_val_if_fail(compose->account != NULL, -1);
5697 if (compose_check_entries(compose, check_subject) == FALSE) {
5699 if (compose->batch) {
5700 gtk_widget_show_all(compose->window);
5705 if (!compose->to_list && !compose->newsgroup_list) {
5706 g_warning("can't get recipient list.");
5711 if (compose->to_list) {
5712 if (compose->account->protocol != A_NNTP)
5713 mailac = compose->account;
5714 else if (cur_account && cur_account->protocol != A_NNTP)
5715 mailac = cur_account;
5716 else if (!(mailac = compose_current_mail_account())) {
5718 alertpanel_error(_("No account for sending mails available!"));
5723 if (compose->newsgroup_list) {
5724 if (compose->account->protocol == A_NNTP)
5725 newsac = compose->account;
5728 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5733 /* write queue header */
5734 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5735 G_DIR_SEPARATOR, compose, (guint) rand());
5736 debug_print("queuing to %s\n", tmp);
5737 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5738 FILE_OP_ERROR(tmp, "fopen");
5744 if (change_file_mode_rw(fp, tmp) < 0) {
5745 FILE_OP_ERROR(tmp, "chmod");
5746 g_warning("can't change file mode\n");
5749 /* queueing variables */
5750 err |= (fprintf(fp, "AF:\n") < 0);
5751 err |= (fprintf(fp, "NF:0\n") < 0);
5752 err |= (fprintf(fp, "PS:10\n") < 0);
5753 err |= (fprintf(fp, "SRH:1\n") < 0);
5754 err |= (fprintf(fp, "SFN:\n") < 0);
5755 err |= (fprintf(fp, "DSR:\n") < 0);
5757 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5759 err |= (fprintf(fp, "MID:\n") < 0);
5760 err |= (fprintf(fp, "CFG:\n") < 0);
5761 err |= (fprintf(fp, "PT:0\n") < 0);
5762 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5763 err |= (fprintf(fp, "RQ:\n") < 0);
5765 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5767 err |= (fprintf(fp, "SSV:\n") < 0);
5769 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5771 err |= (fprintf(fp, "NSV:\n") < 0);
5772 err |= (fprintf(fp, "SSH:\n") < 0);
5773 /* write recepient list */
5774 if (compose->to_list) {
5775 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5776 for (cur = compose->to_list->next; cur != NULL;
5778 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5779 err |= (fprintf(fp, "\n") < 0);
5781 /* write newsgroup list */
5782 if (compose->newsgroup_list) {
5783 err |= (fprintf(fp, "NG:") < 0);
5784 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5785 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5786 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5787 err |= (fprintf(fp, "\n") < 0);
5789 /* Sylpheed account IDs */
5791 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5793 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5796 if (compose->privacy_system != NULL) {
5797 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5798 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5799 if (compose->use_encryption) {
5801 if (!compose_warn_encryption(compose)) {
5808 if (mailac && mailac->encrypt_to_self) {
5809 GSList *tmp_list = g_slist_copy(compose->to_list);
5810 tmp_list = g_slist_append(tmp_list, compose->account->address);
5811 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5812 g_slist_free(tmp_list);
5814 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5816 if (encdata != NULL) {
5817 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5818 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5819 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5821 } /* else we finally dont want to encrypt */
5823 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5824 /* and if encdata was null, it means there's been a problem in
5836 /* Save copy folder */
5837 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5838 gchar *savefolderid;
5840 savefolderid = compose_get_save_to(compose);
5841 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5842 g_free(savefolderid);
5844 /* Save copy folder */
5845 if (compose->return_receipt) {
5846 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5848 /* Message-ID of message replying to */
5849 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5852 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5853 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5856 /* Message-ID of message forwarding to */
5857 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5860 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5861 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5865 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5866 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5868 /* end of headers */
5869 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5871 if (compose->redirect_filename != NULL) {
5872 if (compose_redirect_write_to_file(compose, fp) < 0) {
5881 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5886 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5890 g_warning("failed to write queue message\n");
5897 if (fclose(fp) == EOF) {
5898 FILE_OP_ERROR(tmp, "fclose");
5905 if (item && *item) {
5908 queue = account_get_special_folder(compose->account, F_QUEUE);
5911 g_warning("can't find queue folder\n");
5917 folder_item_scan(queue);
5918 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5919 g_warning("can't queue the message\n");
5926 if (msgpath == NULL) {
5932 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5933 compose_remove_reedit_target(compose, FALSE);
5936 if ((msgnum != NULL) && (item != NULL)) {
5944 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5947 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5949 struct stat statbuf;
5950 gchar *type, *subtype;
5951 GtkTreeModel *model;
5954 model = gtk_tree_view_get_model(tree_view);
5956 if (!gtk_tree_model_get_iter_first(model, &iter))
5959 gtk_tree_model_get(model, &iter,
5963 if (!is_file_exist(ainfo->file)) {
5964 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5965 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5966 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5968 if (val == G_ALERTDEFAULT) {
5973 mimepart = procmime_mimeinfo_new();
5974 mimepart->content = MIMECONTENT_FILE;
5975 mimepart->data.filename = g_strdup(ainfo->file);
5976 mimepart->tmp = FALSE; /* or we destroy our attachment */
5977 mimepart->offset = 0;
5979 g_stat(ainfo->file, &statbuf);
5980 mimepart->length = statbuf.st_size;
5982 type = g_strdup(ainfo->content_type);
5984 if (!strchr(type, '/')) {
5986 type = g_strdup("application/octet-stream");
5989 subtype = strchr(type, '/') + 1;
5990 *(subtype - 1) = '\0';
5991 mimepart->type = procmime_get_media_type(type);
5992 mimepart->subtype = g_strdup(subtype);
5995 if (mimepart->type == MIMETYPE_MESSAGE &&
5996 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5997 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5998 } else if (mimepart->type == MIMETYPE_TEXT) {
5999 if (!ainfo->name && compose->mode == COMPOSE_FORWARD_INLINE) {
6000 /* Text parts with no name come from multipart/alternative
6001 * forwards. Make sure the recipient won't look at the
6002 * original HTML part by mistake. */
6003 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6004 ainfo->name = g_strdup_printf(_("Original %s part"),
6008 g_hash_table_insert(mimepart->typeparameters,
6009 g_strdup("charset"), g_strdup(ainfo->charset));
6011 if (ainfo->name && mimepart->type != MIMETYPE_MESSAGE) {
6012 if (mimepart->type == MIMETYPE_APPLICATION &&
6013 !strcmp2(mimepart->subtype, "octet-stream"))
6014 g_hash_table_insert(mimepart->typeparameters,
6015 g_strdup("name"), g_strdup(ainfo->name));
6016 g_hash_table_insert(mimepart->dispositionparameters,
6017 g_strdup("filename"), g_strdup(ainfo->name));
6018 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6021 if (mimepart->type == MIMETYPE_MESSAGE
6022 || mimepart->type == MIMETYPE_MULTIPART)
6023 ainfo->encoding = ENC_BINARY;
6024 else if (compose->use_signing) {
6025 if (ainfo->encoding == ENC_7BIT)
6026 ainfo->encoding = ENC_QUOTED_PRINTABLE;
6027 else if (ainfo->encoding == ENC_8BIT)
6028 ainfo->encoding = ENC_BASE64;
6033 procmime_encode_content(mimepart, ainfo->encoding);
6035 g_node_append(parent->node, mimepart->node);
6036 } while (gtk_tree_model_iter_next(model, &iter));
6041 #define IS_IN_CUSTOM_HEADER(header) \
6042 (compose->account->add_customhdr && \
6043 custom_header_find(compose->account->customhdr_list, header) != NULL)
6045 static void compose_add_headerfield_from_headerlist(Compose *compose,
6047 const gchar *fieldname,
6048 const gchar *seperator)
6050 gchar *str, *fieldname_w_colon;
6051 gboolean add_field = FALSE;
6053 ComposeHeaderEntry *headerentry;
6054 const gchar *headerentryname;
6055 const gchar *trans_fieldname;
6058 if (IS_IN_CUSTOM_HEADER(fieldname))
6061 debug_print("Adding %s-fields\n", fieldname);
6063 fieldstr = g_string_sized_new(64);
6065 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6066 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6068 for (list = compose->header_list; list; list = list->next) {
6069 headerentry = ((ComposeHeaderEntry *)list->data);
6070 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6072 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6073 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6075 if (str[0] != '\0') {
6077 g_string_append(fieldstr, seperator);
6078 g_string_append(fieldstr, str);
6087 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6088 compose_convert_header
6089 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6090 strlen(fieldname) + 2, TRUE);
6091 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6095 g_free(fieldname_w_colon);
6096 g_string_free(fieldstr, TRUE);
6101 static gchar *compose_get_header(Compose *compose)
6103 gchar buf[BUFFSIZE];
6104 const gchar *entry_str;
6108 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6110 gchar *from_name = NULL, *from_address = NULL;
6113 cm_return_val_if_fail(compose->account != NULL, NULL);
6114 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6116 header = g_string_sized_new(64);
6119 get_rfc822_date(buf, sizeof(buf));
6120 g_string_append_printf(header, "Date: %s\n", buf);
6124 if (compose->account->name && *compose->account->name) {
6126 QUOTE_IF_REQUIRED(buf, compose->account->name);
6127 tmp = g_strdup_printf("%s <%s>",
6128 buf, compose->account->address);
6130 tmp = g_strdup_printf("%s",
6131 compose->account->address);
6133 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6134 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6136 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6137 from_address = g_strdup(compose->account->address);
6139 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6140 /* extract name and address */
6141 if (strstr(spec, " <") && strstr(spec, ">")) {
6142 from_address = g_strdup(strrchr(spec, '<')+1);
6143 *(strrchr(from_address, '>')) = '\0';
6144 from_name = g_strdup(spec);
6145 *(strrchr(from_name, '<')) = '\0';
6148 from_address = g_strdup(spec);
6155 if (from_name && *from_name) {
6156 compose_convert_header
6157 (compose, buf, sizeof(buf), from_name,
6158 strlen("From: "), TRUE);
6159 QUOTE_IF_REQUIRED(name, buf);
6161 g_string_append_printf(header, "From: %s <%s>\n",
6162 name, from_address);
6164 g_string_append_printf(header, "From: %s\n", from_address);
6167 g_free(from_address);
6170 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6173 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6176 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6180 * If this account is a NNTP account remove Bcc header from
6181 * message body since it otherwise will be publicly shown
6183 if (compose->account->protocol != A_NNTP)
6184 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6187 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6189 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6192 compose_convert_header(compose, buf, sizeof(buf), str,
6193 strlen("Subject: "), FALSE);
6194 g_string_append_printf(header, "Subject: %s\n", buf);
6200 if (compose->account->set_domain && compose->account->domain) {
6201 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6202 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6203 g_snprintf(buf, sizeof(buf), "%s",
6204 strchr(compose->account->address, '@') ?
6205 strchr(compose->account->address, '@')+1 :
6206 compose->account->address);
6208 g_snprintf(buf, sizeof(buf), "%s", "");
6211 if (compose->account->gen_msgid) {
6213 if (compose->account->msgid_with_addr) {
6214 addr = compose->account->address;
6216 generate_msgid(buf, sizeof(buf), addr);
6217 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6218 compose->msgid = g_strdup(buf);
6220 compose->msgid = NULL;
6223 if (compose->remove_references == FALSE) {
6225 if (compose->inreplyto && compose->to_list)
6226 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6229 if (compose->references)
6230 g_string_append_printf(header, "References: %s\n", compose->references);
6234 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6237 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6240 if (compose->account->organization &&
6241 strlen(compose->account->organization) &&
6242 !IS_IN_CUSTOM_HEADER("Organization")) {
6243 compose_convert_header(compose, buf, sizeof(buf),
6244 compose->account->organization,
6245 strlen("Organization: "), FALSE);
6246 g_string_append_printf(header, "Organization: %s\n", buf);
6249 /* Program version and system info */
6250 if (compose->account->gen_xmailer &&
6251 g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6252 !compose->newsgroup_list) {
6253 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6255 gtk_major_version, gtk_minor_version, gtk_micro_version,
6258 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6259 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6261 gtk_major_version, gtk_minor_version, gtk_micro_version,
6265 /* custom headers */
6266 if (compose->account->add_customhdr) {
6269 for (cur = compose->account->customhdr_list; cur != NULL;
6271 CustomHeader *chdr = (CustomHeader *)cur->data;
6273 if (custom_header_is_allowed(chdr->name)
6274 && chdr->value != NULL
6275 && *(chdr->value) != '\0') {
6276 compose_convert_header
6277 (compose, buf, sizeof(buf),
6279 strlen(chdr->name) + 2, FALSE);
6280 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6285 /* Automatic Faces and X-Faces */
6286 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6287 g_string_append_printf(header, "X-Face: %s\n", buf);
6289 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6290 g_string_append_printf(header, "X-Face: %s\n", buf);
6292 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6293 g_string_append_printf(header, "Face: %s\n", buf);
6295 else if (get_default_face (buf, sizeof(buf)) == 0) {
6296 g_string_append_printf(header, "Face: %s\n", buf);
6300 switch (compose->priority) {
6301 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6302 "X-Priority: 1 (Highest)\n");
6304 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6305 "X-Priority: 2 (High)\n");
6307 case PRIORITY_NORMAL: break;
6308 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6309 "X-Priority: 4 (Low)\n");
6311 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6312 "X-Priority: 5 (Lowest)\n");
6314 default: debug_print("compose: priority unknown : %d\n",
6318 /* Request Return Receipt */
6319 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6320 if (compose->return_receipt) {
6321 if (compose->account->name
6322 && *compose->account->name) {
6323 compose_convert_header(compose, buf, sizeof(buf),
6324 compose->account->name,
6325 strlen("Disposition-Notification-To: "),
6327 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6329 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6333 /* get special headers */
6334 for (list = compose->header_list; list; list = list->next) {
6335 ComposeHeaderEntry *headerentry;
6338 gchar *headername_wcolon;
6339 const gchar *headername_trans;
6342 gboolean standard_header = FALSE;
6344 headerentry = ((ComposeHeaderEntry *)list->data);
6346 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6348 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6353 if (!strstr(tmp, ":")) {
6354 headername_wcolon = g_strconcat(tmp, ":", NULL);
6355 headername = g_strdup(tmp);
6357 headername_wcolon = g_strdup(tmp);
6358 headername = g_strdup(strtok(tmp, ":"));
6362 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6363 Xstrdup_a(headervalue, entry_str, return NULL);
6364 subst_char(headervalue, '\r', ' ');
6365 subst_char(headervalue, '\n', ' ');
6366 string = std_headers;
6367 while (*string != NULL) {
6368 headername_trans = prefs_common_translated_header_name(*string);
6369 if (!strcmp(headername_trans, headername_wcolon))
6370 standard_header = TRUE;
6373 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6374 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6377 g_free(headername_wcolon);
6381 g_string_free(header, FALSE);
6386 #undef IS_IN_CUSTOM_HEADER
6388 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6389 gint header_len, gboolean addr_field)
6391 gchar *tmpstr = NULL;
6392 const gchar *out_codeset = NULL;
6394 cm_return_if_fail(src != NULL);
6395 cm_return_if_fail(dest != NULL);
6397 if (len < 1) return;
6399 tmpstr = g_strdup(src);
6401 subst_char(tmpstr, '\n', ' ');
6402 subst_char(tmpstr, '\r', ' ');
6405 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6406 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6407 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6412 codeconv_set_strict(TRUE);
6413 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6414 conv_get_charset_str(compose->out_encoding));
6415 codeconv_set_strict(FALSE);
6417 if (!dest || *dest == '\0') {
6418 gchar *test_conv_global_out = NULL;
6419 gchar *test_conv_reply = NULL;
6421 /* automatic mode. be automatic. */
6422 codeconv_set_strict(TRUE);
6424 out_codeset = conv_get_outgoing_charset_str();
6426 debug_print("trying to convert to %s\n", out_codeset);
6427 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6430 if (!test_conv_global_out && compose->orig_charset
6431 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6432 out_codeset = compose->orig_charset;
6433 debug_print("failure; trying to convert to %s\n", out_codeset);
6434 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6437 if (!test_conv_global_out && !test_conv_reply) {
6439 out_codeset = CS_INTERNAL;
6440 debug_print("finally using %s\n", out_codeset);
6442 g_free(test_conv_global_out);
6443 g_free(test_conv_reply);
6444 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6446 codeconv_set_strict(FALSE);
6451 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6455 cm_return_if_fail(user_data != NULL);
6457 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6458 g_strstrip(address);
6459 if (*address != '\0') {
6460 gchar *name = procheader_get_fromname(address);
6461 extract_address(address);
6462 addressbook_add_contact(name, address, NULL, NULL);
6467 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6469 GtkWidget *menuitem;
6472 cm_return_if_fail(menu != NULL);
6473 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6475 menuitem = gtk_separator_menu_item_new();
6476 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6477 gtk_widget_show(menuitem);
6479 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6480 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6482 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6483 g_strstrip(address);
6484 if (*address == '\0') {
6485 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6488 g_signal_connect(G_OBJECT(menuitem), "activate",
6489 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6490 gtk_widget_show(menuitem);
6493 static void compose_create_header_entry(Compose *compose)
6495 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6502 const gchar *header = NULL;
6503 ComposeHeaderEntry *headerentry;
6504 gboolean standard_header = FALSE;
6505 GtkListStore *model;
6507 #if !(GTK_CHECK_VERSION(2,12,0))
6508 GtkTooltips *tips = compose->tooltips;
6511 headerentry = g_new0(ComposeHeaderEntry, 1);
6514 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6515 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6516 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6518 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6520 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6522 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6523 COMPOSE_NEWSGROUPS);
6524 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6526 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6527 COMPOSE_FOLLOWUPTO);
6529 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6530 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6531 G_CALLBACK(compose_grab_focus_cb), compose);
6532 gtk_widget_show(combo);
6533 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6534 compose->header_nextrow, compose->header_nextrow+1,
6535 GTK_SHRINK, GTK_FILL, 0, 0);
6536 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6537 const gchar *last_header_entry = gtk_entry_get_text(
6538 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6540 while (*string != NULL) {
6541 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6542 standard_header = TRUE;
6545 if (standard_header)
6546 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6548 if (!compose->header_last || !standard_header) {
6549 switch(compose->account->protocol) {
6551 header = prefs_common_translated_header_name("Newsgroups:");
6554 header = prefs_common_translated_header_name("To:");
6559 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6561 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6562 G_CALLBACK(compose_grab_focus_cb), compose);
6564 /* Entry field with cleanup button */
6565 button = gtk_button_new();
6566 gtk_button_set_image(GTK_BUTTON(button),
6567 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6568 gtk_widget_show(button);
6569 CLAWS_SET_TIP(button,
6570 _("Delete entry contents"));
6571 entry = gtk_entry_new();
6572 gtk_widget_show(entry);
6573 CLAWS_SET_TIP(entry,
6574 _("Use <tab> to autocomplete from addressbook"));
6575 hbox = gtk_hbox_new (FALSE, 0);
6576 gtk_widget_show(hbox);
6577 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6578 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6579 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6580 compose->header_nextrow, compose->header_nextrow+1,
6581 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6583 g_signal_connect(G_OBJECT(entry), "key-press-event",
6584 G_CALLBACK(compose_headerentry_key_press_event_cb),
6586 g_signal_connect(G_OBJECT(entry), "changed",
6587 G_CALLBACK(compose_headerentry_changed_cb),
6589 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6590 G_CALLBACK(compose_grab_focus_cb), compose);
6592 g_signal_connect(G_OBJECT(button), "clicked",
6593 G_CALLBACK(compose_headerentry_button_clicked_cb),
6597 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6598 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6599 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6600 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6601 G_CALLBACK(compose_header_drag_received_cb),
6603 g_signal_connect(G_OBJECT(entry), "drag-drop",
6604 G_CALLBACK(compose_drag_drop),
6606 g_signal_connect(G_OBJECT(entry), "populate-popup",
6607 G_CALLBACK(compose_entry_popup_extend),
6610 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6612 headerentry->compose = compose;
6613 headerentry->combo = combo;
6614 headerentry->entry = entry;
6615 headerentry->button = button;
6616 headerentry->hbox = hbox;
6617 headerentry->headernum = compose->header_nextrow;
6618 headerentry->type = PREF_NONE;
6620 compose->header_nextrow++;
6621 compose->header_last = headerentry;
6622 compose->header_list =
6623 g_slist_append(compose->header_list,
6627 static void compose_add_header_entry(Compose *compose, const gchar *header,
6628 gchar *text, ComposePrefType pref_type)
6630 ComposeHeaderEntry *last_header = compose->header_last;
6631 gchar *tmp = g_strdup(text), *email;
6632 gboolean replyto_hdr;
6634 replyto_hdr = (!strcasecmp(header,
6635 prefs_common_translated_header_name("Reply-To:")) ||
6637 prefs_common_translated_header_name("Followup-To:")) ||
6639 prefs_common_translated_header_name("In-Reply-To:")));
6641 extract_address(tmp);
6642 email = g_utf8_strdown(tmp, -1);
6644 if (replyto_hdr == FALSE &&
6645 g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6647 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6648 header, text, (gint) pref_type);
6654 if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6655 gtk_entry_set_text(GTK_ENTRY(
6656 gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6658 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6659 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6660 last_header->type = pref_type;
6662 if (replyto_hdr == FALSE)
6663 g_hash_table_insert(compose->email_hashtable, email,
6664 GUINT_TO_POINTER(1));
6671 static void compose_destroy_headerentry(Compose *compose,
6672 ComposeHeaderEntry *headerentry)
6674 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6677 extract_address(text);
6678 email = g_utf8_strdown(text, -1);
6679 g_hash_table_remove(compose->email_hashtable, email);
6683 gtk_widget_destroy(headerentry->combo);
6684 gtk_widget_destroy(headerentry->entry);
6685 gtk_widget_destroy(headerentry->button);
6686 gtk_widget_destroy(headerentry->hbox);
6687 g_free(headerentry);
6690 static void compose_remove_header_entries(Compose *compose)
6693 for (list = compose->header_list; list; list = list->next)
6694 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6696 compose->header_last = NULL;
6697 g_slist_free(compose->header_list);
6698 compose->header_list = NULL;
6699 compose->header_nextrow = 1;
6700 compose_create_header_entry(compose);
6703 static GtkWidget *compose_create_header(Compose *compose)
6705 GtkWidget *from_optmenu_hbox;
6706 GtkWidget *header_scrolledwin;
6707 GtkWidget *header_table;
6711 /* header labels and entries */
6712 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6713 gtk_widget_show(header_scrolledwin);
6714 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6716 header_table = gtk_table_new(2, 2, FALSE);
6717 gtk_widget_show(header_table);
6718 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6719 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6720 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6723 /* option menu for selecting accounts */
6724 from_optmenu_hbox = compose_account_option_menu_create(compose);
6725 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6726 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6729 compose->header_table = header_table;
6730 compose->header_list = NULL;
6731 compose->header_nextrow = count;
6733 compose_create_header_entry(compose);
6735 compose->table = NULL;
6737 return header_scrolledwin ;
6740 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6742 Compose *compose = (Compose *)data;
6743 GdkEventButton event;
6746 event.time = gtk_get_current_event_time();
6748 return attach_button_pressed(compose->attach_clist, &event, compose);
6751 static GtkWidget *compose_create_attach(Compose *compose)
6753 GtkWidget *attach_scrwin;
6754 GtkWidget *attach_clist;
6756 GtkListStore *store;
6757 GtkCellRenderer *renderer;
6758 GtkTreeViewColumn *column;
6759 GtkTreeSelection *selection;
6761 /* attachment list */
6762 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6763 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6764 GTK_POLICY_AUTOMATIC,
6765 GTK_POLICY_AUTOMATIC);
6766 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6768 store = gtk_list_store_new(N_ATTACH_COLS,
6774 G_TYPE_AUTO_POINTER,
6776 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6777 (GTK_TREE_MODEL(store)));
6778 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6779 g_object_unref(store);
6781 renderer = gtk_cell_renderer_text_new();
6782 column = gtk_tree_view_column_new_with_attributes
6783 (_("Mime type"), renderer, "text",
6784 COL_MIMETYPE, NULL);
6785 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6787 renderer = gtk_cell_renderer_text_new();
6788 column = gtk_tree_view_column_new_with_attributes
6789 (_("Size"), renderer, "text",
6791 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6793 renderer = gtk_cell_renderer_text_new();
6794 column = gtk_tree_view_column_new_with_attributes
6795 (_("Name"), renderer, "text",
6797 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6799 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6800 prefs_common.use_stripes_everywhere);
6801 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6802 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6804 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6805 G_CALLBACK(attach_selected), compose);
6806 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6807 G_CALLBACK(attach_button_pressed), compose);
6809 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6810 G_CALLBACK(popup_attach_button_pressed), compose);
6812 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6813 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6814 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6815 G_CALLBACK(popup_attach_button_pressed), compose);
6817 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6818 G_CALLBACK(attach_key_pressed), compose);
6821 gtk_drag_dest_set(attach_clist,
6822 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6823 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6824 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6825 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6826 G_CALLBACK(compose_attach_drag_received_cb),
6828 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6829 G_CALLBACK(compose_drag_drop),
6832 compose->attach_scrwin = attach_scrwin;
6833 compose->attach_clist = attach_clist;
6835 return attach_scrwin;
6838 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6839 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6841 static GtkWidget *compose_create_others(Compose *compose)
6844 GtkWidget *savemsg_checkbtn;
6845 GtkWidget *savemsg_combo;
6846 GtkWidget *savemsg_select;
6849 gchar *folderidentifier;
6851 /* Table for settings */
6852 table = gtk_table_new(3, 1, FALSE);
6853 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6854 gtk_widget_show(table);
6855 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6858 /* Save Message to folder */
6859 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6860 gtk_widget_show(savemsg_checkbtn);
6861 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6862 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6863 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6865 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6866 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6868 savemsg_combo = gtk_combo_box_entry_new_text();
6869 compose->savemsg_checkbtn = savemsg_checkbtn;
6870 compose->savemsg_combo = savemsg_combo;
6871 gtk_widget_show(savemsg_combo);
6873 if (prefs_common.compose_save_to_history)
6874 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6875 prefs_common.compose_save_to_history);
6877 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6878 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6879 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6880 G_CALLBACK(compose_grab_focus_cb), compose);
6881 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6882 folderidentifier = folder_item_get_identifier(account_get_special_folder
6883 (compose->account, F_OUTBOX));
6884 compose_set_save_to(compose, folderidentifier);
6885 g_free(folderidentifier);
6888 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6889 gtk_widget_show(savemsg_select);
6890 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6891 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6892 G_CALLBACK(compose_savemsg_select_cb),
6900 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6902 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6903 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6906 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6911 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6914 path = folder_item_get_identifier(dest);
6916 compose_set_save_to(compose, path);
6920 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6921 GdkAtom clip, GtkTextIter *insert_place);
6924 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6928 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
6930 if (event->button == 3) {
6932 GtkTextIter sel_start, sel_end;
6933 gboolean stuff_selected;
6935 /* move the cursor to allow GtkAspell to check the word
6936 * under the mouse */
6937 if (event->x && event->y) {
6938 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6939 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6941 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6944 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6945 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6948 stuff_selected = gtk_text_buffer_get_selection_bounds(
6950 &sel_start, &sel_end);
6952 gtk_text_buffer_place_cursor (buffer, &iter);
6953 /* reselect stuff */
6955 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6956 gtk_text_buffer_select_range(buffer,
6957 &sel_start, &sel_end);
6959 return FALSE; /* pass the event so that the right-click goes through */
6962 if (event->button == 2) {
6967 /* get the middle-click position to paste at the correct place */
6968 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6969 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6971 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6974 entry_paste_clipboard(compose, text,
6975 prefs_common.linewrap_pastes,
6976 GDK_SELECTION_PRIMARY, &iter);
6984 static void compose_spell_menu_changed(void *data)
6986 Compose *compose = (Compose *)data;
6988 GtkWidget *menuitem;
6989 GtkWidget *parent_item;
6990 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6993 if (compose->gtkaspell == NULL)
6996 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6997 "/Menu/Spelling/Options");
6999 /* setting the submenu removes /Spelling/Options from the factory
7000 * so we need to save it */
7002 if (parent_item == NULL) {
7003 parent_item = compose->aspell_options_menu;
7004 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
7006 compose->aspell_options_menu = parent_item;
7008 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
7010 spell_menu = g_slist_reverse(spell_menu);
7011 for (items = spell_menu;
7012 items; items = items->next) {
7013 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
7014 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
7015 gtk_widget_show(GTK_WIDGET(menuitem));
7017 g_slist_free(spell_menu);
7019 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7020 gtk_widget_show(parent_item);
7023 static void compose_dict_changed(void *data)
7025 Compose *compose = (Compose *) data;
7027 if(compose->gtkaspell &&
7028 compose->gtkaspell->recheck_when_changing_dict == FALSE)
7031 gtkaspell_highlight_all(compose->gtkaspell);
7032 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7036 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7038 Compose *compose = (Compose *)data;
7039 GdkEventButton event;
7042 event.time = gtk_get_current_event_time();
7046 return text_clicked(compose->text, &event, compose);
7049 static gboolean compose_force_window_origin = TRUE;
7050 static Compose *compose_create(PrefsAccount *account,
7059 GtkWidget *handlebox;
7061 GtkWidget *notebook;
7063 GtkWidget *attach_hbox;
7064 GtkWidget *attach_lab1;
7065 GtkWidget *attach_lab2;
7070 GtkWidget *subject_hbox;
7071 GtkWidget *subject_frame;
7072 GtkWidget *subject_entry;
7076 GtkWidget *edit_vbox;
7077 GtkWidget *ruler_hbox;
7079 GtkWidget *scrolledwin;
7081 GtkTextBuffer *buffer;
7082 GtkClipboard *clipboard;
7084 UndoMain *undostruct;
7086 gchar *titles[N_ATTACH_COLS];
7087 GtkWidget *popupmenu;
7088 GtkWidget *tmpl_menu;
7089 GtkActionGroup *action_group = NULL;
7092 GtkAspell * gtkaspell = NULL;
7095 static GdkGeometry geometry;
7097 cm_return_val_if_fail(account != NULL, NULL);
7099 debug_print("Creating compose window...\n");
7100 compose = g_new0(Compose, 1);
7102 titles[COL_MIMETYPE] = _("MIME type");
7103 titles[COL_SIZE] = _("Size");
7104 titles[COL_NAME] = _("Name");
7105 titles[COL_CHARSET] = _("Charset");
7107 compose->batch = batch;
7108 compose->account = account;
7109 compose->folder = folder;
7111 compose->mutex = g_mutex_new();
7112 compose->set_cursor_pos = -1;
7114 #if !(GTK_CHECK_VERSION(2,12,0))
7115 compose->tooltips = tips;
7118 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7120 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7121 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
7123 if (!geometry.max_width) {
7124 geometry.max_width = gdk_screen_width();
7125 geometry.max_height = gdk_screen_height();
7128 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7129 &geometry, GDK_HINT_MAX_SIZE);
7130 if (!geometry.min_width) {
7131 geometry.min_width = 600;
7132 geometry.min_height = 440;
7134 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7135 &geometry, GDK_HINT_MIN_SIZE);
7137 #ifndef GENERIC_UMPC
7138 if (compose_force_window_origin)
7139 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7140 prefs_common.compose_y);
7142 g_signal_connect(G_OBJECT(window), "delete_event",
7143 G_CALLBACK(compose_delete_cb), compose);
7144 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7145 gtk_widget_realize(window);
7147 gtkut_widget_set_composer_icon(window);
7149 vbox = gtk_vbox_new(FALSE, 0);
7150 gtk_container_add(GTK_CONTAINER(window), vbox);
7152 compose->ui_manager = gtk_ui_manager_new();
7153 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7154 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7155 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7156 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7157 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7158 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7159 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7160 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7161 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7162 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7165 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7167 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7170 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7171 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7173 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7175 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7176 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7177 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7180 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7181 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7182 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7183 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7184 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7185 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7186 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7187 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7188 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7189 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7192 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7193 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7194 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7196 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7197 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7198 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7200 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7201 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7202 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7203 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7205 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7207 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7208 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7209 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7210 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7211 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7212 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7213 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7214 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7215 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7216 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7217 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7218 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7219 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7220 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7221 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7223 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7225 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7226 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7227 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7228 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7229 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7231 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7233 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7237 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7238 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7239 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7240 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7241 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7242 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7246 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7247 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7248 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7249 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7250 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7252 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7253 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7254 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7255 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7256 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7259 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7260 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7261 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7262 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7263 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7264 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7265 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7267 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7268 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7269 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7270 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7271 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7273 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7275 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7276 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7277 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7278 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7279 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7281 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7282 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_ISO_8859_1, "Options/Encoding/Western/"CS_ISO_8859_1, GTK_UI_MANAGER_MENUITEM)
7283 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_ISO_8859_15, "Options/Encoding/Western/"CS_ISO_8859_15, GTK_UI_MANAGER_MENUITEM)
7284 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7286 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7288 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7289 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Baltic", CS_ISO_8859_13, "Options/Encoding/Baltic/"CS_ISO_8859_13, GTK_UI_MANAGER_MENUITEM)
7290 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Baltic", CS_ISO_8859_4, "Options/Encoding/Baltic/"CS_ISO_8859_4, GTK_UI_MANAGER_MENUITEM)
7292 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7294 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7295 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_ISO_8859_8, "Options/Encoding/Hebrew/"CS_ISO_8859_8, GTK_UI_MANAGER_MENUITEM)
7296 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7298 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7299 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_ISO_8859_6, "Options/Encoding/Arabic/"CS_ISO_8859_6, GTK_UI_MANAGER_MENUITEM)
7300 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7302 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7304 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7305 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_ISO_8859_5, "Options/Encoding/Cyrillic/"CS_ISO_8859_5, GTK_UI_MANAGER_MENUITEM)
7306 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7307 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7308 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7310 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7311 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_ISO_2022_JP, "Options/Encoding/Japanese/"CS_ISO_2022_JP, GTK_UI_MANAGER_MENUITEM)
7312 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_ISO_2022_JP_2, "Options/Encoding/Japanese/"CS_ISO_2022_JP_2, GTK_UI_MANAGER_MENUITEM)
7313 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7314 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7316 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7317 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7318 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7319 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7320 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7321 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7323 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7324 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7325 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_ISO_2022_KR, "Options/Encoding/Korean/"CS_ISO_2022_KR, GTK_UI_MANAGER_MENUITEM)
7327 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7328 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7329 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7333 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7334 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7335 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7336 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7337 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7338 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7341 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7343 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7344 gtk_widget_show_all(menubar);
7346 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7348 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7350 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7353 if (prefs_common.toolbar_detachable) {
7354 handlebox = gtk_handle_box_new();
7356 handlebox = gtk_hbox_new(FALSE, 0);
7358 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7360 gtk_widget_realize(handlebox);
7362 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7365 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7369 vbox2 = gtk_vbox_new(FALSE, 2);
7370 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7371 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7374 notebook = gtk_notebook_new();
7375 gtk_widget_set_size_request(notebook, -1, 130);
7376 gtk_widget_show(notebook);
7378 /* header labels and entries */
7379 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7380 compose_create_header(compose),
7381 gtk_label_new_with_mnemonic(_("Hea_der")));
7382 /* attachment list */
7383 attach_hbox = gtk_hbox_new(FALSE, 0);
7384 gtk_widget_show(attach_hbox);
7386 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7387 gtk_widget_show(attach_lab1);
7388 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7390 attach_lab2 = gtk_label_new("");
7391 gtk_widget_show(attach_lab2);
7392 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7394 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7395 compose_create_attach(compose),
7398 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7399 compose_create_others(compose),
7400 gtk_label_new_with_mnemonic(_("Othe_rs")));
7403 subject_hbox = gtk_hbox_new(FALSE, 0);
7404 gtk_widget_show(subject_hbox);
7406 subject_frame = gtk_frame_new(NULL);
7407 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7408 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7409 gtk_widget_show(subject_frame);
7411 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7412 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7413 gtk_widget_show(subject);
7415 label = gtk_label_new(_("Subject:"));
7416 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7417 gtk_widget_show(label);
7420 subject_entry = claws_spell_entry_new();
7422 subject_entry = gtk_entry_new();
7424 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7425 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7426 G_CALLBACK(compose_grab_focus_cb), compose);
7427 gtk_widget_show(subject_entry);
7428 compose->subject_entry = subject_entry;
7429 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7431 edit_vbox = gtk_vbox_new(FALSE, 0);
7433 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7436 ruler_hbox = gtk_hbox_new(FALSE, 0);
7437 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7439 ruler = gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL);
7440 gtk_shruler_set_range(GTK_SHRULER(ruler), 0.0, 100.0, 1.0);
7441 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7445 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7446 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7447 GTK_POLICY_AUTOMATIC,
7448 GTK_POLICY_AUTOMATIC);
7449 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7451 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7452 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7454 text = gtk_text_view_new();
7455 if (prefs_common.show_compose_margin) {
7456 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7457 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7459 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7460 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7461 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7462 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7463 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7465 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7466 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7467 G_CALLBACK(compose_edit_size_alloc),
7469 g_signal_connect(G_OBJECT(buffer), "changed",
7470 G_CALLBACK(compose_changed_cb), compose);
7471 g_signal_connect(G_OBJECT(text), "grab_focus",
7472 G_CALLBACK(compose_grab_focus_cb), compose);
7473 g_signal_connect(G_OBJECT(buffer), "insert_text",
7474 G_CALLBACK(text_inserted), compose);
7475 g_signal_connect(G_OBJECT(text), "button_press_event",
7476 G_CALLBACK(text_clicked), compose);
7478 g_signal_connect(G_OBJECT(text), "popup-menu",
7479 G_CALLBACK(compose_popup_menu), compose);
7481 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7482 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7483 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7484 G_CALLBACK(compose_popup_menu), compose);
7486 g_signal_connect(G_OBJECT(subject_entry), "changed",
7487 G_CALLBACK(compose_changed_cb), compose);
7490 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7491 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7492 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7493 g_signal_connect(G_OBJECT(text), "drag_data_received",
7494 G_CALLBACK(compose_insert_drag_received_cb),
7496 g_signal_connect(G_OBJECT(text), "drag-drop",
7497 G_CALLBACK(compose_drag_drop),
7499 gtk_widget_show_all(vbox);
7501 /* pane between attach clist and text */
7502 paned = gtk_vpaned_new();
7503 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7505 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7506 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7508 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7510 gtk_paned_add1(GTK_PANED(paned), notebook);
7511 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7512 gtk_widget_show_all(paned);
7515 if (prefs_common.textfont) {
7516 PangoFontDescription *font_desc;
7518 font_desc = pango_font_description_from_string
7519 (prefs_common.textfont);
7521 gtk_widget_modify_font(text, font_desc);
7522 pango_font_description_free(font_desc);
7526 gtk_action_group_add_actions(action_group, compose_popup_entries,
7527 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7528 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7529 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7530 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7531 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7532 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7533 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7535 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7537 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7538 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7539 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7541 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7543 undostruct = undo_init(text);
7544 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7547 address_completion_start(window);
7549 compose->window = window;
7550 compose->vbox = vbox;
7551 compose->menubar = menubar;
7552 compose->handlebox = handlebox;
7554 compose->vbox2 = vbox2;
7556 compose->paned = paned;
7558 compose->attach_label = attach_lab2;
7560 compose->notebook = notebook;
7561 compose->edit_vbox = edit_vbox;
7562 compose->ruler_hbox = ruler_hbox;
7563 compose->ruler = ruler;
7564 compose->scrolledwin = scrolledwin;
7565 compose->text = text;
7567 compose->focused_editable = NULL;
7569 compose->popupmenu = popupmenu;
7571 compose->tmpl_menu = tmpl_menu;
7573 compose->mode = mode;
7574 compose->rmode = mode;
7576 compose->targetinfo = NULL;
7577 compose->replyinfo = NULL;
7578 compose->fwdinfo = NULL;
7580 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7581 g_str_equal, (GDestroyNotify) g_free, NULL);
7583 compose->replyto = NULL;
7585 compose->bcc = NULL;
7586 compose->followup_to = NULL;
7588 compose->ml_post = NULL;
7590 compose->inreplyto = NULL;
7591 compose->references = NULL;
7592 compose->msgid = NULL;
7593 compose->boundary = NULL;
7595 compose->autowrap = prefs_common.autowrap;
7596 compose->autoindent = prefs_common.auto_indent;
7597 compose->use_signing = FALSE;
7598 compose->use_encryption = FALSE;
7599 compose->privacy_system = NULL;
7601 compose->modified = FALSE;
7603 compose->return_receipt = FALSE;
7605 compose->to_list = NULL;
7606 compose->newsgroup_list = NULL;
7608 compose->undostruct = undostruct;
7610 compose->sig_str = NULL;
7612 compose->exteditor_file = NULL;
7613 compose->exteditor_pid = -1;
7614 compose->exteditor_tag = -1;
7615 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7618 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7619 if (mode != COMPOSE_REDIRECT) {
7620 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7621 strcmp(prefs_common.dictionary, "")) {
7622 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7623 prefs_common.alt_dictionary,
7624 conv_get_locale_charset_str(),
7625 prefs_common.misspelled_col,
7626 prefs_common.check_while_typing,
7627 prefs_common.recheck_when_changing_dict,
7628 prefs_common.use_alternate,
7629 prefs_common.use_both_dicts,
7630 GTK_TEXT_VIEW(text),
7631 GTK_WINDOW(compose->window),
7632 compose_dict_changed,
7633 compose_spell_menu_changed,
7636 alertpanel_error(_("Spell checker could not "
7638 gtkaspell_checkers_strerror());
7639 gtkaspell_checkers_reset_error();
7641 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7645 compose->gtkaspell = gtkaspell;
7646 compose_spell_menu_changed(compose);
7647 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7650 compose_select_account(compose, account, TRUE);
7652 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7653 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7655 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7656 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7658 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7659 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7661 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7662 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7664 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7665 if (account->protocol != A_NNTP)
7666 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7667 prefs_common_translated_header_name("To:"));
7669 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7670 prefs_common_translated_header_name("Newsgroups:"));
7672 addressbook_set_target_compose(compose);
7674 if (mode != COMPOSE_REDIRECT)
7675 compose_set_template_menu(compose);
7677 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7680 compose_list = g_list_append(compose_list, compose);
7682 if (!prefs_common.show_ruler)
7683 gtk_widget_hide(ruler_hbox);
7685 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7688 compose->priority = PRIORITY_NORMAL;
7689 compose_update_priority_menu_item(compose);
7691 compose_set_out_encoding(compose);
7694 compose_update_actions_menu(compose);
7696 /* Privacy Systems menu */
7697 compose_update_privacy_systems_menu(compose);
7699 activate_privacy_system(compose, account, TRUE);
7700 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7702 gtk_widget_realize(window);
7704 gtk_widget_show(window);
7706 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7707 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7714 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7719 GtkWidget *optmenubox;
7722 GtkWidget *from_name = NULL;
7723 #if !(GTK_CHECK_VERSION(2,12,0))
7724 GtkTooltips *tips = compose->tooltips;
7727 gint num = 0, def_menu = 0;
7729 accounts = account_get_list();
7730 cm_return_val_if_fail(accounts != NULL, NULL);
7732 optmenubox = gtk_event_box_new();
7733 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7734 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7736 hbox = gtk_hbox_new(FALSE, 6);
7737 from_name = gtk_entry_new();
7739 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7740 G_CALLBACK(compose_grab_focus_cb), compose);
7742 for (; accounts != NULL; accounts = accounts->next, num++) {
7743 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7744 gchar *name, *from = NULL;
7746 if (ac == compose->account) def_menu = num;
7748 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7751 if (ac == compose->account) {
7752 if (ac->name && *ac->name) {
7754 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7755 from = g_strdup_printf("%s <%s>",
7757 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7759 from = g_strdup_printf("%s",
7761 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7764 COMBOBOX_ADD(menu, name, ac->account_id);
7769 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7771 g_signal_connect(G_OBJECT(optmenu), "changed",
7772 G_CALLBACK(account_activated),
7774 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7775 G_CALLBACK(compose_entry_popup_extend),
7778 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7779 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7781 CLAWS_SET_TIP(optmenubox,
7782 _("Account to use for this email"));
7783 CLAWS_SET_TIP(from_name,
7784 _("Sender address to be used"));
7786 compose->account_combo = optmenu;
7787 compose->from_name = from_name;
7792 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7794 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7795 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7796 Compose *compose = (Compose *) data;
7798 compose->priority = value;
7802 static void compose_reply_change_mode(Compose *compose,
7805 gboolean was_modified = compose->modified;
7807 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7809 cm_return_if_fail(compose->replyinfo != NULL);
7811 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7813 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7815 if (action == COMPOSE_REPLY_TO_ALL)
7817 if (action == COMPOSE_REPLY_TO_SENDER)
7819 if (action == COMPOSE_REPLY_TO_LIST)
7822 compose_remove_header_entries(compose);
7823 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7824 if (compose->account->set_autocc && compose->account->auto_cc)
7825 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7827 if (compose->account->set_autobcc && compose->account->auto_bcc)
7828 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7830 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7831 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7832 compose_show_first_last_header(compose, TRUE);
7833 compose->modified = was_modified;
7834 compose_set_title(compose);
7837 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7839 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7840 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7841 Compose *compose = (Compose *) data;
7844 compose_reply_change_mode(compose, value);
7847 static void compose_update_priority_menu_item(Compose * compose)
7849 GtkWidget *menuitem = NULL;
7850 switch (compose->priority) {
7851 case PRIORITY_HIGHEST:
7852 menuitem = gtk_ui_manager_get_widget
7853 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7856 menuitem = gtk_ui_manager_get_widget
7857 (compose->ui_manager, "/Menu/Options/Priority/High");
7859 case PRIORITY_NORMAL:
7860 menuitem = gtk_ui_manager_get_widget
7861 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7864 menuitem = gtk_ui_manager_get_widget
7865 (compose->ui_manager, "/Menu/Options/Priority/Low");
7867 case PRIORITY_LOWEST:
7868 menuitem = gtk_ui_manager_get_widget
7869 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7872 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7875 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7877 Compose *compose = (Compose *) data;
7879 gboolean can_sign = FALSE, can_encrypt = FALSE;
7881 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7883 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
7886 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7887 g_free(compose->privacy_system);
7888 compose->privacy_system = NULL;
7889 if (systemid != NULL) {
7890 compose->privacy_system = g_strdup(systemid);
7892 can_sign = privacy_system_can_sign(systemid);
7893 can_encrypt = privacy_system_can_encrypt(systemid);
7896 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7898 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7899 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7902 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7904 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7905 GtkWidget *menuitem = NULL;
7906 GList *children, *amenu;
7907 gboolean can_sign = FALSE, can_encrypt = FALSE;
7908 gboolean found = FALSE;
7910 if (compose->privacy_system != NULL) {
7912 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7913 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7914 cm_return_if_fail(menuitem != NULL);
7916 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
7919 while (amenu != NULL) {
7920 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7921 if (systemid != NULL) {
7922 if (strcmp(systemid, compose->privacy_system) == 0 &&
7923 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7924 menuitem = GTK_WIDGET(amenu->data);
7926 can_sign = privacy_system_can_sign(systemid);
7927 can_encrypt = privacy_system_can_encrypt(systemid);
7931 } else if (strlen(compose->privacy_system) == 0 &&
7932 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7933 menuitem = GTK_WIDGET(amenu->data);
7936 can_encrypt = FALSE;
7941 amenu = amenu->next;
7943 g_list_free(children);
7944 if (menuitem != NULL)
7945 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7947 if (warn && !found && strlen(compose->privacy_system)) {
7948 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7949 "will not be able to sign or encrypt this message."),
7950 compose->privacy_system);
7954 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7955 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7958 static void compose_set_out_encoding(Compose *compose)
7960 CharSet out_encoding;
7961 const gchar *branch = NULL;
7962 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7964 switch(out_encoding) {
7965 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7966 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7967 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7968 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7969 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7970 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7971 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7972 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7973 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7974 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7975 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7976 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7977 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7978 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7979 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7980 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7981 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7982 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7983 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7984 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7985 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7986 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7987 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7988 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
7989 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7990 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7991 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7992 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7993 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7994 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7995 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7996 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7997 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7999 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8002 static void compose_set_template_menu(Compose *compose)
8004 GSList *tmpl_list, *cur;
8008 tmpl_list = template_get_config();
8010 menu = gtk_menu_new();
8012 gtk_menu_set_accel_group (GTK_MENU (menu),
8013 gtk_ui_manager_get_accel_group(compose->ui_manager));
8014 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8015 Template *tmpl = (Template *)cur->data;
8016 gchar *accel_path = NULL;
8017 item = gtk_menu_item_new_with_label(tmpl->name);
8018 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8019 g_signal_connect(G_OBJECT(item), "activate",
8020 G_CALLBACK(compose_template_activate_cb),
8022 g_object_set_data(G_OBJECT(item), "template", tmpl);
8023 gtk_widget_show(item);
8024 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8025 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8029 gtk_widget_show(menu);
8030 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8033 void compose_update_actions_menu(Compose *compose)
8035 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8038 static void compose_update_privacy_systems_menu(Compose *compose)
8040 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8041 GSList *systems, *cur;
8043 GtkWidget *system_none;
8045 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8046 GtkWidget *privacy_menu = gtk_menu_new();
8048 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8049 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8051 g_signal_connect(G_OBJECT(system_none), "activate",
8052 G_CALLBACK(compose_set_privacy_system_cb), compose);
8054 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8055 gtk_widget_show(system_none);
8057 systems = privacy_get_system_ids();
8058 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8059 gchar *systemid = cur->data;
8061 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8062 widget = gtk_radio_menu_item_new_with_label(group,
8063 privacy_system_get_name(systemid));
8064 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8065 g_strdup(systemid), g_free);
8066 g_signal_connect(G_OBJECT(widget), "activate",
8067 G_CALLBACK(compose_set_privacy_system_cb), compose);
8069 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8070 gtk_widget_show(widget);
8073 g_slist_free(systems);
8074 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8075 gtk_widget_show_all(privacy_menu);
8076 gtk_widget_show_all(privacy_menuitem);
8079 void compose_reflect_prefs_all(void)
8084 for (cur = compose_list; cur != NULL; cur = cur->next) {
8085 compose = (Compose *)cur->data;
8086 compose_set_template_menu(compose);
8090 void compose_reflect_prefs_pixmap_theme(void)
8095 for (cur = compose_list; cur != NULL; cur = cur->next) {
8096 compose = (Compose *)cur->data;
8097 toolbar_update(TOOLBAR_COMPOSE, compose);
8101 static const gchar *compose_quote_char_from_context(Compose *compose)
8103 const gchar *qmark = NULL;
8105 cm_return_val_if_fail(compose != NULL, NULL);
8107 switch (compose->mode) {
8108 /* use forward-specific quote char */
8109 case COMPOSE_FORWARD:
8110 case COMPOSE_FORWARD_AS_ATTACH:
8111 case COMPOSE_FORWARD_INLINE:
8112 if (compose->folder && compose->folder->prefs &&
8113 compose->folder->prefs->forward_with_format)
8114 qmark = compose->folder->prefs->forward_quotemark;
8115 else if (compose->account->forward_with_format)
8116 qmark = compose->account->forward_quotemark;
8118 qmark = prefs_common.fw_quotemark;
8121 /* use reply-specific quote char in all other modes */
8123 if (compose->folder && compose->folder->prefs &&
8124 compose->folder->prefs->reply_with_format)
8125 qmark = compose->folder->prefs->reply_quotemark;
8126 else if (compose->account->reply_with_format)
8127 qmark = compose->account->reply_quotemark;
8129 qmark = prefs_common.quotemark;
8133 if (qmark == NULL || *qmark == '\0')
8139 static void compose_template_apply(Compose *compose, Template *tmpl,
8143 GtkTextBuffer *buffer;
8147 gchar *parsed_str = NULL;
8148 gint cursor_pos = 0;
8149 const gchar *err_msg = _("The body of the template has an error at line %d.");
8152 /* process the body */
8154 text = GTK_TEXT_VIEW(compose->text);
8155 buffer = gtk_text_view_get_buffer(text);
8158 qmark = compose_quote_char_from_context(compose);
8160 if (compose->replyinfo != NULL) {
8163 gtk_text_buffer_set_text(buffer, "", -1);
8164 mark = gtk_text_buffer_get_insert(buffer);
8165 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8167 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8168 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8170 } else if (compose->fwdinfo != NULL) {
8173 gtk_text_buffer_set_text(buffer, "", -1);
8174 mark = gtk_text_buffer_get_insert(buffer);
8175 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8177 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8178 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8181 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8183 GtkTextIter start, end;
8186 gtk_text_buffer_get_start_iter(buffer, &start);
8187 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8188 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8190 /* clear the buffer now */
8192 gtk_text_buffer_set_text(buffer, "", -1);
8194 parsed_str = compose_quote_fmt(compose, dummyinfo,
8195 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8196 procmsg_msginfo_free( dummyinfo );
8202 gtk_text_buffer_set_text(buffer, "", -1);
8203 mark = gtk_text_buffer_get_insert(buffer);
8204 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8207 if (replace && parsed_str && compose->account->auto_sig)
8208 compose_insert_sig(compose, FALSE);
8210 if (replace && parsed_str) {
8211 gtk_text_buffer_get_start_iter(buffer, &iter);
8212 gtk_text_buffer_place_cursor(buffer, &iter);
8216 cursor_pos = quote_fmt_get_cursor_pos();
8217 compose->set_cursor_pos = cursor_pos;
8218 if (cursor_pos == -1)
8220 gtk_text_buffer_get_start_iter(buffer, &iter);
8221 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8222 gtk_text_buffer_place_cursor(buffer, &iter);
8225 /* process the other fields */
8227 compose_template_apply_fields(compose, tmpl);
8228 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8229 quote_fmt_reset_vartable();
8230 compose_changed_cb(NULL, compose);
8233 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8234 gtkaspell_highlight_all(compose->gtkaspell);
8238 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8240 MsgInfo* dummyinfo = NULL;
8241 MsgInfo *msginfo = NULL;
8244 if (compose->replyinfo != NULL)
8245 msginfo = compose->replyinfo;
8246 else if (compose->fwdinfo != NULL)
8247 msginfo = compose->fwdinfo;
8249 dummyinfo = compose_msginfo_new_from_compose(compose);
8250 msginfo = dummyinfo;
8253 if (tmpl->from && *tmpl->from != '\0') {
8255 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8256 compose->gtkaspell);
8258 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8260 quote_fmt_scan_string(tmpl->from);
8263 buf = quote_fmt_get_buffer();
8265 alertpanel_error(_("Template From format error."));
8267 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8271 if (tmpl->to && *tmpl->to != '\0') {
8273 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8274 compose->gtkaspell);
8276 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8278 quote_fmt_scan_string(tmpl->to);
8281 buf = quote_fmt_get_buffer();
8283 alertpanel_error(_("Template To format error."));
8285 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8289 if (tmpl->cc && *tmpl->cc != '\0') {
8291 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8292 compose->gtkaspell);
8294 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8296 quote_fmt_scan_string(tmpl->cc);
8299 buf = quote_fmt_get_buffer();
8301 alertpanel_error(_("Template Cc format error."));
8303 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8307 if (tmpl->bcc && *tmpl->bcc != '\0') {
8309 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8310 compose->gtkaspell);
8312 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8314 quote_fmt_scan_string(tmpl->bcc);
8317 buf = quote_fmt_get_buffer();
8319 alertpanel_error(_("Template Bcc format error."));
8321 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8325 /* process the subject */
8326 if (tmpl->subject && *tmpl->subject != '\0') {
8328 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8329 compose->gtkaspell);
8331 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8333 quote_fmt_scan_string(tmpl->subject);
8336 buf = quote_fmt_get_buffer();
8338 alertpanel_error(_("Template subject format error."));
8340 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8344 procmsg_msginfo_free( dummyinfo );
8347 static void compose_destroy(Compose *compose)
8349 GtkAllocation allocation;
8350 GtkTextBuffer *buffer;
8351 GtkClipboard *clipboard;
8353 compose_list = g_list_remove(compose_list, compose);
8355 if (compose->updating) {
8356 debug_print("danger, not destroying anything now\n");
8357 compose->deferred_destroy = TRUE;
8360 /* NOTE: address_completion_end() does nothing with the window
8361 * however this may change. */
8362 address_completion_end(compose->window);
8364 slist_free_strings(compose->to_list);
8365 g_slist_free(compose->to_list);
8366 slist_free_strings(compose->newsgroup_list);
8367 g_slist_free(compose->newsgroup_list);
8368 slist_free_strings(compose->header_list);
8369 g_slist_free(compose->header_list);
8371 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8373 g_hash_table_destroy(compose->email_hashtable);
8375 procmsg_msginfo_free(compose->targetinfo);
8376 procmsg_msginfo_free(compose->replyinfo);
8377 procmsg_msginfo_free(compose->fwdinfo);
8379 g_free(compose->replyto);
8380 g_free(compose->cc);
8381 g_free(compose->bcc);
8382 g_free(compose->newsgroups);
8383 g_free(compose->followup_to);
8385 g_free(compose->ml_post);
8387 g_free(compose->inreplyto);
8388 g_free(compose->references);
8389 g_free(compose->msgid);
8390 g_free(compose->boundary);
8392 g_free(compose->redirect_filename);
8393 if (compose->undostruct)
8394 undo_destroy(compose->undostruct);
8396 g_free(compose->sig_str);
8398 g_free(compose->exteditor_file);
8400 g_free(compose->orig_charset);
8402 g_free(compose->privacy_system);
8404 if (addressbook_get_target_compose() == compose)
8405 addressbook_set_target_compose(NULL);
8408 if (compose->gtkaspell) {
8409 gtkaspell_delete(compose->gtkaspell);
8410 compose->gtkaspell = NULL;
8414 if (!compose->batch) {
8415 gtk_widget_get_allocation(compose->scrolledwin, &allocation);
8416 prefs_common.compose_width = allocation.width;
8417 prefs_common.compose_height = allocation.height;
8420 if (!gtk_widget_get_parent(compose->paned))
8421 gtk_widget_destroy(compose->paned);
8422 gtk_widget_destroy(compose->popupmenu);
8424 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8425 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8426 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8428 gtk_widget_destroy(compose->window);
8429 toolbar_destroy(compose->toolbar);
8430 g_free(compose->toolbar);
8431 g_mutex_free(compose->mutex);
8435 static void compose_attach_info_free(AttachInfo *ainfo)
8437 g_free(ainfo->file);
8438 g_free(ainfo->content_type);
8439 g_free(ainfo->name);
8440 g_free(ainfo->charset);
8444 static void compose_attach_update_label(Compose *compose)
8449 GtkTreeModel *model;
8454 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8455 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8456 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8460 while(gtk_tree_model_iter_next(model, &iter))
8463 text = g_strdup_printf("(%d)", i);
8464 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8468 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8470 Compose *compose = (Compose *)data;
8471 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8472 GtkTreeSelection *selection;
8474 GtkTreeModel *model;
8476 selection = gtk_tree_view_get_selection(tree_view);
8477 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8482 for (cur = sel; cur != NULL; cur = cur->next) {
8483 GtkTreePath *path = cur->data;
8484 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8487 gtk_tree_path_free(path);
8490 for (cur = sel; cur != NULL; cur = cur->next) {
8491 GtkTreeRowReference *ref = cur->data;
8492 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8495 if (gtk_tree_model_get_iter(model, &iter, path))
8496 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8498 gtk_tree_path_free(path);
8499 gtk_tree_row_reference_free(ref);
8503 compose_attach_update_label(compose);
8506 static struct _AttachProperty
8509 GtkWidget *mimetype_entry;
8510 GtkWidget *encoding_optmenu;
8511 GtkWidget *path_entry;
8512 GtkWidget *filename_entry;
8514 GtkWidget *cancel_btn;
8517 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8519 gtk_tree_path_free((GtkTreePath *)ptr);
8522 static void compose_attach_property(GtkAction *action, gpointer data)
8524 Compose *compose = (Compose *)data;
8525 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8527 GtkComboBox *optmenu;
8528 GtkTreeSelection *selection;
8530 GtkTreeModel *model;
8533 static gboolean cancelled;
8535 /* only if one selected */
8536 selection = gtk_tree_view_get_selection(tree_view);
8537 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8540 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8544 path = (GtkTreePath *) sel->data;
8545 gtk_tree_model_get_iter(model, &iter, path);
8546 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8549 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8555 if (!attach_prop.window)
8556 compose_attach_property_create(&cancelled);
8557 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8558 gtk_widget_grab_focus(attach_prop.ok_btn);
8559 gtk_widget_show(attach_prop.window);
8560 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8562 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8563 if (ainfo->encoding == ENC_UNKNOWN)
8564 combobox_select_by_data(optmenu, ENC_BASE64);
8566 combobox_select_by_data(optmenu, ainfo->encoding);
8568 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8569 ainfo->content_type ? ainfo->content_type : "");
8570 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8571 ainfo->file ? ainfo->file : "");
8572 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8573 ainfo->name ? ainfo->name : "");
8576 const gchar *entry_text;
8578 gchar *cnttype = NULL;
8585 gtk_widget_hide(attach_prop.window);
8586 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8591 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8592 if (*entry_text != '\0') {
8595 text = g_strstrip(g_strdup(entry_text));
8596 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8597 cnttype = g_strdup(text);
8600 alertpanel_error(_("Invalid MIME type."));
8606 ainfo->encoding = combobox_get_active_data(optmenu);
8608 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8609 if (*entry_text != '\0') {
8610 if (is_file_exist(entry_text) &&
8611 (size = get_file_size(entry_text)) > 0)
8612 file = g_strdup(entry_text);
8615 (_("File doesn't exist or is empty."));
8621 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8622 if (*entry_text != '\0') {
8623 g_free(ainfo->name);
8624 ainfo->name = g_strdup(entry_text);
8628 g_free(ainfo->content_type);
8629 ainfo->content_type = cnttype;
8632 g_free(ainfo->file);
8636 ainfo->size = (goffset)size;
8638 /* update tree store */
8639 text = to_human_readable(ainfo->size);
8640 gtk_tree_model_get_iter(model, &iter, path);
8641 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8642 COL_MIMETYPE, ainfo->content_type,
8644 COL_NAME, ainfo->name,
8645 COL_CHARSET, ainfo->charset,
8651 gtk_tree_path_free(path);
8654 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8656 label = gtk_label_new(str); \
8657 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8658 GTK_FILL, 0, 0, 0); \
8659 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8661 entry = gtk_entry_new(); \
8662 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8663 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8666 static void compose_attach_property_create(gboolean *cancelled)
8672 GtkWidget *mimetype_entry;
8675 GtkListStore *optmenu_menu;
8676 GtkWidget *path_entry;
8677 GtkWidget *filename_entry;
8680 GtkWidget *cancel_btn;
8681 GList *mime_type_list, *strlist;
8684 debug_print("Creating attach_property window...\n");
8686 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8687 gtk_widget_set_size_request(window, 480, -1);
8688 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8689 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8690 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8691 g_signal_connect(G_OBJECT(window), "delete_event",
8692 G_CALLBACK(attach_property_delete_event),
8694 g_signal_connect(G_OBJECT(window), "key_press_event",
8695 G_CALLBACK(attach_property_key_pressed),
8698 vbox = gtk_vbox_new(FALSE, 8);
8699 gtk_container_add(GTK_CONTAINER(window), vbox);
8701 table = gtk_table_new(4, 2, FALSE);
8702 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8703 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8704 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8706 label = gtk_label_new(_("MIME type"));
8707 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8709 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8710 mimetype_entry = gtk_combo_box_entry_new_text();
8711 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8712 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8714 /* stuff with list */
8715 mime_type_list = procmime_get_mime_type_list();
8717 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8718 MimeType *type = (MimeType *) mime_type_list->data;
8721 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8723 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8726 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8727 (GCompareFunc)strcmp2);
8730 for (mime_type_list = strlist; mime_type_list != NULL;
8731 mime_type_list = mime_type_list->next) {
8732 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8733 g_free(mime_type_list->data);
8735 g_list_free(strlist);
8736 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8737 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8739 label = gtk_label_new(_("Encoding"));
8740 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8742 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8744 hbox = gtk_hbox_new(FALSE, 0);
8745 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8746 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8748 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8749 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8751 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8752 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8753 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8754 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8755 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8757 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8759 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8760 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8762 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8763 &ok_btn, GTK_STOCK_OK,
8765 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8766 gtk_widget_grab_default(ok_btn);
8768 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8769 G_CALLBACK(attach_property_ok),
8771 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8772 G_CALLBACK(attach_property_cancel),
8775 gtk_widget_show_all(vbox);
8777 attach_prop.window = window;
8778 attach_prop.mimetype_entry = mimetype_entry;
8779 attach_prop.encoding_optmenu = optmenu;
8780 attach_prop.path_entry = path_entry;
8781 attach_prop.filename_entry = filename_entry;
8782 attach_prop.ok_btn = ok_btn;
8783 attach_prop.cancel_btn = cancel_btn;
8786 #undef SET_LABEL_AND_ENTRY
8788 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8794 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8800 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8801 gboolean *cancelled)
8809 static gboolean attach_property_key_pressed(GtkWidget *widget,
8811 gboolean *cancelled)
8813 if (event && event->keyval == GDK_KEY_Escape) {
8817 if (event && event->keyval == GDK_KEY_Return) {
8825 static void compose_exec_ext_editor(Compose *compose)
8832 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8833 G_DIR_SEPARATOR, compose);
8835 if (pipe(pipe_fds) < 0) {
8841 if ((pid = fork()) < 0) {
8848 /* close the write side of the pipe */
8851 compose->exteditor_file = g_strdup(tmp);
8852 compose->exteditor_pid = pid;
8854 compose_set_ext_editor_sensitive(compose, FALSE);
8857 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8859 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8861 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8865 } else { /* process-monitoring process */
8871 /* close the read side of the pipe */
8874 if (compose_write_body_to_file(compose, tmp) < 0) {
8875 fd_write_all(pipe_fds[1], "2\n", 2);
8879 pid_ed = compose_exec_ext_editor_real(tmp);
8881 fd_write_all(pipe_fds[1], "1\n", 2);
8885 /* wait until editor is terminated */
8886 waitpid(pid_ed, NULL, 0);
8888 fd_write_all(pipe_fds[1], "0\n", 2);
8895 #endif /* G_OS_UNIX */
8899 static gint compose_exec_ext_editor_real(const gchar *file)
8906 cm_return_val_if_fail(file != NULL, -1);
8908 if ((pid = fork()) < 0) {
8913 if (pid != 0) return pid;
8915 /* grandchild process */
8917 if (setpgid(0, getppid()))
8920 if (prefs_common_get_ext_editor_cmd() &&
8921 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8922 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8923 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8925 if (prefs_common_get_ext_editor_cmd())
8926 g_warning("External editor command-line is invalid: '%s'\n",
8927 prefs_common_get_ext_editor_cmd());
8928 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8931 cmdline = strsplit_with_quote(buf, " ", 1024);
8932 execvp(cmdline[0], cmdline);
8935 g_strfreev(cmdline);
8940 static gboolean compose_ext_editor_kill(Compose *compose)
8942 pid_t pgid = compose->exteditor_pid * -1;
8945 ret = kill(pgid, 0);
8947 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8951 msg = g_strdup_printf
8952 (_("The external editor is still working.\n"
8953 "Force terminating the process?\n"
8954 "process group id: %d"), -pgid);
8955 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8956 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8960 if (val == G_ALERTALTERNATE) {
8961 g_source_remove(compose->exteditor_tag);
8962 g_io_channel_shutdown(compose->exteditor_ch,
8964 g_io_channel_unref(compose->exteditor_ch);
8966 if (kill(pgid, SIGTERM) < 0) perror("kill");
8967 waitpid(compose->exteditor_pid, NULL, 0);
8969 g_warning("Terminated process group id: %d", -pgid);
8970 g_warning("Temporary file: %s",
8971 compose->exteditor_file);
8973 compose_set_ext_editor_sensitive(compose, TRUE);
8975 g_free(compose->exteditor_file);
8976 compose->exteditor_file = NULL;
8977 compose->exteditor_pid = -1;
8978 compose->exteditor_ch = NULL;
8979 compose->exteditor_tag = -1;
8987 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8991 Compose *compose = (Compose *)data;
8994 debug_print("Compose: input from monitoring process\n");
8996 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8998 g_io_channel_shutdown(source, FALSE, NULL);
8999 g_io_channel_unref(source);
9001 waitpid(compose->exteditor_pid, NULL, 0);
9003 if (buf[0] == '0') { /* success */
9004 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9005 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9007 gtk_text_buffer_set_text(buffer, "", -1);
9008 compose_insert_file(compose, compose->exteditor_file);
9009 compose_changed_cb(NULL, compose);
9010 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9012 if (claws_unlink(compose->exteditor_file) < 0)
9013 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9014 } else if (buf[0] == '1') { /* failed */
9015 g_warning("Couldn't exec external editor\n");
9016 if (claws_unlink(compose->exteditor_file) < 0)
9017 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9018 } else if (buf[0] == '2') {
9019 g_warning("Couldn't write to file\n");
9020 } else if (buf[0] == '3') {
9021 g_warning("Pipe read failed\n");
9024 compose_set_ext_editor_sensitive(compose, TRUE);
9026 g_free(compose->exteditor_file);
9027 compose->exteditor_file = NULL;
9028 compose->exteditor_pid = -1;
9029 compose->exteditor_ch = NULL;
9030 compose->exteditor_tag = -1;
9035 static void compose_set_ext_editor_sensitive(Compose *compose,
9038 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9039 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9040 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9041 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9042 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9043 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9044 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9046 gtk_widget_set_sensitive(compose->text, sensitive);
9047 if (compose->toolbar->send_btn)
9048 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
9049 if (compose->toolbar->sendl_btn)
9050 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
9051 if (compose->toolbar->draft_btn)
9052 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
9053 if (compose->toolbar->insert_btn)
9054 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
9055 if (compose->toolbar->sig_btn)
9056 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
9057 if (compose->toolbar->exteditor_btn)
9058 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9059 if (compose->toolbar->linewrap_current_btn)
9060 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9061 if (compose->toolbar->linewrap_all_btn)
9062 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9064 #endif /* G_OS_UNIX */
9067 * compose_undo_state_changed:
9069 * Change the sensivity of the menuentries undo and redo
9071 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9072 gint redo_state, gpointer data)
9074 Compose *compose = (Compose *)data;
9076 switch (undo_state) {
9077 case UNDO_STATE_TRUE:
9078 if (!undostruct->undo_state) {
9079 undostruct->undo_state = TRUE;
9080 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9083 case UNDO_STATE_FALSE:
9084 if (undostruct->undo_state) {
9085 undostruct->undo_state = FALSE;
9086 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9089 case UNDO_STATE_UNCHANGED:
9091 case UNDO_STATE_REFRESH:
9092 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9095 g_warning("Undo state not recognized");
9099 switch (redo_state) {
9100 case UNDO_STATE_TRUE:
9101 if (!undostruct->redo_state) {
9102 undostruct->redo_state = TRUE;
9103 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9106 case UNDO_STATE_FALSE:
9107 if (undostruct->redo_state) {
9108 undostruct->redo_state = FALSE;
9109 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9112 case UNDO_STATE_UNCHANGED:
9114 case UNDO_STATE_REFRESH:
9115 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9118 g_warning("Redo state not recognized");
9123 /* callback functions */
9125 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9126 * includes "non-client" (windows-izm) in calculation, so this calculation
9127 * may not be accurate.
9129 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9130 GtkAllocation *allocation,
9131 GtkSHRuler *shruler)
9133 if (prefs_common.show_ruler) {
9134 gint char_width = 0, char_height = 0;
9135 gint line_width_in_chars;
9137 gtkut_get_font_size(GTK_WIDGET(widget),
9138 &char_width, &char_height);
9139 line_width_in_chars =
9140 (allocation->width - allocation->x) / char_width;
9142 /* got the maximum */
9143 gtk_shruler_set_range(GTK_SHRULER(shruler),
9144 0.0, line_width_in_chars, 0);
9153 ComposePrefType type;
9154 gboolean entry_marked;
9157 static void account_activated(GtkComboBox *optmenu, gpointer data)
9159 Compose *compose = (Compose *)data;
9162 gchar *folderidentifier;
9163 gint account_id = 0;
9166 GSList *list, *saved_list = NULL;
9167 HeaderEntryState *state;
9168 GtkRcStyle *style = NULL;
9169 static GdkColor yellow;
9170 static gboolean color_set = FALSE;
9172 /* Get ID of active account in the combo box */
9173 menu = gtk_combo_box_get_model(optmenu);
9174 gtk_combo_box_get_active_iter(optmenu, &iter);
9175 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9177 ac = account_find_from_id(account_id);
9178 cm_return_if_fail(ac != NULL);
9180 if (ac != compose->account) {
9181 compose_select_account(compose, ac, FALSE);
9183 for (list = compose->header_list; list; list = list->next) {
9184 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9186 if (hentry->type == PREF_ACCOUNT || !list->next) {
9187 compose_destroy_headerentry(compose, hentry);
9191 state = g_malloc0(sizeof(HeaderEntryState));
9192 state->header = gtk_editable_get_chars(GTK_EDITABLE(
9193 gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9194 state->entry = gtk_editable_get_chars(
9195 GTK_EDITABLE(hentry->entry), 0, -1);
9196 state->type = hentry->type;
9199 gdk_color_parse("#f5f6be", &yellow);
9200 color_set = gdk_colormap_alloc_color(
9201 gdk_colormap_get_system(),
9202 &yellow, FALSE, TRUE);
9205 style = gtk_widget_get_modifier_style(hentry->entry);
9206 state->entry_marked = gdk_color_equal(&yellow,
9207 &style->base[GTK_STATE_NORMAL]);
9209 saved_list = g_slist_append(saved_list, state);
9210 compose_destroy_headerentry(compose, hentry);
9213 compose->header_last = NULL;
9214 g_slist_free(compose->header_list);
9215 compose->header_list = NULL;
9216 compose->header_nextrow = 1;
9217 compose_create_header_entry(compose);
9219 if (ac->set_autocc && ac->auto_cc)
9220 compose_entry_append(compose, ac->auto_cc,
9221 COMPOSE_CC, PREF_ACCOUNT);
9223 if (ac->set_autobcc && ac->auto_bcc)
9224 compose_entry_append(compose, ac->auto_bcc,
9225 COMPOSE_BCC, PREF_ACCOUNT);
9227 if (ac->set_autoreplyto && ac->auto_replyto)
9228 compose_entry_append(compose, ac->auto_replyto,
9229 COMPOSE_REPLYTO, PREF_ACCOUNT);
9231 for (list = saved_list; list; list = list->next) {
9232 state = (HeaderEntryState *) list->data;
9234 compose_add_header_entry(compose, state->header,
9235 state->entry, state->type);
9236 if (state->entry_marked)
9237 compose_entry_mark_default_to(compose, state->entry);
9239 g_free(state->header);
9240 g_free(state->entry);
9243 g_slist_free(saved_list);
9245 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9246 (ac->protocol == A_NNTP) ?
9247 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9250 /* Set message save folder */
9251 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9252 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9254 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9255 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9257 compose_set_save_to(compose, NULL);
9258 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9259 folderidentifier = folder_item_get_identifier(account_get_special_folder
9260 (compose->account, F_OUTBOX));
9261 compose_set_save_to(compose, folderidentifier);
9262 g_free(folderidentifier);
9266 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9267 GtkTreeViewColumn *column, Compose *compose)
9269 compose_attach_property(NULL, compose);
9272 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9275 Compose *compose = (Compose *)data;
9276 GtkTreeSelection *attach_selection;
9277 gint attach_nr_selected;
9279 if (!event) return FALSE;
9281 if (event->button == 3) {
9282 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9283 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9285 if (attach_nr_selected > 0)
9287 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9288 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9290 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9291 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9294 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9295 NULL, NULL, event->button, event->time);
9302 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9305 Compose *compose = (Compose *)data;
9307 if (!event) return FALSE;
9309 switch (event->keyval) {
9310 case GDK_KEY_Delete:
9311 compose_attach_remove_selected(NULL, compose);
9317 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9319 toolbar_comp_set_sensitive(compose, allow);
9320 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9321 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9323 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9325 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9326 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9327 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9329 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9333 static void compose_send_cb(GtkAction *action, gpointer data)
9335 Compose *compose = (Compose *)data;
9337 if (prefs_common.work_offline &&
9338 !inc_offline_should_override(TRUE,
9339 _("Claws Mail needs network access in order "
9340 "to send this email.")))
9343 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9344 g_source_remove(compose->draft_timeout_tag);
9345 compose->draft_timeout_tag = -1;
9348 compose_send(compose);
9351 static void compose_send_later_cb(GtkAction *action, gpointer data)
9353 Compose *compose = (Compose *)data;
9357 compose_allow_user_actions(compose, FALSE);
9358 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9359 compose_allow_user_actions(compose, TRUE);
9363 compose_close(compose);
9364 } else if (val == -1) {
9365 alertpanel_error(_("Could not queue message."));
9366 } else if (val == -2) {
9367 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9368 } else if (val == -3) {
9369 if (privacy_peek_error())
9370 alertpanel_error(_("Could not queue message for sending:\n\n"
9371 "Signature failed: %s"), privacy_get_error());
9372 } else if (val == -4) {
9373 alertpanel_error(_("Could not queue message for sending:\n\n"
9374 "Charset conversion failed."));
9375 } else if (val == -5) {
9376 alertpanel_error(_("Could not queue message for sending:\n\n"
9377 "Couldn't get recipient encryption key."));
9378 } else if (val == -6) {
9381 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9384 #define DRAFTED_AT_EXIT "drafted_at_exit"
9385 static void compose_register_draft(MsgInfo *info)
9387 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9388 DRAFTED_AT_EXIT, NULL);
9389 FILE *fp = g_fopen(filepath, "ab");
9392 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9400 gboolean compose_draft (gpointer data, guint action)
9402 Compose *compose = (Compose *)data;
9406 MsgFlags flag = {0, 0};
9407 static gboolean lock = FALSE;
9408 MsgInfo *newmsginfo;
9410 gboolean target_locked = FALSE;
9411 gboolean err = FALSE;
9413 if (lock) return FALSE;
9415 if (compose->sending)
9418 draft = account_get_special_folder(compose->account, F_DRAFT);
9419 cm_return_val_if_fail(draft != NULL, FALSE);
9421 if (!g_mutex_trylock(compose->mutex)) {
9422 /* we don't want to lock the mutex once it's available,
9423 * because as the only other part of compose.c locking
9424 * it is compose_close - which means once unlocked,
9425 * the compose struct will be freed */
9426 debug_print("couldn't lock mutex, probably sending\n");
9432 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9433 G_DIR_SEPARATOR, compose);
9434 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9435 FILE_OP_ERROR(tmp, "fopen");
9439 /* chmod for security */
9440 if (change_file_mode_rw(fp, tmp) < 0) {
9441 FILE_OP_ERROR(tmp, "chmod");
9442 g_warning("can't change file mode\n");
9445 /* Save draft infos */
9446 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9447 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9449 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9450 gchar *savefolderid;
9452 savefolderid = compose_get_save_to(compose);
9453 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9454 g_free(savefolderid);
9456 if (compose->return_receipt) {
9457 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9459 if (compose->privacy_system) {
9460 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9461 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9462 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9465 /* Message-ID of message replying to */
9466 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9469 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9470 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9473 /* Message-ID of message forwarding to */
9474 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9477 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9478 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9482 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9483 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9485 /* end of headers */
9486 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9493 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9497 if (fclose(fp) == EOF) {
9501 if (compose->targetinfo) {
9502 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9503 flag.perm_flags = target_locked?MSG_LOCKED:0;
9505 flag.tmp_flags = MSG_DRAFT;
9507 folder_item_scan(draft);
9508 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9509 MsgInfo *tmpinfo = NULL;
9510 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9511 if (compose->msgid) {
9512 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9515 msgnum = tmpinfo->msgnum;
9516 procmsg_msginfo_free(tmpinfo);
9517 debug_print("got draft msgnum %d from scanning\n", msgnum);
9519 debug_print("didn't get draft msgnum after scanning\n");
9522 debug_print("got draft msgnum %d from adding\n", msgnum);
9528 if (action != COMPOSE_AUTO_SAVE) {
9529 if (action != COMPOSE_DRAFT_FOR_EXIT)
9530 alertpanel_error(_("Could not save draft."));
9533 gtkut_window_popup(compose->window);
9534 val = alertpanel_full(_("Could not save draft"),
9535 _("Could not save draft.\n"
9536 "Do you want to cancel exit or discard this email?"),
9537 _("_Cancel exit"), _("_Discard email"), NULL,
9538 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9539 if (val == G_ALERTALTERNATE) {
9541 g_mutex_unlock(compose->mutex); /* must be done before closing */
9542 compose_close(compose);
9546 g_mutex_unlock(compose->mutex); /* must be done before closing */
9555 if (compose->mode == COMPOSE_REEDIT) {
9556 compose_remove_reedit_target(compose, TRUE);
9559 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9562 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9564 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9566 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9567 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9568 procmsg_msginfo_set_flags(newmsginfo, 0,
9569 MSG_HAS_ATTACHMENT);
9571 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9572 compose_register_draft(newmsginfo);
9574 procmsg_msginfo_free(newmsginfo);
9577 folder_item_scan(draft);
9579 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9581 g_mutex_unlock(compose->mutex); /* must be done before closing */
9582 compose_close(compose);
9588 path = folder_item_fetch_msg(draft, msgnum);
9590 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9593 if (g_stat(path, &s) < 0) {
9594 FILE_OP_ERROR(path, "stat");
9600 procmsg_msginfo_free(compose->targetinfo);
9601 compose->targetinfo = procmsg_msginfo_new();
9602 compose->targetinfo->msgnum = msgnum;
9603 compose->targetinfo->size = (goffset)s.st_size;
9604 compose->targetinfo->mtime = s.st_mtime;
9605 compose->targetinfo->folder = draft;
9607 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9608 compose->mode = COMPOSE_REEDIT;
9610 if (action == COMPOSE_AUTO_SAVE) {
9611 compose->autosaved_draft = compose->targetinfo;
9613 compose->modified = FALSE;
9614 compose_set_title(compose);
9618 g_mutex_unlock(compose->mutex);
9622 void compose_clear_exit_drafts(void)
9624 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9625 DRAFTED_AT_EXIT, NULL);
9626 if (is_file_exist(filepath))
9627 claws_unlink(filepath);
9632 void compose_reopen_exit_drafts(void)
9634 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9635 DRAFTED_AT_EXIT, NULL);
9636 FILE *fp = g_fopen(filepath, "rb");
9640 while (fgets(buf, sizeof(buf), fp)) {
9641 gchar **parts = g_strsplit(buf, "\t", 2);
9642 const gchar *folder = parts[0];
9643 int msgnum = parts[1] ? atoi(parts[1]):-1;
9645 if (folder && *folder && msgnum > -1) {
9646 FolderItem *item = folder_find_item_from_identifier(folder);
9647 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9649 compose_reedit(info, FALSE);
9656 compose_clear_exit_drafts();
9659 static void compose_save_cb(GtkAction *action, gpointer data)
9661 Compose *compose = (Compose *)data;
9662 compose_draft(compose, COMPOSE_KEEP_EDITING);
9663 compose->rmode = COMPOSE_REEDIT;
9666 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9668 if (compose && file_list) {
9671 for ( tmp = file_list; tmp; tmp = tmp->next) {
9672 gchar *file = (gchar *) tmp->data;
9673 gchar *utf8_filename = conv_filename_to_utf8(file);
9674 compose_attach_append(compose, file, utf8_filename, NULL, NULL);
9675 compose_changed_cb(NULL, compose);
9680 g_free(utf8_filename);
9685 static void compose_attach_cb(GtkAction *action, gpointer data)
9687 Compose *compose = (Compose *)data;
9690 if (compose->redirect_filename != NULL)
9693 file_list = filesel_select_multiple_files_open(_("Select file"));
9696 compose_attach_from_list(compose, file_list, TRUE);
9697 g_list_free(file_list);
9701 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9703 Compose *compose = (Compose *)data;
9705 gint files_inserted = 0;
9707 file_list = filesel_select_multiple_files_open(_("Select file"));
9712 for ( tmp = file_list; tmp; tmp = tmp->next) {
9713 gchar *file = (gchar *) tmp->data;
9714 gchar *filedup = g_strdup(file);
9715 gchar *shortfile = g_path_get_basename(filedup);
9716 ComposeInsertResult res;
9717 /* insert the file if the file is short or if the user confirmed that
9718 he/she wants to insert the large file */
9719 res = compose_insert_file(compose, file);
9720 if (res == COMPOSE_INSERT_READ_ERROR) {
9721 alertpanel_error(_("File '%s' could not be read."), shortfile);
9722 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9723 alertpanel_error(_("File '%s' contained invalid characters\n"
9724 "for the current encoding, insertion may be incorrect."),
9726 } else if (res == COMPOSE_INSERT_SUCCESS)
9733 g_list_free(file_list);
9737 if (files_inserted > 0 && compose->gtkaspell &&
9738 compose->gtkaspell->check_while_typing)
9739 gtkaspell_highlight_all(compose->gtkaspell);
9743 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9745 Compose *compose = (Compose *)data;
9747 compose_insert_sig(compose, FALSE);
9750 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9754 Compose *compose = (Compose *)data;
9756 gtkut_widget_get_uposition(widget, &x, &y);
9757 if (!compose->batch) {
9758 prefs_common.compose_x = x;
9759 prefs_common.compose_y = y;
9761 if (compose->sending || compose->updating)
9763 compose_close_cb(NULL, compose);
9767 void compose_close_toolbar(Compose *compose)
9769 compose_close_cb(NULL, compose);
9772 static void compose_close_cb(GtkAction *action, gpointer data)
9774 Compose *compose = (Compose *)data;
9778 if (compose->exteditor_tag != -1) {
9779 if (!compose_ext_editor_kill(compose))
9784 if (compose->modified) {
9785 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9786 if (!g_mutex_trylock(compose->mutex)) {
9787 /* we don't want to lock the mutex once it's available,
9788 * because as the only other part of compose.c locking
9789 * it is compose_close - which means once unlocked,
9790 * the compose struct will be freed */
9791 debug_print("couldn't lock mutex, probably sending\n");
9795 val = alertpanel(_("Discard message"),
9796 _("This message has been modified. Discard it?"),
9797 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9799 val = alertpanel(_("Save changes"),
9800 _("This message has been modified. Save the latest changes?"),
9801 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9803 g_mutex_unlock(compose->mutex);
9805 case G_ALERTDEFAULT:
9806 if (prefs_common.autosave && !reedit)
9807 compose_remove_draft(compose);
9809 case G_ALERTALTERNATE:
9810 compose_draft(data, COMPOSE_QUIT_EDITING);
9817 compose_close(compose);
9820 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9822 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9823 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9824 Compose *compose = (Compose *) data;
9827 compose->out_encoding = (CharSet)value;
9830 static void compose_address_cb(GtkAction *action, gpointer data)
9832 Compose *compose = (Compose *)data;
9834 addressbook_open(compose);
9837 static void about_show_cb(GtkAction *action, gpointer data)
9842 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9844 Compose *compose = (Compose *)data;
9849 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9850 cm_return_if_fail(tmpl != NULL);
9852 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9854 val = alertpanel(_("Apply template"), msg,
9855 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9858 if (val == G_ALERTDEFAULT)
9859 compose_template_apply(compose, tmpl, TRUE);
9860 else if (val == G_ALERTALTERNATE)
9861 compose_template_apply(compose, tmpl, FALSE);
9864 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9866 Compose *compose = (Compose *)data;
9868 compose_exec_ext_editor(compose);
9871 static void compose_undo_cb(GtkAction *action, gpointer data)
9873 Compose *compose = (Compose *)data;
9874 gboolean prev_autowrap = compose->autowrap;
9876 compose->autowrap = FALSE;
9877 undo_undo(compose->undostruct);
9878 compose->autowrap = prev_autowrap;
9881 static void compose_redo_cb(GtkAction *action, gpointer data)
9883 Compose *compose = (Compose *)data;
9884 gboolean prev_autowrap = compose->autowrap;
9886 compose->autowrap = FALSE;
9887 undo_redo(compose->undostruct);
9888 compose->autowrap = prev_autowrap;
9891 static void entry_cut_clipboard(GtkWidget *entry)
9893 if (GTK_IS_EDITABLE(entry))
9894 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9895 else if (GTK_IS_TEXT_VIEW(entry))
9896 gtk_text_buffer_cut_clipboard(
9897 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9898 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9902 static void entry_copy_clipboard(GtkWidget *entry)
9904 if (GTK_IS_EDITABLE(entry))
9905 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9906 else if (GTK_IS_TEXT_VIEW(entry))
9907 gtk_text_buffer_copy_clipboard(
9908 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9909 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9912 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9913 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9915 if (GTK_IS_TEXT_VIEW(entry)) {
9916 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9917 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9918 GtkTextIter start_iter, end_iter;
9920 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9922 if (contents == NULL)
9925 /* we shouldn't delete the selection when middle-click-pasting, or we
9926 * can't mid-click-paste our own selection */
9927 if (clip != GDK_SELECTION_PRIMARY) {
9928 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9929 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9932 if (insert_place == NULL) {
9933 /* if insert_place isn't specified, insert at the cursor.
9934 * used for Ctrl-V pasting */
9935 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9936 start = gtk_text_iter_get_offset(&start_iter);
9937 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9939 /* if insert_place is specified, paste here.
9940 * used for mid-click-pasting */
9941 start = gtk_text_iter_get_offset(insert_place);
9942 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9943 if (prefs_common.primary_paste_unselects)
9944 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9948 /* paste unwrapped: mark the paste so it's not wrapped later */
9949 end = start + strlen(contents);
9950 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9951 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9952 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9953 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9954 /* rewrap paragraph now (after a mid-click-paste) */
9955 mark_start = gtk_text_buffer_get_insert(buffer);
9956 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9957 gtk_text_iter_backward_char(&start_iter);
9958 compose_beautify_paragraph(compose, &start_iter, TRUE);
9960 } else if (GTK_IS_EDITABLE(entry))
9961 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9963 compose->modified = TRUE;
9966 static void entry_allsel(GtkWidget *entry)
9968 if (GTK_IS_EDITABLE(entry))
9969 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9970 else if (GTK_IS_TEXT_VIEW(entry)) {
9971 GtkTextIter startiter, enditer;
9972 GtkTextBuffer *textbuf;
9974 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9975 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9976 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9978 gtk_text_buffer_move_mark_by_name(textbuf,
9979 "selection_bound", &startiter);
9980 gtk_text_buffer_move_mark_by_name(textbuf,
9981 "insert", &enditer);
9985 static void compose_cut_cb(GtkAction *action, gpointer data)
9987 Compose *compose = (Compose *)data;
9988 if (compose->focused_editable
9989 #ifndef GENERIC_UMPC
9990 && gtk_widget_has_focus(compose->focused_editable)
9993 entry_cut_clipboard(compose->focused_editable);
9996 static void compose_copy_cb(GtkAction *action, gpointer data)
9998 Compose *compose = (Compose *)data;
9999 if (compose->focused_editable
10000 #ifndef GENERIC_UMPC
10001 && gtk_widget_has_focus(compose->focused_editable)
10004 entry_copy_clipboard(compose->focused_editable);
10007 static void compose_paste_cb(GtkAction *action, gpointer data)
10009 Compose *compose = (Compose *)data;
10010 gint prev_autowrap;
10011 GtkTextBuffer *buffer;
10013 if (compose->focused_editable &&
10014 gtk_widget_has_focus(compose->focused_editable))
10015 entry_paste_clipboard(compose, compose->focused_editable,
10016 prefs_common.linewrap_pastes,
10017 GDK_SELECTION_CLIPBOARD, NULL);
10021 if (gtk_widget_has_focus(compose->text) &&
10022 compose->gtkaspell &&
10023 compose->gtkaspell->check_while_typing)
10024 gtkaspell_highlight_all(compose->gtkaspell);
10028 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10030 Compose *compose = (Compose *)data;
10031 gint wrap_quote = prefs_common.linewrap_quote;
10032 if (compose->focused_editable
10033 #ifndef GENERIC_UMPC
10034 && gtk_widget_has_focus(compose->focused_editable)
10037 /* let text_insert() (called directly or at a later time
10038 * after the gtk_editable_paste_clipboard) know that
10039 * text is to be inserted as a quotation. implemented
10040 * by using a simple refcount... */
10041 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10042 G_OBJECT(compose->focused_editable),
10043 "paste_as_quotation"));
10044 g_object_set_data(G_OBJECT(compose->focused_editable),
10045 "paste_as_quotation",
10046 GINT_TO_POINTER(paste_as_quotation + 1));
10047 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10048 entry_paste_clipboard(compose, compose->focused_editable,
10049 prefs_common.linewrap_pastes,
10050 GDK_SELECTION_CLIPBOARD, NULL);
10051 prefs_common.linewrap_quote = wrap_quote;
10055 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10057 Compose *compose = (Compose *)data;
10058 gint prev_autowrap;
10059 GtkTextBuffer *buffer;
10061 if (compose->focused_editable
10062 #ifndef GENERIC_UMPC
10063 && gtk_widget_has_focus(compose->focused_editable)
10066 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10067 GDK_SELECTION_CLIPBOARD, NULL);
10071 if (gtk_widget_has_focus(compose->text) &&
10072 compose->gtkaspell &&
10073 compose->gtkaspell->check_while_typing)
10074 gtkaspell_highlight_all(compose->gtkaspell);
10078 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10080 Compose *compose = (Compose *)data;
10081 gint prev_autowrap;
10082 GtkTextBuffer *buffer;
10084 if (compose->focused_editable
10085 #ifndef GENERIC_UMPC
10086 && gtk_widget_has_focus(compose->focused_editable)
10089 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10090 GDK_SELECTION_CLIPBOARD, NULL);
10094 if (gtk_widget_has_focus(compose->text) &&
10095 compose->gtkaspell &&
10096 compose->gtkaspell->check_while_typing)
10097 gtkaspell_highlight_all(compose->gtkaspell);
10101 static void compose_allsel_cb(GtkAction *action, gpointer data)
10103 Compose *compose = (Compose *)data;
10104 if (compose->focused_editable
10105 #ifndef GENERIC_UMPC
10106 && gtk_widget_has_focus(compose->focused_editable)
10109 entry_allsel(compose->focused_editable);
10112 static void textview_move_beginning_of_line (GtkTextView *text)
10114 GtkTextBuffer *buffer;
10118 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10120 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10121 mark = gtk_text_buffer_get_insert(buffer);
10122 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10123 gtk_text_iter_set_line_offset(&ins, 0);
10124 gtk_text_buffer_place_cursor(buffer, &ins);
10127 static void textview_move_forward_character (GtkTextView *text)
10129 GtkTextBuffer *buffer;
10133 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10135 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10136 mark = gtk_text_buffer_get_insert(buffer);
10137 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10138 if (gtk_text_iter_forward_cursor_position(&ins))
10139 gtk_text_buffer_place_cursor(buffer, &ins);
10142 static void textview_move_backward_character (GtkTextView *text)
10144 GtkTextBuffer *buffer;
10148 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10150 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10151 mark = gtk_text_buffer_get_insert(buffer);
10152 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10153 if (gtk_text_iter_backward_cursor_position(&ins))
10154 gtk_text_buffer_place_cursor(buffer, &ins);
10157 static void textview_move_forward_word (GtkTextView *text)
10159 GtkTextBuffer *buffer;
10164 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10166 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10167 mark = gtk_text_buffer_get_insert(buffer);
10168 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10169 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10170 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10171 gtk_text_iter_backward_word_start(&ins);
10172 gtk_text_buffer_place_cursor(buffer, &ins);
10176 static void textview_move_backward_word (GtkTextView *text)
10178 GtkTextBuffer *buffer;
10183 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10185 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10186 mark = gtk_text_buffer_get_insert(buffer);
10187 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10188 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10189 if (gtk_text_iter_backward_word_starts(&ins, 1))
10190 gtk_text_buffer_place_cursor(buffer, &ins);
10193 static void textview_move_end_of_line (GtkTextView *text)
10195 GtkTextBuffer *buffer;
10199 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10201 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10202 mark = gtk_text_buffer_get_insert(buffer);
10203 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10204 if (gtk_text_iter_forward_to_line_end(&ins))
10205 gtk_text_buffer_place_cursor(buffer, &ins);
10208 static void textview_move_next_line (GtkTextView *text)
10210 GtkTextBuffer *buffer;
10215 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10217 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10218 mark = gtk_text_buffer_get_insert(buffer);
10219 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10220 offset = gtk_text_iter_get_line_offset(&ins);
10221 if (gtk_text_iter_forward_line(&ins)) {
10222 gtk_text_iter_set_line_offset(&ins, offset);
10223 gtk_text_buffer_place_cursor(buffer, &ins);
10227 static void textview_move_previous_line (GtkTextView *text)
10229 GtkTextBuffer *buffer;
10234 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10236 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10237 mark = gtk_text_buffer_get_insert(buffer);
10238 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10239 offset = gtk_text_iter_get_line_offset(&ins);
10240 if (gtk_text_iter_backward_line(&ins)) {
10241 gtk_text_iter_set_line_offset(&ins, offset);
10242 gtk_text_buffer_place_cursor(buffer, &ins);
10246 static void textview_delete_forward_character (GtkTextView *text)
10248 GtkTextBuffer *buffer;
10250 GtkTextIter ins, end_iter;
10252 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10254 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10255 mark = gtk_text_buffer_get_insert(buffer);
10256 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10258 if (gtk_text_iter_forward_char(&end_iter)) {
10259 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10263 static void textview_delete_backward_character (GtkTextView *text)
10265 GtkTextBuffer *buffer;
10267 GtkTextIter ins, end_iter;
10269 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10271 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10272 mark = gtk_text_buffer_get_insert(buffer);
10273 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10275 if (gtk_text_iter_backward_char(&end_iter)) {
10276 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10280 static void textview_delete_forward_word (GtkTextView *text)
10282 GtkTextBuffer *buffer;
10284 GtkTextIter ins, end_iter;
10286 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10288 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10289 mark = gtk_text_buffer_get_insert(buffer);
10290 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10292 if (gtk_text_iter_forward_word_end(&end_iter)) {
10293 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10297 static void textview_delete_backward_word (GtkTextView *text)
10299 GtkTextBuffer *buffer;
10301 GtkTextIter ins, end_iter;
10303 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10305 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10306 mark = gtk_text_buffer_get_insert(buffer);
10307 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10309 if (gtk_text_iter_backward_word_start(&end_iter)) {
10310 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10314 static void textview_delete_line (GtkTextView *text)
10316 GtkTextBuffer *buffer;
10318 GtkTextIter ins, start_iter, end_iter;
10320 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10322 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10323 mark = gtk_text_buffer_get_insert(buffer);
10324 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10327 gtk_text_iter_set_line_offset(&start_iter, 0);
10330 if (gtk_text_iter_ends_line(&end_iter)){
10331 if (!gtk_text_iter_forward_char(&end_iter))
10332 gtk_text_iter_backward_char(&start_iter);
10335 gtk_text_iter_forward_to_line_end(&end_iter);
10336 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10339 static void textview_delete_to_line_end (GtkTextView *text)
10341 GtkTextBuffer *buffer;
10343 GtkTextIter ins, end_iter;
10345 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10347 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10348 mark = gtk_text_buffer_get_insert(buffer);
10349 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10351 if (gtk_text_iter_ends_line(&end_iter))
10352 gtk_text_iter_forward_char(&end_iter);
10354 gtk_text_iter_forward_to_line_end(&end_iter);
10355 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10358 #define DO_ACTION(name, act) { \
10359 if(!strcmp(name, a_name)) { \
10363 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10365 const gchar *a_name = gtk_action_get_name(action);
10366 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10367 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10368 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10369 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10370 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10371 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10372 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10373 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10374 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10375 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10376 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10377 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10378 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10379 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10383 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10385 Compose *compose = (Compose *)data;
10386 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10387 ComposeCallAdvancedAction action = -1;
10389 action = compose_call_advanced_action_from_path(gaction);
10392 void (*do_action) (GtkTextView *text);
10393 } action_table[] = {
10394 {textview_move_beginning_of_line},
10395 {textview_move_forward_character},
10396 {textview_move_backward_character},
10397 {textview_move_forward_word},
10398 {textview_move_backward_word},
10399 {textview_move_end_of_line},
10400 {textview_move_next_line},
10401 {textview_move_previous_line},
10402 {textview_delete_forward_character},
10403 {textview_delete_backward_character},
10404 {textview_delete_forward_word},
10405 {textview_delete_backward_word},
10406 {textview_delete_line},
10407 {textview_delete_to_line_end}
10410 if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
10412 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10413 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10414 if (action_table[action].do_action)
10415 action_table[action].do_action(text);
10417 g_warning("Not implemented yet.");
10421 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10423 GtkAllocation allocation;
10427 if (GTK_IS_EDITABLE(widget)) {
10428 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10429 gtk_editable_set_position(GTK_EDITABLE(widget),
10432 if ((parent = gtk_widget_get_parent(widget))
10433 && (parent = gtk_widget_get_parent(parent))
10434 && (parent = gtk_widget_get_parent(parent))) {
10435 if (GTK_IS_SCROLLED_WINDOW(parent)) {
10436 gtk_widget_get_allocation(widget, &allocation);
10437 gint y = allocation.y;
10438 gint height = allocation.height;
10439 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10440 (GTK_SCROLLED_WINDOW(parent));
10442 gfloat value = gtk_adjustment_get_value(shown);
10443 gfloat upper = gtk_adjustment_get_upper(shown);
10444 gfloat page_size = gtk_adjustment_get_page_size(shown);
10445 if (y < (int)value) {
10446 gtk_adjustment_set_value(shown, y - 1);
10448 if ((y + height) > ((int)value + (int)page_size)) {
10449 if ((y - height - 1) < ((int)upper - (int)page_size)) {
10450 gtk_adjustment_set_value(shown,
10451 y + height - (int)page_size - 1);
10453 gtk_adjustment_set_value(shown,
10454 (int)upper - (int)page_size - 1);
10461 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10462 compose->focused_editable = widget;
10464 #ifdef GENERIC_UMPC
10465 if (GTK_IS_TEXT_VIEW(widget)
10466 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10467 g_object_ref(compose->notebook);
10468 g_object_ref(compose->edit_vbox);
10469 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10470 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10471 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10472 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10473 g_object_unref(compose->notebook);
10474 g_object_unref(compose->edit_vbox);
10475 g_signal_handlers_block_by_func(G_OBJECT(widget),
10476 G_CALLBACK(compose_grab_focus_cb),
10478 gtk_widget_grab_focus(widget);
10479 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10480 G_CALLBACK(compose_grab_focus_cb),
10482 } else if (!GTK_IS_TEXT_VIEW(widget)
10483 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10484 g_object_ref(compose->notebook);
10485 g_object_ref(compose->edit_vbox);
10486 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10487 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10488 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10489 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10490 g_object_unref(compose->notebook);
10491 g_object_unref(compose->edit_vbox);
10492 g_signal_handlers_block_by_func(G_OBJECT(widget),
10493 G_CALLBACK(compose_grab_focus_cb),
10495 gtk_widget_grab_focus(widget);
10496 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10497 G_CALLBACK(compose_grab_focus_cb),
10503 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10505 compose->modified = TRUE;
10506 // compose_beautify_paragraph(compose, NULL, TRUE);
10507 #ifndef GENERIC_UMPC
10508 compose_set_title(compose);
10512 static void compose_wrap_cb(GtkAction *action, gpointer data)
10514 Compose *compose = (Compose *)data;
10515 compose_beautify_paragraph(compose, NULL, TRUE);
10518 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10520 Compose *compose = (Compose *)data;
10521 compose_wrap_all_full(compose, TRUE);
10524 static void compose_find_cb(GtkAction *action, gpointer data)
10526 Compose *compose = (Compose *)data;
10528 message_search_compose(compose);
10531 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10534 Compose *compose = (Compose *)data;
10535 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10536 if (compose->autowrap)
10537 compose_wrap_all_full(compose, TRUE);
10538 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10541 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10544 Compose *compose = (Compose *)data;
10545 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10548 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10550 Compose *compose = (Compose *)data;
10552 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10555 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10557 Compose *compose = (Compose *)data;
10559 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10562 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10564 g_free(compose->privacy_system);
10566 compose->privacy_system = g_strdup(account->default_privacy_system);
10567 compose_update_privacy_system_menu_item(compose, warn);
10570 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10572 Compose *compose = (Compose *)data;
10574 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10575 gtk_widget_show(compose->ruler_hbox);
10576 prefs_common.show_ruler = TRUE;
10578 gtk_widget_hide(compose->ruler_hbox);
10579 gtk_widget_queue_resize(compose->edit_vbox);
10580 prefs_common.show_ruler = FALSE;
10584 static void compose_attach_drag_received_cb (GtkWidget *widget,
10585 GdkDragContext *context,
10588 GtkSelectionData *data,
10591 gpointer user_data)
10593 Compose *compose = (Compose *)user_data;
10597 type = gtk_selection_data_get_data_type(data);
10598 if (((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
10600 || (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND"))
10602 ) && gtk_drag_get_source_widget(context) !=
10603 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10604 list = uri_list_extract_filenames(
10605 (const gchar *)gtk_selection_data_get_data(data));
10606 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10607 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10608 compose_attach_append
10609 (compose, (const gchar *)tmp->data,
10610 utf8_filename, NULL, NULL);
10611 g_free(utf8_filename);
10613 if (list) compose_changed_cb(NULL, compose);
10614 list_free_strings(list);
10616 } else if (gtk_drag_get_source_widget(context)
10617 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10618 /* comes from our summaryview */
10619 SummaryView * summaryview = NULL;
10620 GSList * list = NULL, *cur = NULL;
10622 if (mainwindow_get_mainwindow())
10623 summaryview = mainwindow_get_mainwindow()->summaryview;
10626 list = summary_get_selected_msg_list(summaryview);
10628 for (cur = list; cur; cur = cur->next) {
10629 MsgInfo *msginfo = (MsgInfo *)cur->data;
10630 gchar *file = NULL;
10632 file = procmsg_get_message_file_full(msginfo,
10635 compose_attach_append(compose, (const gchar *)file,
10636 (const gchar *)file, "message/rfc822", NULL);
10640 g_slist_free(list);
10644 static gboolean compose_drag_drop(GtkWidget *widget,
10645 GdkDragContext *drag_context,
10647 guint time, gpointer user_data)
10649 /* not handling this signal makes compose_insert_drag_received_cb
10654 static void compose_insert_drag_received_cb (GtkWidget *widget,
10655 GdkDragContext *drag_context,
10658 GtkSelectionData *data,
10661 gpointer user_data)
10663 Compose *compose = (Compose *)user_data;
10667 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10669 type = gtk_selection_data_get_data_type(data);
10671 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
10673 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND")) {
10675 AlertValue val = G_ALERTDEFAULT;
10676 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
10678 list = uri_list_extract_filenames(ddata);
10679 if (list == NULL && strstr(ddata, "://")) {
10680 /* Assume a list of no files, and data has ://, is a remote link */
10681 gchar *tmpdata = g_strstrip(g_strdup(ddata));
10682 gchar *tmpfile = get_tmp_file();
10683 str_write_to_file(tmpdata, tmpfile);
10685 compose_insert_file(compose, tmpfile);
10686 claws_unlink(tmpfile);
10688 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10689 compose_beautify_paragraph(compose, NULL, TRUE);
10692 switch (prefs_common.compose_dnd_mode) {
10693 case COMPOSE_DND_ASK:
10694 val = alertpanel_full(_("Insert or attach?"),
10695 _("Do you want to insert the contents of the file(s) "
10696 "into the message body, or attach it to the email?"),
10697 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10698 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10700 case COMPOSE_DND_INSERT:
10701 val = G_ALERTALTERNATE;
10703 case COMPOSE_DND_ATTACH:
10704 val = G_ALERTOTHER;
10707 /* unexpected case */
10708 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10711 if (val & G_ALERTDISABLE) {
10712 val &= ~G_ALERTDISABLE;
10713 /* remember what action to perform by default, only if we don't click Cancel */
10714 if (val == G_ALERTALTERNATE)
10715 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10716 else if (val == G_ALERTOTHER)
10717 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10720 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10721 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10722 list_free_strings(list);
10725 } else if (val == G_ALERTOTHER) {
10726 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10727 list_free_strings(list);
10732 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10733 compose_insert_file(compose, (const gchar *)tmp->data);
10735 list_free_strings(list);
10737 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10742 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10745 static void compose_header_drag_received_cb (GtkWidget *widget,
10746 GdkDragContext *drag_context,
10749 GtkSelectionData *data,
10752 gpointer user_data)
10754 GtkEditable *entry = (GtkEditable *)user_data;
10755 const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
10757 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10760 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10761 gchar *decoded=g_new(gchar, strlen(email));
10764 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
10765 gtk_editable_delete_text(entry, 0, -1);
10766 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10767 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10771 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10774 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10776 Compose *compose = (Compose *)data;
10778 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10779 compose->return_receipt = TRUE;
10781 compose->return_receipt = FALSE;
10784 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10786 Compose *compose = (Compose *)data;
10788 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10789 compose->remove_references = TRUE;
10791 compose->remove_references = FALSE;
10794 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10795 ComposeHeaderEntry *headerentry)
10797 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10801 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10802 GdkEventKey *event,
10803 ComposeHeaderEntry *headerentry)
10805 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10806 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10807 !(event->state & GDK_MODIFIER_MASK) &&
10808 (event->keyval == GDK_KEY_BackSpace) &&
10809 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10810 gtk_container_remove
10811 (GTK_CONTAINER(headerentry->compose->header_table),
10812 headerentry->combo);
10813 gtk_container_remove
10814 (GTK_CONTAINER(headerentry->compose->header_table),
10815 headerentry->entry);
10816 headerentry->compose->header_list =
10817 g_slist_remove(headerentry->compose->header_list,
10819 g_free(headerentry);
10820 } else if (event->keyval == GDK_KEY_Tab) {
10821 if (headerentry->compose->header_last == headerentry) {
10822 /* Override default next focus, and give it to subject_entry
10823 * instead of notebook tabs
10825 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10826 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10833 static gboolean scroll_postpone(gpointer data)
10835 Compose *compose = (Compose *)data;
10837 cm_return_val_if_fail(!compose->batch, FALSE);
10839 GTK_EVENTS_FLUSH();
10840 compose_show_first_last_header(compose, FALSE);
10844 static void compose_headerentry_changed_cb(GtkWidget *entry,
10845 ComposeHeaderEntry *headerentry)
10847 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10848 compose_create_header_entry(headerentry->compose);
10849 g_signal_handlers_disconnect_matched
10850 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10851 0, 0, NULL, NULL, headerentry);
10853 if (!headerentry->compose->batch)
10854 g_timeout_add(0, scroll_postpone, headerentry->compose);
10858 static gboolean compose_defer_auto_save_draft(Compose *compose)
10860 compose->draft_timeout_tag = -1;
10861 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10865 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10867 GtkAdjustment *vadj;
10869 cm_return_if_fail(compose);
10870 cm_return_if_fail(!compose->batch);
10871 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10872 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
10873 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
10874 gtk_widget_get_parent(compose->header_table)));
10875 gtk_adjustment_set_value(vadj, (show_first ?
10876 gtk_adjustment_get_lower(vadj) :
10877 (gtk_adjustment_get_upper(vadj) -
10878 gtk_adjustment_get_page_size(vadj))));
10879 gtk_adjustment_changed(vadj);
10882 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10883 const gchar *text, gint len, Compose *compose)
10885 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10886 (G_OBJECT(compose->text), "paste_as_quotation"));
10889 cm_return_if_fail(text != NULL);
10891 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10892 G_CALLBACK(text_inserted),
10894 if (paste_as_quotation) {
10896 const gchar *qmark;
10898 GtkTextIter start_iter;
10901 len = strlen(text);
10903 new_text = g_strndup(text, len);
10905 qmark = compose_quote_char_from_context(compose);
10907 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10908 gtk_text_buffer_place_cursor(buffer, iter);
10910 pos = gtk_text_iter_get_offset(iter);
10912 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10913 _("Quote format error at line %d."));
10914 quote_fmt_reset_vartable();
10916 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10917 GINT_TO_POINTER(paste_as_quotation - 1));
10919 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10920 gtk_text_buffer_place_cursor(buffer, iter);
10921 gtk_text_buffer_delete_mark(buffer, mark);
10923 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10924 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10925 compose_beautify_paragraph(compose, &start_iter, FALSE);
10926 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10927 gtk_text_buffer_delete_mark(buffer, mark);
10929 if (strcmp(text, "\n") || compose->automatic_break
10930 || gtk_text_iter_starts_line(iter)) {
10931 GtkTextIter before_ins;
10932 gtk_text_buffer_insert(buffer, iter, text, len);
10933 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10934 before_ins = *iter;
10935 gtk_text_iter_backward_chars(&before_ins, len);
10936 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10939 /* check if the preceding is just whitespace or quote */
10940 GtkTextIter start_line;
10941 gchar *tmp = NULL, *quote = NULL;
10942 gint quote_len = 0, is_normal = 0;
10943 start_line = *iter;
10944 gtk_text_iter_set_line_offset(&start_line, 0);
10945 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10948 if (*tmp == '\0') {
10951 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10959 gtk_text_buffer_insert(buffer, iter, text, len);
10961 gtk_text_buffer_insert_with_tags_by_name(buffer,
10962 iter, text, len, "no_join", NULL);
10967 if (!paste_as_quotation) {
10968 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10969 compose_beautify_paragraph(compose, iter, FALSE);
10970 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10971 gtk_text_buffer_delete_mark(buffer, mark);
10974 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10975 G_CALLBACK(text_inserted),
10977 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10979 if (prefs_common.autosave &&
10980 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10981 compose->draft_timeout_tag != -2 /* disabled while loading */)
10982 compose->draft_timeout_tag = g_timeout_add
10983 (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
10987 static void compose_check_all(GtkAction *action, gpointer data)
10989 Compose *compose = (Compose *)data;
10990 if (!compose->gtkaspell)
10993 if (gtk_widget_has_focus(compose->subject_entry))
10994 claws_spell_entry_check_all(
10995 CLAWS_SPELL_ENTRY(compose->subject_entry));
10997 gtkaspell_check_all(compose->gtkaspell);
11000 static void compose_highlight_all(GtkAction *action, gpointer data)
11002 Compose *compose = (Compose *)data;
11003 if (compose->gtkaspell) {
11004 claws_spell_entry_recheck_all(
11005 CLAWS_SPELL_ENTRY(compose->subject_entry));
11006 gtkaspell_highlight_all(compose->gtkaspell);
11010 static void compose_check_backwards(GtkAction *action, gpointer data)
11012 Compose *compose = (Compose *)data;
11013 if (!compose->gtkaspell) {
11014 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11018 if (gtk_widget_has_focus(compose->subject_entry))
11019 claws_spell_entry_check_backwards(
11020 CLAWS_SPELL_ENTRY(compose->subject_entry));
11022 gtkaspell_check_backwards(compose->gtkaspell);
11025 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11027 Compose *compose = (Compose *)data;
11028 if (!compose->gtkaspell) {
11029 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11033 if (gtk_widget_has_focus(compose->subject_entry))
11034 claws_spell_entry_check_forwards_go(
11035 CLAWS_SPELL_ENTRY(compose->subject_entry));
11037 gtkaspell_check_forwards_go(compose->gtkaspell);
11042 *\brief Guess originating forward account from MsgInfo and several
11043 * "common preference" settings. Return NULL if no guess.
11045 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11047 PrefsAccount *account = NULL;
11049 cm_return_val_if_fail(msginfo, NULL);
11050 cm_return_val_if_fail(msginfo->folder, NULL);
11051 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11053 if (msginfo->folder->prefs->enable_default_account)
11054 account = account_find_from_id(msginfo->folder->prefs->default_account);
11057 account = msginfo->folder->folder->account;
11059 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11061 Xstrdup_a(to, msginfo->to, return NULL);
11062 extract_address(to);
11063 account = account_find_from_address(to, FALSE);
11066 if (!account && prefs_common.forward_account_autosel) {
11067 gchar cc[BUFFSIZE];
11068 if (!procheader_get_header_from_msginfo
11069 (msginfo, cc,sizeof cc , "Cc:")) {
11070 gchar *buf = cc + strlen("Cc:");
11071 extract_address(buf);
11072 account = account_find_from_address(buf, FALSE);
11076 if (!account && prefs_common.forward_account_autosel) {
11077 gchar deliveredto[BUFFSIZE];
11078 if (!procheader_get_header_from_msginfo
11079 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
11080 gchar *buf = deliveredto + strlen("Delivered-To:");
11081 extract_address(buf);
11082 account = account_find_from_address(buf, FALSE);
11089 gboolean compose_close(Compose *compose)
11093 if (!g_mutex_trylock(compose->mutex)) {
11094 /* we have to wait for the (possibly deferred by auto-save)
11095 * drafting to be done, before destroying the compose under
11097 debug_print("waiting for drafting to finish...\n");
11098 compose_allow_user_actions(compose, FALSE);
11099 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11102 cm_return_val_if_fail(compose, FALSE);
11103 gtkut_widget_get_uposition(compose->window, &x, &y);
11104 if (!compose->batch) {
11105 prefs_common.compose_x = x;
11106 prefs_common.compose_y = y;
11108 g_mutex_unlock(compose->mutex);
11109 compose_destroy(compose);
11114 * Add entry field for each address in list.
11115 * \param compose E-Mail composition object.
11116 * \param listAddress List of (formatted) E-Mail addresses.
11118 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11121 node = listAddress;
11123 addr = ( gchar * ) node->data;
11124 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11125 node = g_list_next( node );
11129 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11130 guint action, gboolean opening_multiple)
11132 gchar *body = NULL;
11133 GSList *new_msglist = NULL;
11134 MsgInfo *tmp_msginfo = NULL;
11135 gboolean originally_enc = FALSE;
11136 gboolean originally_sig = FALSE;
11137 Compose *compose = NULL;
11138 gchar *s_system = NULL;
11140 cm_return_if_fail(msgview != NULL);
11142 cm_return_if_fail(msginfo_list != NULL);
11144 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11145 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11146 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11148 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11149 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11150 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11151 orig_msginfo, mimeinfo);
11152 if (tmp_msginfo != NULL) {
11153 new_msglist = g_slist_append(NULL, tmp_msginfo);
11155 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11156 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11157 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11159 tmp_msginfo->folder = orig_msginfo->folder;
11160 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11161 if (orig_msginfo->tags) {
11162 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11163 tmp_msginfo->folder->tags_dirty = TRUE;
11169 if (!opening_multiple)
11170 body = messageview_get_selection(msgview);
11173 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11174 procmsg_msginfo_free(tmp_msginfo);
11175 g_slist_free(new_msglist);
11177 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11179 if (compose && originally_enc) {
11180 compose_force_encryption(compose, compose->account, FALSE, s_system);
11183 if (compose && originally_sig && compose->account->default_sign_reply) {
11184 compose_force_signing(compose, compose->account, s_system);
11188 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11191 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11194 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11195 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11196 GSList *cur = msginfo_list;
11197 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11198 "messages. Opening the windows "
11199 "could take some time. Do you "
11200 "want to continue?"),
11201 g_slist_length(msginfo_list));
11202 if (g_slist_length(msginfo_list) > 9
11203 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11204 != G_ALERTALTERNATE) {
11209 /* We'll open multiple compose windows */
11210 /* let the WM place the next windows */
11211 compose_force_window_origin = FALSE;
11212 for (; cur; cur = cur->next) {
11214 tmplist.data = cur->data;
11215 tmplist.next = NULL;
11216 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11218 compose_force_window_origin = TRUE;
11220 /* forwarding multiple mails as attachments is done via a
11221 * single compose window */
11222 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11226 void compose_check_for_email_account(Compose *compose)
11228 PrefsAccount *ac = NULL, *curr = NULL;
11234 if (compose->account && compose->account->protocol == A_NNTP) {
11235 ac = account_get_cur_account();
11236 if (ac->protocol == A_NNTP) {
11237 list = account_get_list();
11239 for( ; list != NULL ; list = g_list_next(list)) {
11240 curr = (PrefsAccount *) list->data;
11241 if (curr->protocol != A_NNTP) {
11247 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11252 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo,
11253 const gchar *address)
11255 GSList *msginfo_list = NULL;
11256 gchar *body = messageview_get_selection(msgview);
11259 msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11261 compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11262 compose_check_for_email_account(compose);
11263 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11264 compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11265 compose_reply_set_subject(compose, msginfo);
11268 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11271 void compose_set_position(Compose *compose, gint pos)
11273 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11275 gtkut_text_view_set_position(text, pos);
11278 gboolean compose_search_string(Compose *compose,
11279 const gchar *str, gboolean case_sens)
11281 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11283 return gtkut_text_view_search_string(text, str, case_sens);
11286 gboolean compose_search_string_backward(Compose *compose,
11287 const gchar *str, gboolean case_sens)
11289 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11291 return gtkut_text_view_search_string_backward(text, str, case_sens);
11294 /* allocate a msginfo structure and populate its data from a compose data structure */
11295 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11297 MsgInfo *newmsginfo;
11299 gchar buf[BUFFSIZE];
11301 cm_return_val_if_fail( compose != NULL, NULL );
11303 newmsginfo = procmsg_msginfo_new();
11306 get_rfc822_date(buf, sizeof(buf));
11307 newmsginfo->date = g_strdup(buf);
11310 if (compose->from_name) {
11311 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11312 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11316 if (compose->subject_entry)
11317 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11319 /* to, cc, reply-to, newsgroups */
11320 for (list = compose->header_list; list; list = list->next) {
11321 gchar *header = gtk_editable_get_chars(
11323 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11324 gchar *entry = gtk_editable_get_chars(
11325 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11327 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11328 if ( newmsginfo->to == NULL ) {
11329 newmsginfo->to = g_strdup(entry);
11330 } else if (entry && *entry) {
11331 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11332 g_free(newmsginfo->to);
11333 newmsginfo->to = tmp;
11336 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11337 if ( newmsginfo->cc == NULL ) {
11338 newmsginfo->cc = g_strdup(entry);
11339 } else if (entry && *entry) {
11340 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11341 g_free(newmsginfo->cc);
11342 newmsginfo->cc = tmp;
11345 if ( strcasecmp(header,
11346 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11347 if ( newmsginfo->newsgroups == NULL ) {
11348 newmsginfo->newsgroups = g_strdup(entry);
11349 } else if (entry && *entry) {
11350 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11351 g_free(newmsginfo->newsgroups);
11352 newmsginfo->newsgroups = tmp;
11360 /* other data is unset */
11366 /* update compose's dictionaries from folder dict settings */
11367 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11368 FolderItem *folder_item)
11370 cm_return_if_fail(compose != NULL);
11372 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11373 FolderItemPrefs *prefs = folder_item->prefs;
11375 if (prefs->enable_default_dictionary)
11376 gtkaspell_change_dict(compose->gtkaspell,
11377 prefs->default_dictionary, FALSE);
11378 if (folder_item->prefs->enable_default_alt_dictionary)
11379 gtkaspell_change_alt_dict(compose->gtkaspell,
11380 prefs->default_alt_dictionary);
11381 if (prefs->enable_default_dictionary
11382 || prefs->enable_default_alt_dictionary)
11383 compose_spell_menu_changed(compose);