2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2012 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/>.
22 #include "claws-features.h"
27 #ifndef PANGO_ENABLE_ENGINE
28 # define PANGO_ENABLE_ENGINE
32 #include <glib/gi18n.h>
33 #include <gdk/gdkkeysyms.h>
36 #include <pango/pango-break.h>
41 #include <sys/types.h>
47 # include <sys/wait.h>
51 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
55 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
62 #include "mainwindow.h"
64 #ifndef USE_NEW_ADDRBOOK
65 #include "addressbook.h"
67 #include "addressbook-dbus.h"
68 #include "addressadd.h"
70 #include "folderview.h"
73 #include "stock_pixmap.h"
74 #include "send_message.h"
77 #include "customheader.h"
78 #include "prefs_common.h"
79 #include "prefs_account.h"
83 #include "procheader.h"
85 #include "statusbar.h"
88 #include "quoted-printable.h"
92 #include "gtkshruler.h"
94 #include "alertpanel.h"
95 #include "manage_window.h"
97 #include "addr_compl.h"
98 #include "quote_fmt.h"
100 #include "foldersel.h"
103 #include "message_search.h"
104 #include "combobox.h"
108 #include "autofaces.h"
109 #include "spell_entry.h"
122 #define N_ATTACH_COLS (N_COL_COLUMNS)
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
132 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
134 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
135 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
136 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
137 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
138 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
139 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
140 } ComposeCallAdvancedAction;
144 PRIORITY_HIGHEST = 1,
153 COMPOSE_INSERT_SUCCESS,
154 COMPOSE_INSERT_READ_ERROR,
155 COMPOSE_INSERT_INVALID_CHARACTER,
156 COMPOSE_INSERT_NO_FILE
157 } ComposeInsertResult;
161 COMPOSE_WRITE_FOR_SEND,
162 COMPOSE_WRITE_FOR_STORE
167 COMPOSE_QUOTE_FORCED,
174 SUBJECT_FIELD_PRESENT,
179 #define B64_LINE_SIZE 57
180 #define B64_BUFFSIZE 77
182 #define MAX_REFERENCES_LEN 999
184 static GList *compose_list = NULL;
186 static Compose *compose_generic_new (PrefsAccount *account,
190 GList *listAddress );
192 static Compose *compose_create (PrefsAccount *account,
197 static void compose_entry_mark_default_to (Compose *compose,
198 const gchar *address);
199 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
200 ComposeQuoteMode quote_mode,
204 static Compose *compose_forward_multiple (PrefsAccount *account,
205 GSList *msginfo_list);
206 static Compose *compose_reply (MsgInfo *msginfo,
207 ComposeQuoteMode quote_mode,
212 static Compose *compose_reply_mode (ComposeMode mode,
213 GSList *msginfo_list,
215 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
216 static void compose_update_privacy_systems_menu(Compose *compose);
218 static GtkWidget *compose_account_option_menu_create
220 static void compose_set_out_encoding (Compose *compose);
221 static void compose_set_template_menu (Compose *compose);
222 static void compose_destroy (Compose *compose);
224 static MailField compose_entries_set (Compose *compose,
226 ComposeEntryType to_type);
227 static gint compose_parse_header (Compose *compose,
229 static gint compose_parse_manual_headers (Compose *compose,
231 HeaderEntry *entries);
232 static gchar *compose_parse_references (const gchar *ref,
235 static gchar *compose_quote_fmt (Compose *compose,
241 gboolean need_unescape,
242 const gchar *err_msg);
244 static void compose_reply_set_entry (Compose *compose,
250 followup_and_reply_to);
251 static void compose_reedit_set_entry (Compose *compose,
254 static void compose_insert_sig (Compose *compose,
256 static ComposeInsertResult compose_insert_file (Compose *compose,
259 static gboolean compose_attach_append (Compose *compose,
262 const gchar *content_type,
263 const gchar *charset);
264 static void compose_attach_parts (Compose *compose,
267 static gboolean compose_beautify_paragraph (Compose *compose,
268 GtkTextIter *par_iter,
270 static void compose_wrap_all (Compose *compose);
271 static void compose_wrap_all_full (Compose *compose,
274 static void compose_set_title (Compose *compose);
275 static void compose_select_account (Compose *compose,
276 PrefsAccount *account,
279 static PrefsAccount *compose_current_mail_account(void);
280 /* static gint compose_send (Compose *compose); */
281 static gboolean compose_check_for_valid_recipient
283 static gboolean compose_check_entries (Compose *compose,
284 gboolean check_everything);
285 static gint compose_write_to_file (Compose *compose,
288 gboolean attach_parts);
289 static gint compose_write_body_to_file (Compose *compose,
291 static gint compose_remove_reedit_target (Compose *compose,
293 static void compose_remove_draft (Compose *compose);
294 static gint compose_queue_sub (Compose *compose,
298 gboolean check_subject,
299 gboolean remove_reedit_target);
300 static int compose_add_attachments (Compose *compose,
302 static gchar *compose_get_header (Compose *compose);
303 static gchar *compose_get_manual_headers_info (Compose *compose);
305 static void compose_convert_header (Compose *compose,
310 gboolean addr_field);
312 static void compose_attach_info_free (AttachInfo *ainfo);
313 static void compose_attach_remove_selected (GtkAction *action,
316 static void compose_template_apply (Compose *compose,
319 static void compose_attach_property (GtkAction *action,
321 static void compose_attach_property_create (gboolean *cancelled);
322 static void attach_property_ok (GtkWidget *widget,
323 gboolean *cancelled);
324 static void attach_property_cancel (GtkWidget *widget,
325 gboolean *cancelled);
326 static gint attach_property_delete_event (GtkWidget *widget,
328 gboolean *cancelled);
329 static gboolean attach_property_key_pressed (GtkWidget *widget,
331 gboolean *cancelled);
333 static void compose_exec_ext_editor (Compose *compose);
335 static gint compose_exec_ext_editor_real (const gchar *file);
336 static gboolean compose_ext_editor_kill (Compose *compose);
337 static gboolean compose_input_cb (GIOChannel *source,
338 GIOCondition condition,
340 static void compose_set_ext_editor_sensitive (Compose *compose,
342 #endif /* G_OS_UNIX */
344 static void compose_undo_state_changed (UndoMain *undostruct,
349 static void compose_create_header_entry (Compose *compose);
350 static void compose_add_header_entry (Compose *compose, const gchar *header,
351 gchar *text, ComposePrefType pref_type);
352 static void compose_remove_header_entries(Compose *compose);
354 static void compose_update_priority_menu_item(Compose * compose);
356 static void compose_spell_menu_changed (void *data);
357 static void compose_dict_changed (void *data);
359 static void compose_add_field_list ( Compose *compose,
360 GList *listAddress );
362 /* callback functions */
364 static void compose_notebook_size_alloc (GtkNotebook *notebook,
365 GtkAllocation *allocation,
367 static gboolean compose_edit_size_alloc (GtkEditable *widget,
368 GtkAllocation *allocation,
369 GtkSHRuler *shruler);
370 static void account_activated (GtkComboBox *optmenu,
372 static void attach_selected (GtkTreeView *tree_view,
373 GtkTreePath *tree_path,
374 GtkTreeViewColumn *column,
376 static gboolean attach_button_pressed (GtkWidget *widget,
377 GdkEventButton *event,
379 static gboolean attach_key_pressed (GtkWidget *widget,
382 static void compose_send_cb (GtkAction *action, gpointer data);
383 static void compose_send_later_cb (GtkAction *action, gpointer data);
385 static void compose_save_cb (GtkAction *action,
388 static void compose_attach_cb (GtkAction *action,
390 static void compose_insert_file_cb (GtkAction *action,
392 static void compose_insert_sig_cb (GtkAction *action,
395 static void compose_close_cb (GtkAction *action,
397 static void compose_print_cb (GtkAction *action,
400 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
402 static void compose_address_cb (GtkAction *action,
404 static void about_show_cb (GtkAction *action,
406 static void compose_template_activate_cb(GtkWidget *widget,
409 static void compose_ext_editor_cb (GtkAction *action,
412 static gint compose_delete_cb (GtkWidget *widget,
416 static void compose_undo_cb (GtkAction *action,
418 static void compose_redo_cb (GtkAction *action,
420 static void compose_cut_cb (GtkAction *action,
422 static void compose_copy_cb (GtkAction *action,
424 static void compose_paste_cb (GtkAction *action,
426 static void compose_paste_as_quote_cb (GtkAction *action,
428 static void compose_paste_no_wrap_cb (GtkAction *action,
430 static void compose_paste_wrap_cb (GtkAction *action,
432 static void compose_allsel_cb (GtkAction *action,
435 static void compose_advanced_action_cb (GtkAction *action,
438 static void compose_grab_focus_cb (GtkWidget *widget,
441 static void compose_changed_cb (GtkTextBuffer *textbuf,
444 static void compose_wrap_cb (GtkAction *action,
446 static void compose_wrap_all_cb (GtkAction *action,
448 static void compose_find_cb (GtkAction *action,
450 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
452 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
455 static void compose_toggle_ruler_cb (GtkToggleAction *action,
457 static void compose_toggle_sign_cb (GtkToggleAction *action,
459 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
461 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
462 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
463 static void activate_privacy_system (Compose *compose,
464 PrefsAccount *account,
466 static void compose_use_signing(Compose *compose, gboolean use_signing);
467 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
468 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
470 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
472 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
473 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
474 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
476 static void compose_attach_drag_received_cb (GtkWidget *widget,
477 GdkDragContext *drag_context,
480 GtkSelectionData *data,
484 static void compose_insert_drag_received_cb (GtkWidget *widget,
485 GdkDragContext *drag_context,
488 GtkSelectionData *data,
492 static void compose_header_drag_received_cb (GtkWidget *widget,
493 GdkDragContext *drag_context,
496 GtkSelectionData *data,
501 static gboolean compose_drag_drop (GtkWidget *widget,
502 GdkDragContext *drag_context,
504 guint time, gpointer user_data);
505 static gboolean completion_set_focus_to_subject
510 static void text_inserted (GtkTextBuffer *buffer,
515 static Compose *compose_generic_reply(MsgInfo *msginfo,
516 ComposeQuoteMode quote_mode,
520 gboolean followup_and_reply_to,
523 static void compose_headerentry_changed_cb (GtkWidget *entry,
524 ComposeHeaderEntry *headerentry);
525 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
527 ComposeHeaderEntry *headerentry);
528 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
529 ComposeHeaderEntry *headerentry);
531 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
533 static void compose_allow_user_actions (Compose *compose, gboolean allow);
535 static void compose_nothing_cb (GtkAction *action, gpointer data)
541 static void compose_check_all (GtkAction *action, gpointer data);
542 static void compose_highlight_all (GtkAction *action, gpointer data);
543 static void compose_check_backwards (GtkAction *action, gpointer data);
544 static void compose_check_forwards_go (GtkAction *action, gpointer data);
547 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
549 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
552 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
553 FolderItem *folder_item);
555 static void compose_attach_update_label(Compose *compose);
556 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
557 gboolean respect_default_to);
559 static GtkActionEntry compose_popup_entries[] =
561 {"Compose", NULL, "Compose" },
562 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
563 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
564 {"Compose/---", NULL, "---", NULL, NULL, NULL },
565 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
568 static GtkActionEntry compose_entries[] =
570 {"Menu", NULL, "Menu" },
572 {"Message", NULL, N_("_Message") },
573 {"Edit", NULL, N_("_Edit") },
575 {"Spelling", NULL, N_("_Spelling") },
577 {"Options", NULL, N_("_Options") },
578 {"Tools", NULL, N_("_Tools") },
579 {"Help", NULL, N_("_Help") },
581 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
582 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
583 {"Message/---", NULL, "---" },
585 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
586 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
587 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
588 /* {"Message/---", NULL, "---" }, */
589 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
590 /* {"Message/---", NULL, "---" }, */
591 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
592 /* {"Message/---", NULL, "---" }, */
593 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
596 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
597 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
598 {"Edit/---", NULL, "---" },
600 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
601 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
602 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
604 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
605 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
606 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
607 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
609 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
611 {"Edit/Advanced", NULL, N_("A_dvanced") },
612 {"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*/
613 {"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*/
614 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
615 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
616 {"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*/
617 {"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*/
618 {"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*/
619 {"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*/
620 {"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*/
621 {"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*/
622 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
623 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
624 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
625 {"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*/
627 /* {"Edit/---", NULL, "---" }, */
628 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
630 /* {"Edit/---", NULL, "---" }, */
631 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
632 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
633 /* {"Edit/---", NULL, "---" }, */
634 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
637 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
638 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
639 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
640 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
642 {"Spelling/---", NULL, "---" },
643 {"Spelling/Options", NULL, N_("_Options") },
648 {"Options/ReplyMode", NULL, N_("Reply _mode") },
649 {"Options/---", NULL, "---" },
650 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
651 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
653 /* {"Options/---", NULL, "---" }, */
655 {"Options/Priority", NULL, N_("_Priority") },
657 {"Options/Encoding", NULL, N_("Character _encoding") },
658 {"Options/Encoding/---", NULL, "---" },
659 #define ENC_ACTION(cs_char,c_char,string) \
660 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
662 {"Options/Encoding/Western", NULL, N_("Western European") },
663 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
664 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
665 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
666 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
667 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
668 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
669 {"Options/Encoding/Korean", NULL, N_("Korean") },
670 {"Options/Encoding/Thai", NULL, N_("Thai") },
673 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
675 {"Tools/Template", NULL, N_("_Template") },
676 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
677 {"Tools/Actions", NULL, N_("Actio_ns") },
678 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
681 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
684 static GtkToggleActionEntry compose_toggle_entries[] =
686 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
687 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
688 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
689 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
690 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
691 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
692 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
695 static GtkRadioActionEntry compose_radio_rm_entries[] =
697 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
698 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
699 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
700 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
703 static GtkRadioActionEntry compose_radio_prio_entries[] =
705 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
706 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
707 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
708 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
709 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
712 static GtkRadioActionEntry compose_radio_enc_entries[] =
714 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
729 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
730 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
731 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
732 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
733 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
734 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
735 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
736 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
737 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
738 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
739 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
740 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
741 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
742 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
743 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
744 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
745 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
748 static GtkTargetEntry compose_mime_types[] =
750 {"text/uri-list", 0, 0},
751 {"UTF8_STRING", 0, 0},
755 static gboolean compose_put_existing_to_front(MsgInfo *info)
757 GList *compose_list = compose_get_compose_list();
761 for (elem = compose_list; elem != NULL && elem->data != NULL;
763 Compose *c = (Compose*)elem->data;
765 if (!c->targetinfo || !c->targetinfo->msgid ||
769 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
770 gtkut_window_popup(c->window);
778 static GdkColor quote_color1 =
779 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
780 static GdkColor quote_color2 =
781 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
782 static GdkColor quote_color3 =
783 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
785 static GdkColor quote_bgcolor1 =
786 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
787 static GdkColor quote_bgcolor2 =
788 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
789 static GdkColor quote_bgcolor3 =
790 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
792 static GdkColor signature_color = {
799 static GdkColor uri_color = {
806 static void compose_create_tags(GtkTextView *text, Compose *compose)
808 GtkTextBuffer *buffer;
809 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
810 #if !GTK_CHECK_VERSION(2, 24, 0)
817 buffer = gtk_text_view_get_buffer(text);
819 if (prefs_common.enable_color) {
820 /* grab the quote colors, converting from an int to a GdkColor */
821 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
823 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
825 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
827 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
829 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
831 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
833 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
835 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
838 signature_color = quote_color1 = quote_color2 = quote_color3 =
839 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
842 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
843 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
844 "foreground-gdk", "e_color1,
845 "paragraph-background-gdk", "e_bgcolor1,
847 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
848 "foreground-gdk", "e_color2,
849 "paragraph-background-gdk", "e_bgcolor2,
851 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
852 "foreground-gdk", "e_color3,
853 "paragraph-background-gdk", "e_bgcolor3,
856 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
857 "foreground-gdk", "e_color1,
859 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
860 "foreground-gdk", "e_color2,
862 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
863 "foreground-gdk", "e_color3,
867 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
868 "foreground-gdk", &signature_color,
871 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
872 "foreground-gdk", &uri_color,
874 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
875 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
877 color[0] = quote_color1;
878 color[1] = quote_color2;
879 color[2] = quote_color3;
880 color[3] = quote_bgcolor1;
881 color[4] = quote_bgcolor2;
882 color[5] = quote_bgcolor3;
883 color[6] = signature_color;
884 color[7] = uri_color;
885 #if !GTK_CHECK_VERSION(2, 24, 0)
886 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
887 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
889 for (i = 0; i < 8; i++) {
890 if (success[i] == FALSE) {
893 g_warning("Compose: color allocation failed.\n");
894 style = gtk_widget_get_style(GTK_WIDGET(text));
895 quote_color1 = quote_color2 = quote_color3 =
896 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
897 signature_color = uri_color = black;
903 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
906 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
909 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
911 return compose_generic_new(account, mailto, item, NULL, NULL);
914 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
916 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
919 #define SCROLL_TO_CURSOR(compose) { \
920 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
921 gtk_text_view_get_buffer( \
922 GTK_TEXT_VIEW(compose->text))); \
923 gtk_text_view_scroll_mark_onscreen( \
924 GTK_TEXT_VIEW(compose->text), \
928 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
931 if (folderidentifier) {
932 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
933 prefs_common.compose_save_to_history = add_history(
934 prefs_common.compose_save_to_history, folderidentifier);
935 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
936 prefs_common.compose_save_to_history);
939 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
940 if (folderidentifier)
941 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
943 gtk_entry_set_text(GTK_ENTRY(entry), "");
946 static gchar *compose_get_save_to(Compose *compose)
949 gchar *result = NULL;
950 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
951 result = gtk_editable_get_chars(entry, 0, -1);
954 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
955 prefs_common.compose_save_to_history = add_history(
956 prefs_common.compose_save_to_history, result);
957 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
958 prefs_common.compose_save_to_history);
963 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
964 GList *attach_files, GList *listAddress )
967 GtkTextView *textview;
968 GtkTextBuffer *textbuf;
970 const gchar *subject_format = NULL;
971 const gchar *body_format = NULL;
972 gchar *mailto_from = NULL;
973 PrefsAccount *mailto_account = NULL;
974 MsgInfo* dummyinfo = NULL;
975 gint cursor_pos = -1;
976 MailField mfield = NO_FIELD_PRESENT;
980 /* check if mailto defines a from */
981 if (mailto && *mailto != '\0') {
982 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
983 /* mailto defines a from, check if we can get account prefs from it,
984 if not, the account prefs will be guessed using other ways, but we'll keep
987 mailto_account = account_find_from_address(mailto_from, TRUE);
989 account = mailto_account;
992 /* if no account prefs set from mailto, set if from folder prefs (if any) */
993 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
994 account = account_find_from_id(item->prefs->default_account);
996 /* if no account prefs set, fallback to the current one */
997 if (!account) account = cur_account;
998 cm_return_val_if_fail(account != NULL, NULL);
1000 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1002 /* override from name if mailto asked for it */
1004 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1005 g_free(mailto_from);
1007 /* override from name according to folder properties */
1008 if (item && item->prefs &&
1009 item->prefs->compose_with_format &&
1010 item->prefs->compose_override_from_format &&
1011 *item->prefs->compose_override_from_format != '\0') {
1016 dummyinfo = compose_msginfo_new_from_compose(compose);
1018 /* decode \-escape sequences in the internal representation of the quote format */
1019 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1020 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1023 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1024 compose->gtkaspell);
1026 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1028 quote_fmt_scan_string(tmp);
1031 buf = quote_fmt_get_buffer();
1033 alertpanel_error(_("New message From format error."));
1035 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1036 quote_fmt_reset_vartable();
1041 compose->replyinfo = NULL;
1042 compose->fwdinfo = NULL;
1044 textview = GTK_TEXT_VIEW(compose->text);
1045 textbuf = gtk_text_view_get_buffer(textview);
1046 compose_create_tags(textview, compose);
1048 undo_block(compose->undostruct);
1050 compose_set_dictionaries_from_folder_prefs(compose, item);
1053 if (account->auto_sig)
1054 compose_insert_sig(compose, FALSE);
1055 gtk_text_buffer_get_start_iter(textbuf, &iter);
1056 gtk_text_buffer_place_cursor(textbuf, &iter);
1058 if (account->protocol != A_NNTP) {
1059 if (mailto && *mailto != '\0') {
1060 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1063 compose_set_folder_prefs(compose, item, TRUE);
1065 if (item && item->ret_rcpt) {
1066 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1069 if (mailto && *mailto != '\0') {
1070 if (!strchr(mailto, '@'))
1071 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1073 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1074 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1075 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1076 mfield = TO_FIELD_PRESENT;
1079 * CLAWS: just don't allow return receipt request, even if the user
1080 * may want to send an email. simple but foolproof.
1082 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1084 compose_add_field_list( compose, listAddress );
1086 if (item && item->prefs && item->prefs->compose_with_format) {
1087 subject_format = item->prefs->compose_subject_format;
1088 body_format = item->prefs->compose_body_format;
1089 } else if (account->compose_with_format) {
1090 subject_format = account->compose_subject_format;
1091 body_format = account->compose_body_format;
1092 } else if (prefs_common.compose_with_format) {
1093 subject_format = prefs_common.compose_subject_format;
1094 body_format = prefs_common.compose_body_format;
1097 if (subject_format || body_format) {
1100 && *subject_format != '\0' )
1102 gchar *subject = NULL;
1107 dummyinfo = compose_msginfo_new_from_compose(compose);
1109 /* decode \-escape sequences in the internal representation of the quote format */
1110 tmp = g_malloc(strlen(subject_format)+1);
1111 pref_get_unescaped_pref(tmp, subject_format);
1113 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1115 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1116 compose->gtkaspell);
1118 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1120 quote_fmt_scan_string(tmp);
1123 buf = quote_fmt_get_buffer();
1125 alertpanel_error(_("New message subject format error."));
1127 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1128 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1129 quote_fmt_reset_vartable();
1133 mfield = SUBJECT_FIELD_PRESENT;
1137 && *body_format != '\0' )
1140 GtkTextBuffer *buffer;
1141 GtkTextIter start, end;
1145 dummyinfo = compose_msginfo_new_from_compose(compose);
1147 text = GTK_TEXT_VIEW(compose->text);
1148 buffer = gtk_text_view_get_buffer(text);
1149 gtk_text_buffer_get_start_iter(buffer, &start);
1150 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1151 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1153 compose_quote_fmt(compose, dummyinfo,
1155 NULL, tmp, FALSE, TRUE,
1156 _("The body of the \"New message\" template has an error at line %d."));
1157 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1158 quote_fmt_reset_vartable();
1162 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1163 gtkaspell_highlight_all(compose->gtkaspell);
1165 mfield = BODY_FIELD_PRESENT;
1169 procmsg_msginfo_free( dummyinfo );
1175 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1176 ainfo = (AttachInfo *) curr->data;
1177 compose_attach_append(compose, ainfo->file, ainfo->name,
1178 ainfo->content_type, ainfo->charset);
1182 compose_show_first_last_header(compose, TRUE);
1184 /* Set save folder */
1185 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1186 gchar *folderidentifier;
1188 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1189 folderidentifier = folder_item_get_identifier(item);
1190 compose_set_save_to(compose, folderidentifier);
1191 g_free(folderidentifier);
1194 /* Place cursor according to provided input (mfield) */
1196 case NO_FIELD_PRESENT:
1197 if (compose->header_last)
1198 gtk_widget_grab_focus(compose->header_last->entry);
1200 case TO_FIELD_PRESENT:
1201 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1203 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1206 gtk_widget_grab_focus(compose->subject_entry);
1208 case SUBJECT_FIELD_PRESENT:
1209 textview = GTK_TEXT_VIEW(compose->text);
1212 textbuf = gtk_text_view_get_buffer(textview);
1215 mark = gtk_text_buffer_get_insert(textbuf);
1216 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1217 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1219 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1220 * only defers where it comes to the variable body
1221 * is not null. If no body is present compose->text
1222 * will be null in which case you cannot place the
1223 * cursor inside the component so. An empty component
1224 * is therefore created before placing the cursor
1226 case BODY_FIELD_PRESENT:
1227 cursor_pos = quote_fmt_get_cursor_pos();
1228 if (cursor_pos == -1)
1229 gtk_widget_grab_focus(compose->header_last->entry);
1231 gtk_widget_grab_focus(compose->text);
1235 undo_unblock(compose->undostruct);
1237 if (prefs_common.auto_exteditor)
1238 compose_exec_ext_editor(compose);
1240 compose->draft_timeout_tag = -1;
1241 SCROLL_TO_CURSOR(compose);
1243 compose->modified = FALSE;
1244 compose_set_title(compose);
1246 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1251 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1252 gboolean override_pref, const gchar *system)
1254 const gchar *privacy = NULL;
1256 cm_return_if_fail(compose != NULL);
1257 cm_return_if_fail(account != NULL);
1259 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1264 else if (account->default_privacy_system
1265 && strlen(account->default_privacy_system)) {
1266 privacy = account->default_privacy_system;
1268 GSList *privacy_avail = privacy_get_system_ids();
1269 if (privacy_avail && g_slist_length(privacy_avail)) {
1270 privacy = (gchar *)(privacy_avail->data);
1273 if (privacy != NULL) {
1275 g_free(compose->privacy_system);
1276 compose->privacy_system = NULL;
1278 if (compose->privacy_system == NULL)
1279 compose->privacy_system = g_strdup(privacy);
1280 else if (*(compose->privacy_system) == '\0') {
1281 g_free(compose->privacy_system);
1282 compose->privacy_system = g_strdup(privacy);
1284 compose_update_privacy_system_menu_item(compose, FALSE);
1285 compose_use_encryption(compose, TRUE);
1289 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1291 const gchar *privacy = NULL;
1295 else if (account->default_privacy_system
1296 && strlen(account->default_privacy_system)) {
1297 privacy = account->default_privacy_system;
1299 GSList *privacy_avail = privacy_get_system_ids();
1300 if (privacy_avail && g_slist_length(privacy_avail)) {
1301 privacy = (gchar *)(privacy_avail->data);
1305 if (privacy != NULL) {
1307 g_free(compose->privacy_system);
1308 compose->privacy_system = NULL;
1310 if (compose->privacy_system == NULL)
1311 compose->privacy_system = g_strdup(privacy);
1312 compose_update_privacy_system_menu_item(compose, FALSE);
1313 compose_use_signing(compose, TRUE);
1317 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1321 Compose *compose = NULL;
1323 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1325 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1326 cm_return_val_if_fail(msginfo != NULL, NULL);
1328 list_len = g_slist_length(msginfo_list);
1332 case COMPOSE_REPLY_TO_ADDRESS:
1333 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1334 FALSE, prefs_common.default_reply_list, FALSE, body);
1336 case COMPOSE_REPLY_WITH_QUOTE:
1337 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1338 FALSE, prefs_common.default_reply_list, FALSE, body);
1340 case COMPOSE_REPLY_WITHOUT_QUOTE:
1341 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1342 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1344 case COMPOSE_REPLY_TO_SENDER:
1345 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1346 FALSE, FALSE, TRUE, body);
1348 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1349 compose = compose_followup_and_reply_to(msginfo,
1350 COMPOSE_QUOTE_CHECK,
1351 FALSE, FALSE, body);
1353 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1354 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1355 FALSE, FALSE, TRUE, body);
1357 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1358 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1359 FALSE, FALSE, TRUE, NULL);
1361 case COMPOSE_REPLY_TO_ALL:
1362 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1363 TRUE, FALSE, FALSE, body);
1365 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1366 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1367 TRUE, FALSE, FALSE, body);
1369 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1370 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1371 TRUE, FALSE, FALSE, NULL);
1373 case COMPOSE_REPLY_TO_LIST:
1374 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1375 FALSE, TRUE, FALSE, body);
1377 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1378 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1379 FALSE, TRUE, FALSE, body);
1381 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1382 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1383 FALSE, TRUE, FALSE, NULL);
1385 case COMPOSE_FORWARD:
1386 if (prefs_common.forward_as_attachment) {
1387 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1390 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1394 case COMPOSE_FORWARD_INLINE:
1395 /* check if we reply to more than one Message */
1396 if (list_len == 1) {
1397 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1400 /* more messages FALL THROUGH */
1401 case COMPOSE_FORWARD_AS_ATTACH:
1402 compose = compose_forward_multiple(NULL, msginfo_list);
1404 case COMPOSE_REDIRECT:
1405 compose = compose_redirect(NULL, msginfo, FALSE);
1408 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1411 if (compose == NULL) {
1412 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1416 compose->rmode = mode;
1417 switch (compose->rmode) {
1419 case COMPOSE_REPLY_WITH_QUOTE:
1420 case COMPOSE_REPLY_WITHOUT_QUOTE:
1421 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1422 debug_print("reply mode Normal\n");
1423 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1424 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1426 case COMPOSE_REPLY_TO_SENDER:
1427 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1428 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1429 debug_print("reply mode Sender\n");
1430 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1432 case COMPOSE_REPLY_TO_ALL:
1433 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1434 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1435 debug_print("reply mode All\n");
1436 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1438 case COMPOSE_REPLY_TO_LIST:
1439 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1440 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1441 debug_print("reply mode List\n");
1442 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1444 case COMPOSE_REPLY_TO_ADDRESS:
1445 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1453 static Compose *compose_reply(MsgInfo *msginfo,
1454 ComposeQuoteMode quote_mode,
1460 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1461 to_sender, FALSE, body);
1464 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1465 ComposeQuoteMode quote_mode,
1470 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1471 to_sender, TRUE, body);
1474 static void compose_extract_original_charset(Compose *compose)
1476 MsgInfo *info = NULL;
1477 if (compose->replyinfo) {
1478 info = compose->replyinfo;
1479 } else if (compose->fwdinfo) {
1480 info = compose->fwdinfo;
1481 } else if (compose->targetinfo) {
1482 info = compose->targetinfo;
1485 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1486 MimeInfo *partinfo = mimeinfo;
1487 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1488 partinfo = procmime_mimeinfo_next(partinfo);
1490 compose->orig_charset =
1491 g_strdup(procmime_mimeinfo_get_parameter(
1492 partinfo, "charset"));
1494 procmime_mimeinfo_free_all(mimeinfo);
1498 #define SIGNAL_BLOCK(buffer) { \
1499 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1500 G_CALLBACK(compose_changed_cb), \
1502 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1503 G_CALLBACK(text_inserted), \
1507 #define SIGNAL_UNBLOCK(buffer) { \
1508 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1509 G_CALLBACK(compose_changed_cb), \
1511 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1512 G_CALLBACK(text_inserted), \
1516 static Compose *compose_generic_reply(MsgInfo *msginfo,
1517 ComposeQuoteMode quote_mode,
1518 gboolean to_all, gboolean to_ml,
1520 gboolean followup_and_reply_to,
1524 PrefsAccount *account = NULL;
1525 GtkTextView *textview;
1526 GtkTextBuffer *textbuf;
1527 gboolean quote = FALSE;
1528 const gchar *qmark = NULL;
1529 const gchar *body_fmt = NULL;
1530 gchar *s_system = NULL;
1532 cm_return_val_if_fail(msginfo != NULL, NULL);
1533 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1535 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1537 cm_return_val_if_fail(account != NULL, NULL);
1539 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1541 compose->updating = TRUE;
1543 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1544 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1546 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1547 if (!compose->replyinfo)
1548 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1550 compose_extract_original_charset(compose);
1552 if (msginfo->folder && msginfo->folder->ret_rcpt)
1553 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1555 /* Set save folder */
1556 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1557 gchar *folderidentifier;
1559 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1560 folderidentifier = folder_item_get_identifier(msginfo->folder);
1561 compose_set_save_to(compose, folderidentifier);
1562 g_free(folderidentifier);
1565 if (compose_parse_header(compose, msginfo) < 0) {
1566 compose->updating = FALSE;
1567 compose_destroy(compose);
1571 /* override from name according to folder properties */
1572 if (msginfo->folder && msginfo->folder->prefs &&
1573 msginfo->folder->prefs->reply_with_format &&
1574 msginfo->folder->prefs->reply_override_from_format &&
1575 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1580 /* decode \-escape sequences in the internal representation of the quote format */
1581 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1582 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1585 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1586 compose->gtkaspell);
1588 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1590 quote_fmt_scan_string(tmp);
1593 buf = quote_fmt_get_buffer();
1595 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1597 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1598 quote_fmt_reset_vartable();
1603 textview = (GTK_TEXT_VIEW(compose->text));
1604 textbuf = gtk_text_view_get_buffer(textview);
1605 compose_create_tags(textview, compose);
1607 undo_block(compose->undostruct);
1609 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1610 gtkaspell_block_check(compose->gtkaspell);
1613 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1614 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1615 /* use the reply format of folder (if enabled), or the account's one
1616 (if enabled) or fallback to the global reply format, which is always
1617 enabled (even if empty), and use the relevant quotemark */
1619 if (msginfo->folder && msginfo->folder->prefs &&
1620 msginfo->folder->prefs->reply_with_format) {
1621 qmark = msginfo->folder->prefs->reply_quotemark;
1622 body_fmt = msginfo->folder->prefs->reply_body_format;
1624 } else if (account->reply_with_format) {
1625 qmark = account->reply_quotemark;
1626 body_fmt = account->reply_body_format;
1629 qmark = prefs_common.quotemark;
1630 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1631 body_fmt = gettext(prefs_common.quotefmt);
1638 /* empty quotemark is not allowed */
1639 if (qmark == NULL || *qmark == '\0')
1641 compose_quote_fmt(compose, compose->replyinfo,
1642 body_fmt, qmark, body, FALSE, TRUE,
1643 _("The body of the \"Reply\" template has an error at line %d."));
1644 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1645 quote_fmt_reset_vartable();
1648 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1649 compose_force_encryption(compose, account, FALSE, s_system);
1652 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1653 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1654 compose_force_signing(compose, account, s_system);
1658 SIGNAL_BLOCK(textbuf);
1660 if (account->auto_sig)
1661 compose_insert_sig(compose, FALSE);
1663 compose_wrap_all(compose);
1666 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1667 gtkaspell_highlight_all(compose->gtkaspell);
1668 gtkaspell_unblock_check(compose->gtkaspell);
1670 SIGNAL_UNBLOCK(textbuf);
1672 gtk_widget_grab_focus(compose->text);
1674 undo_unblock(compose->undostruct);
1676 if (prefs_common.auto_exteditor)
1677 compose_exec_ext_editor(compose);
1679 compose->modified = FALSE;
1680 compose_set_title(compose);
1682 compose->updating = FALSE;
1683 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1684 SCROLL_TO_CURSOR(compose);
1686 if (compose->deferred_destroy) {
1687 compose_destroy(compose);
1695 #define INSERT_FW_HEADER(var, hdr) \
1696 if (msginfo->var && *msginfo->var) { \
1697 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1698 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1699 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1702 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1703 gboolean as_attach, const gchar *body,
1704 gboolean no_extedit,
1708 GtkTextView *textview;
1709 GtkTextBuffer *textbuf;
1710 gint cursor_pos = -1;
1713 cm_return_val_if_fail(msginfo != NULL, NULL);
1714 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1717 !(account = compose_guess_forward_account_from_msginfo
1719 account = cur_account;
1721 if (!prefs_common.forward_as_attachment)
1722 mode = COMPOSE_FORWARD_INLINE;
1724 mode = COMPOSE_FORWARD;
1725 compose = compose_create(account, msginfo->folder, mode, batch);
1727 compose->updating = TRUE;
1728 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1729 if (!compose->fwdinfo)
1730 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1732 compose_extract_original_charset(compose);
1734 if (msginfo->subject && *msginfo->subject) {
1735 gchar *buf, *buf2, *p;
1737 buf = p = g_strdup(msginfo->subject);
1738 p += subject_get_prefix_length(p);
1739 memmove(buf, p, strlen(p) + 1);
1741 buf2 = g_strdup_printf("Fw: %s", buf);
1742 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1748 /* override from name according to folder properties */
1749 if (msginfo->folder && msginfo->folder->prefs &&
1750 msginfo->folder->prefs->forward_with_format &&
1751 msginfo->folder->prefs->forward_override_from_format &&
1752 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1756 MsgInfo *full_msginfo = NULL;
1759 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1761 full_msginfo = procmsg_msginfo_copy(msginfo);
1763 /* decode \-escape sequences in the internal representation of the quote format */
1764 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1765 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1768 gtkaspell_block_check(compose->gtkaspell);
1769 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1770 compose->gtkaspell);
1772 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1774 quote_fmt_scan_string(tmp);
1777 buf = quote_fmt_get_buffer();
1779 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1781 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1782 quote_fmt_reset_vartable();
1785 procmsg_msginfo_free(full_msginfo);
1788 textview = GTK_TEXT_VIEW(compose->text);
1789 textbuf = gtk_text_view_get_buffer(textview);
1790 compose_create_tags(textview, compose);
1792 undo_block(compose->undostruct);
1796 msgfile = procmsg_get_message_file(msginfo);
1797 if (!is_file_exist(msgfile))
1798 g_warning("%s: file not exist\n", msgfile);
1800 compose_attach_append(compose, msgfile, msgfile,
1801 "message/rfc822", NULL);
1805 const gchar *qmark = NULL;
1806 const gchar *body_fmt = NULL;
1807 MsgInfo *full_msginfo;
1809 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1810 body_fmt = gettext(prefs_common.fw_quotefmt);
1814 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1816 full_msginfo = procmsg_msginfo_copy(msginfo);
1818 /* use the forward format of folder (if enabled), or the account's one
1819 (if enabled) or fallback to the global forward format, which is always
1820 enabled (even if empty), and use the relevant quotemark */
1821 if (msginfo->folder && msginfo->folder->prefs &&
1822 msginfo->folder->prefs->forward_with_format) {
1823 qmark = msginfo->folder->prefs->forward_quotemark;
1824 body_fmt = msginfo->folder->prefs->forward_body_format;
1826 } else if (account->forward_with_format) {
1827 qmark = account->forward_quotemark;
1828 body_fmt = account->forward_body_format;
1831 qmark = prefs_common.fw_quotemark;
1832 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1833 body_fmt = gettext(prefs_common.fw_quotefmt);
1838 /* empty quotemark is not allowed */
1839 if (qmark == NULL || *qmark == '\0')
1842 compose_quote_fmt(compose, full_msginfo,
1843 body_fmt, qmark, body, FALSE, TRUE,
1844 _("The body of the \"Forward\" template has an error at line %d."));
1845 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1846 quote_fmt_reset_vartable();
1847 compose_attach_parts(compose, msginfo);
1849 procmsg_msginfo_free(full_msginfo);
1852 SIGNAL_BLOCK(textbuf);
1854 if (account->auto_sig)
1855 compose_insert_sig(compose, FALSE);
1857 compose_wrap_all(compose);
1860 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1861 gtkaspell_highlight_all(compose->gtkaspell);
1862 gtkaspell_unblock_check(compose->gtkaspell);
1864 SIGNAL_UNBLOCK(textbuf);
1866 cursor_pos = quote_fmt_get_cursor_pos();
1867 if (cursor_pos == -1)
1868 gtk_widget_grab_focus(compose->header_last->entry);
1870 gtk_widget_grab_focus(compose->text);
1872 if (!no_extedit && prefs_common.auto_exteditor)
1873 compose_exec_ext_editor(compose);
1876 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1877 gchar *folderidentifier;
1879 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1880 folderidentifier = folder_item_get_identifier(msginfo->folder);
1881 compose_set_save_to(compose, folderidentifier);
1882 g_free(folderidentifier);
1885 undo_unblock(compose->undostruct);
1887 compose->modified = FALSE;
1888 compose_set_title(compose);
1890 compose->updating = FALSE;
1891 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1892 SCROLL_TO_CURSOR(compose);
1894 if (compose->deferred_destroy) {
1895 compose_destroy(compose);
1899 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1904 #undef INSERT_FW_HEADER
1906 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1909 GtkTextView *textview;
1910 GtkTextBuffer *textbuf;
1914 gboolean single_mail = TRUE;
1916 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1918 if (g_slist_length(msginfo_list) > 1)
1919 single_mail = FALSE;
1921 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1922 if (((MsgInfo *)msginfo->data)->folder == NULL)
1925 /* guess account from first selected message */
1927 !(account = compose_guess_forward_account_from_msginfo
1928 (msginfo_list->data)))
1929 account = cur_account;
1931 cm_return_val_if_fail(account != NULL, NULL);
1933 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1934 if (msginfo->data) {
1935 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1936 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1940 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1941 g_warning("no msginfo_list");
1945 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1947 compose->updating = TRUE;
1949 /* override from name according to folder properties */
1950 if (msginfo_list->data) {
1951 MsgInfo *msginfo = msginfo_list->data;
1953 if (msginfo->folder && msginfo->folder->prefs &&
1954 msginfo->folder->prefs->forward_with_format &&
1955 msginfo->folder->prefs->forward_override_from_format &&
1956 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1961 /* decode \-escape sequences in the internal representation of the quote format */
1962 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1963 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1966 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1967 compose->gtkaspell);
1969 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1971 quote_fmt_scan_string(tmp);
1974 buf = quote_fmt_get_buffer();
1976 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1978 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1979 quote_fmt_reset_vartable();
1985 textview = GTK_TEXT_VIEW(compose->text);
1986 textbuf = gtk_text_view_get_buffer(textview);
1987 compose_create_tags(textview, compose);
1989 undo_block(compose->undostruct);
1990 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1991 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1993 if (!is_file_exist(msgfile))
1994 g_warning("%s: file not exist\n", msgfile);
1996 compose_attach_append(compose, msgfile, msgfile,
1997 "message/rfc822", NULL);
2002 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2003 if (info->subject && *info->subject) {
2004 gchar *buf, *buf2, *p;
2006 buf = p = g_strdup(info->subject);
2007 p += subject_get_prefix_length(p);
2008 memmove(buf, p, strlen(p) + 1);
2010 buf2 = g_strdup_printf("Fw: %s", buf);
2011 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2017 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2018 _("Fw: multiple emails"));
2021 SIGNAL_BLOCK(textbuf);
2023 if (account->auto_sig)
2024 compose_insert_sig(compose, FALSE);
2026 compose_wrap_all(compose);
2028 SIGNAL_UNBLOCK(textbuf);
2030 gtk_text_buffer_get_start_iter(textbuf, &iter);
2031 gtk_text_buffer_place_cursor(textbuf, &iter);
2033 gtk_widget_grab_focus(compose->header_last->entry);
2034 undo_unblock(compose->undostruct);
2035 compose->modified = FALSE;
2036 compose_set_title(compose);
2038 compose->updating = FALSE;
2039 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2040 SCROLL_TO_CURSOR(compose);
2042 if (compose->deferred_destroy) {
2043 compose_destroy(compose);
2047 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2052 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2054 GtkTextIter start = *iter;
2055 GtkTextIter end_iter;
2056 int start_pos = gtk_text_iter_get_offset(&start);
2058 if (!compose->account->sig_sep)
2061 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2062 start_pos+strlen(compose->account->sig_sep));
2064 /* check sig separator */
2065 str = gtk_text_iter_get_text(&start, &end_iter);
2066 if (!strcmp(str, compose->account->sig_sep)) {
2068 /* check end of line (\n) */
2069 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2070 start_pos+strlen(compose->account->sig_sep));
2071 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2072 start_pos+strlen(compose->account->sig_sep)+1);
2073 tmp = gtk_text_iter_get_text(&start, &end_iter);
2074 if (!strcmp(tmp,"\n")) {
2086 static void compose_colorize_signature(Compose *compose)
2088 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2090 GtkTextIter end_iter;
2091 gtk_text_buffer_get_start_iter(buffer, &iter);
2092 while (gtk_text_iter_forward_line(&iter))
2093 if (compose_is_sig_separator(compose, buffer, &iter)) {
2094 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2095 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2099 #define BLOCK_WRAP() { \
2100 prev_autowrap = compose->autowrap; \
2101 buffer = gtk_text_view_get_buffer( \
2102 GTK_TEXT_VIEW(compose->text)); \
2103 compose->autowrap = FALSE; \
2105 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2106 G_CALLBACK(compose_changed_cb), \
2108 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2109 G_CALLBACK(text_inserted), \
2112 #define UNBLOCK_WRAP() { \
2113 compose->autowrap = prev_autowrap; \
2114 if (compose->autowrap) { \
2115 gint old = compose->draft_timeout_tag; \
2116 compose->draft_timeout_tag = -2; \
2117 compose_wrap_all(compose); \
2118 compose->draft_timeout_tag = old; \
2121 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2122 G_CALLBACK(compose_changed_cb), \
2124 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2125 G_CALLBACK(text_inserted), \
2129 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2131 Compose *compose = NULL;
2132 PrefsAccount *account = NULL;
2133 GtkTextView *textview;
2134 GtkTextBuffer *textbuf;
2138 gchar buf[BUFFSIZE];
2139 gboolean use_signing = FALSE;
2140 gboolean use_encryption = FALSE;
2141 gchar *privacy_system = NULL;
2142 int priority = PRIORITY_NORMAL;
2143 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2144 gboolean autowrap = prefs_common.autowrap;
2145 gboolean autoindent = prefs_common.auto_indent;
2146 HeaderEntry *manual_headers = NULL;
2148 cm_return_val_if_fail(msginfo != NULL, NULL);
2149 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2151 if (compose_put_existing_to_front(msginfo)) {
2155 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2156 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2157 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2158 gchar queueheader_buf[BUFFSIZE];
2161 /* Select Account from queue headers */
2162 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2163 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2164 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2165 account = account_find_from_id(id);
2167 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2168 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2169 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2170 account = account_find_from_id(id);
2172 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2173 sizeof(queueheader_buf), "NAID:")) {
2174 id = atoi(&queueheader_buf[strlen("NAID:")]);
2175 account = account_find_from_id(id);
2177 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2178 sizeof(queueheader_buf), "MAID:")) {
2179 id = atoi(&queueheader_buf[strlen("MAID:")]);
2180 account = account_find_from_id(id);
2182 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2183 sizeof(queueheader_buf), "S:")) {
2184 account = account_find_from_address(queueheader_buf, FALSE);
2186 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2187 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2188 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2189 use_signing = param;
2192 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2193 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2194 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2195 use_signing = param;
2198 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2199 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2200 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2201 use_encryption = param;
2203 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2204 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2205 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2206 use_encryption = param;
2208 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2209 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2210 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2213 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2214 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2215 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2218 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2219 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2220 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2222 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2223 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2224 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2226 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2227 sizeof(queueheader_buf), "X-Priority: ")) {
2228 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2231 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2232 sizeof(queueheader_buf), "RMID:")) {
2233 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2234 if (tokens[0] && tokens[1] && tokens[2]) {
2235 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2236 if (orig_item != NULL) {
2237 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2242 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2243 sizeof(queueheader_buf), "FMID:")) {
2244 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2245 if (tokens[0] && tokens[1] && tokens[2]) {
2246 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2247 if (orig_item != NULL) {
2248 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2253 /* Get manual headers */
2254 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2255 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2256 if (*listmh != '\0') {
2257 debug_print("Got manual headers: %s\n", listmh);
2258 manual_headers = procheader_entries_from_str(listmh);
2263 account = msginfo->folder->folder->account;
2266 if (!account && prefs_common.reedit_account_autosel) {
2267 gchar from[BUFFSIZE];
2268 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2269 extract_address(from);
2270 account = account_find_from_address(from, FALSE);
2274 account = cur_account;
2276 cm_return_val_if_fail(account != NULL, NULL);
2278 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2280 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2281 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2282 compose->autowrap = autowrap;
2283 compose->replyinfo = replyinfo;
2284 compose->fwdinfo = fwdinfo;
2286 compose->updating = TRUE;
2287 compose->priority = priority;
2289 if (privacy_system != NULL) {
2290 compose->privacy_system = privacy_system;
2291 compose_use_signing(compose, use_signing);
2292 compose_use_encryption(compose, use_encryption);
2293 compose_update_privacy_system_menu_item(compose, FALSE);
2295 activate_privacy_system(compose, account, FALSE);
2298 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2300 compose_extract_original_charset(compose);
2302 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2303 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2304 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2305 gchar queueheader_buf[BUFFSIZE];
2307 /* Set message save folder */
2308 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2309 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2310 compose_set_save_to(compose, &queueheader_buf[4]);
2312 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2313 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2315 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2320 if (compose_parse_header(compose, msginfo) < 0) {
2321 compose->updating = FALSE;
2322 compose_destroy(compose);
2325 compose_reedit_set_entry(compose, msginfo);
2327 textview = GTK_TEXT_VIEW(compose->text);
2328 textbuf = gtk_text_view_get_buffer(textview);
2329 compose_create_tags(textview, compose);
2331 mark = gtk_text_buffer_get_insert(textbuf);
2332 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2334 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2335 G_CALLBACK(compose_changed_cb),
2338 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2339 fp = procmime_get_first_encrypted_text_content(msginfo);
2341 compose_force_encryption(compose, account, TRUE, NULL);
2344 fp = procmime_get_first_text_content(msginfo);
2347 g_warning("Can't get text part\n");
2351 gboolean prev_autowrap = compose->autowrap;
2352 GtkTextBuffer *buffer = textbuf;
2354 while (fgets(buf, sizeof(buf), fp) != NULL) {
2356 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2362 compose_attach_parts(compose, msginfo);
2364 compose_colorize_signature(compose);
2366 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2367 G_CALLBACK(compose_changed_cb),
2370 if (manual_headers != NULL) {
2371 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2372 procheader_entries_free(manual_headers);
2373 compose->updating = FALSE;
2374 compose_destroy(compose);
2377 procheader_entries_free(manual_headers);
2380 gtk_widget_grab_focus(compose->text);
2382 if (prefs_common.auto_exteditor) {
2383 compose_exec_ext_editor(compose);
2385 compose->modified = FALSE;
2386 compose_set_title(compose);
2388 compose->updating = FALSE;
2389 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2390 SCROLL_TO_CURSOR(compose);
2392 if (compose->deferred_destroy) {
2393 compose_destroy(compose);
2397 compose->sig_str = account_get_signature_str(compose->account);
2399 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2404 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2411 cm_return_val_if_fail(msginfo != NULL, NULL);
2414 account = account_get_reply_account(msginfo,
2415 prefs_common.reply_account_autosel);
2416 cm_return_val_if_fail(account != NULL, NULL);
2418 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2420 compose->updating = TRUE;
2422 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2423 compose->replyinfo = NULL;
2424 compose->fwdinfo = NULL;
2426 compose_show_first_last_header(compose, TRUE);
2428 gtk_widget_grab_focus(compose->header_last->entry);
2430 filename = procmsg_get_message_file(msginfo);
2432 if (filename == NULL) {
2433 compose->updating = FALSE;
2434 compose_destroy(compose);
2439 compose->redirect_filename = filename;
2441 /* Set save folder */
2442 item = msginfo->folder;
2443 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2444 gchar *folderidentifier;
2446 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2447 folderidentifier = folder_item_get_identifier(item);
2448 compose_set_save_to(compose, folderidentifier);
2449 g_free(folderidentifier);
2452 compose_attach_parts(compose, msginfo);
2454 if (msginfo->subject)
2455 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2457 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2459 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2460 _("The body of the \"Redirect\" template has an error at line %d."));
2461 quote_fmt_reset_vartable();
2462 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2464 compose_colorize_signature(compose);
2467 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2468 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2469 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2471 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2472 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2473 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2474 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2475 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2476 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2477 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2478 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2480 if (compose->toolbar->draft_btn)
2481 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2482 if (compose->toolbar->insert_btn)
2483 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2484 if (compose->toolbar->attach_btn)
2485 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2486 if (compose->toolbar->sig_btn)
2487 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2488 if (compose->toolbar->exteditor_btn)
2489 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2490 if (compose->toolbar->linewrap_current_btn)
2491 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2492 if (compose->toolbar->linewrap_all_btn)
2493 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2495 compose->modified = FALSE;
2496 compose_set_title(compose);
2497 compose->updating = FALSE;
2498 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2499 SCROLL_TO_CURSOR(compose);
2501 if (compose->deferred_destroy) {
2502 compose_destroy(compose);
2506 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2511 GList *compose_get_compose_list(void)
2513 return compose_list;
2516 void compose_entry_append(Compose *compose, const gchar *address,
2517 ComposeEntryType type, ComposePrefType pref_type)
2519 const gchar *header;
2521 gboolean in_quote = FALSE;
2522 if (!address || *address == '\0') return;
2529 header = N_("Bcc:");
2531 case COMPOSE_REPLYTO:
2532 header = N_("Reply-To:");
2534 case COMPOSE_NEWSGROUPS:
2535 header = N_("Newsgroups:");
2537 case COMPOSE_FOLLOWUPTO:
2538 header = N_( "Followup-To:");
2540 case COMPOSE_INREPLYTO:
2541 header = N_( "In-Reply-To:");
2548 header = prefs_common_translated_header_name(header);
2550 cur = begin = (gchar *)address;
2552 /* we separate the line by commas, but not if we're inside a quoted
2554 while (*cur != '\0') {
2556 in_quote = !in_quote;
2557 if (*cur == ',' && !in_quote) {
2558 gchar *tmp = g_strdup(begin);
2560 tmp[cur-begin]='\0';
2563 while (*tmp == ' ' || *tmp == '\t')
2565 compose_add_header_entry(compose, header, tmp, pref_type);
2572 gchar *tmp = g_strdup(begin);
2574 tmp[cur-begin]='\0';
2577 while (*tmp == ' ' || *tmp == '\t')
2579 compose_add_header_entry(compose, header, tmp, pref_type);
2584 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2586 #if !GTK_CHECK_VERSION(3, 0, 0)
2587 static GdkColor yellow;
2588 static GdkColor black;
2589 static gboolean yellow_initialised = FALSE;
2591 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2592 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2597 #if !GTK_CHECK_VERSION(3, 0, 0)
2598 if (!yellow_initialised) {
2599 gdk_color_parse("#f5f6be", &yellow);
2600 gdk_color_parse("#000000", &black);
2601 yellow_initialised = gdk_colormap_alloc_color(
2602 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2603 yellow_initialised &= gdk_colormap_alloc_color(
2604 gdk_colormap_get_system(), &black, FALSE, TRUE);
2608 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2609 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2610 if (gtk_entry_get_text(entry) &&
2611 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2612 #if !GTK_CHECK_VERSION(3, 0, 0)
2613 if (yellow_initialised) {
2615 gtk_widget_modify_base(
2616 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2617 GTK_STATE_NORMAL, &yellow);
2618 gtk_widget_modify_text(
2619 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2620 GTK_STATE_NORMAL, &black);
2621 #if !GTK_CHECK_VERSION(3, 0, 0)
2628 void compose_toolbar_cb(gint action, gpointer data)
2630 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2631 Compose *compose = (Compose*)toolbar_item->parent;
2633 cm_return_if_fail(compose != NULL);
2637 compose_send_cb(NULL, compose);
2640 compose_send_later_cb(NULL, compose);
2643 compose_draft(compose, COMPOSE_QUIT_EDITING);
2646 compose_insert_file_cb(NULL, compose);
2649 compose_attach_cb(NULL, compose);
2652 compose_insert_sig(compose, FALSE);
2655 compose_ext_editor_cb(NULL, compose);
2657 case A_LINEWRAP_CURRENT:
2658 compose_beautify_paragraph(compose, NULL, TRUE);
2660 case A_LINEWRAP_ALL:
2661 compose_wrap_all_full(compose, TRUE);
2664 compose_address_cb(NULL, compose);
2667 case A_CHECK_SPELLING:
2668 compose_check_all(NULL, compose);
2676 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2681 gchar *subject = NULL;
2685 gchar **attach = NULL;
2686 gchar *inreplyto = NULL;
2687 MailField mfield = NO_FIELD_PRESENT;
2689 /* get mailto parts but skip from */
2690 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2693 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2694 mfield = TO_FIELD_PRESENT;
2697 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2699 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2701 if (!g_utf8_validate (subject, -1, NULL)) {
2702 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2703 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2706 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2708 mfield = SUBJECT_FIELD_PRESENT;
2711 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2712 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2715 gboolean prev_autowrap = compose->autowrap;
2717 compose->autowrap = FALSE;
2719 mark = gtk_text_buffer_get_insert(buffer);
2720 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2722 if (!g_utf8_validate (body, -1, NULL)) {
2723 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2724 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2727 gtk_text_buffer_insert(buffer, &iter, body, -1);
2729 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2731 compose->autowrap = prev_autowrap;
2732 if (compose->autowrap)
2733 compose_wrap_all(compose);
2734 mfield = BODY_FIELD_PRESENT;
2738 gint i = 0, att = 0;
2739 gchar *warn_files = NULL;
2740 while (attach[i] != NULL) {
2741 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2742 if (utf8_filename) {
2743 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2744 gchar *tmp = g_strdup_printf("%s%s\n",
2745 warn_files?warn_files:"",
2751 g_free(utf8_filename);
2753 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2758 alertpanel_notice(ngettext(
2759 "The following file has been attached: \n%s",
2760 "The following files have been attached: \n%s", att), warn_files);
2765 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2778 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2780 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2781 {"Cc:", NULL, TRUE},
2782 {"References:", NULL, FALSE},
2783 {"Bcc:", NULL, TRUE},
2784 {"Newsgroups:", NULL, TRUE},
2785 {"Followup-To:", NULL, TRUE},
2786 {"List-Post:", NULL, FALSE},
2787 {"X-Priority:", NULL, FALSE},
2788 {NULL, NULL, FALSE}};
2804 cm_return_val_if_fail(msginfo != NULL, -1);
2806 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2807 procheader_get_header_fields(fp, hentry);
2810 if (hentry[H_REPLY_TO].body != NULL) {
2811 if (hentry[H_REPLY_TO].body[0] != '\0') {
2813 conv_unmime_header(hentry[H_REPLY_TO].body,
2816 g_free(hentry[H_REPLY_TO].body);
2817 hentry[H_REPLY_TO].body = NULL;
2819 if (hentry[H_CC].body != NULL) {
2820 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2821 g_free(hentry[H_CC].body);
2822 hentry[H_CC].body = NULL;
2824 if (hentry[H_REFERENCES].body != NULL) {
2825 if (compose->mode == COMPOSE_REEDIT)
2826 compose->references = hentry[H_REFERENCES].body;
2828 compose->references = compose_parse_references
2829 (hentry[H_REFERENCES].body, msginfo->msgid);
2830 g_free(hentry[H_REFERENCES].body);
2832 hentry[H_REFERENCES].body = NULL;
2834 if (hentry[H_BCC].body != NULL) {
2835 if (compose->mode == COMPOSE_REEDIT)
2837 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2838 g_free(hentry[H_BCC].body);
2839 hentry[H_BCC].body = NULL;
2841 if (hentry[H_NEWSGROUPS].body != NULL) {
2842 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2843 hentry[H_NEWSGROUPS].body = NULL;
2845 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2846 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2847 compose->followup_to =
2848 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2851 g_free(hentry[H_FOLLOWUP_TO].body);
2852 hentry[H_FOLLOWUP_TO].body = NULL;
2854 if (hentry[H_LIST_POST].body != NULL) {
2855 gchar *to = NULL, *start = NULL;
2857 extract_address(hentry[H_LIST_POST].body);
2858 if (hentry[H_LIST_POST].body[0] != '\0') {
2859 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2861 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2862 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2865 g_free(compose->ml_post);
2866 compose->ml_post = to;
2869 g_free(hentry[H_LIST_POST].body);
2870 hentry[H_LIST_POST].body = NULL;
2873 /* CLAWS - X-Priority */
2874 if (compose->mode == COMPOSE_REEDIT)
2875 if (hentry[H_X_PRIORITY].body != NULL) {
2878 priority = atoi(hentry[H_X_PRIORITY].body);
2879 g_free(hentry[H_X_PRIORITY].body);
2881 hentry[H_X_PRIORITY].body = NULL;
2883 if (priority < PRIORITY_HIGHEST ||
2884 priority > PRIORITY_LOWEST)
2885 priority = PRIORITY_NORMAL;
2887 compose->priority = priority;
2890 if (compose->mode == COMPOSE_REEDIT) {
2891 if (msginfo->inreplyto && *msginfo->inreplyto)
2892 compose->inreplyto = g_strdup(msginfo->inreplyto);
2896 if (msginfo->msgid && *msginfo->msgid)
2897 compose->inreplyto = g_strdup(msginfo->msgid);
2899 if (!compose->references) {
2900 if (msginfo->msgid && *msginfo->msgid) {
2901 if (msginfo->inreplyto && *msginfo->inreplyto)
2902 compose->references =
2903 g_strdup_printf("<%s>\n\t<%s>",
2907 compose->references =
2908 g_strconcat("<", msginfo->msgid, ">",
2910 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2911 compose->references =
2912 g_strconcat("<", msginfo->inreplyto, ">",
2920 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2925 cm_return_val_if_fail(msginfo != NULL, -1);
2927 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2928 procheader_get_header_fields(fp, entries);
2932 while (he != NULL && he->name != NULL) {
2934 GtkListStore *model = NULL;
2936 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
2937 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
2938 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
2939 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
2940 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
2947 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2949 GSList *ref_id_list, *cur;
2953 ref_id_list = references_list_append(NULL, ref);
2954 if (!ref_id_list) return NULL;
2955 if (msgid && *msgid)
2956 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2961 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2962 /* "<" + Message-ID + ">" + CR+LF+TAB */
2963 len += strlen((gchar *)cur->data) + 5;
2965 if (len > MAX_REFERENCES_LEN) {
2966 /* remove second message-ID */
2967 if (ref_id_list && ref_id_list->next &&
2968 ref_id_list->next->next) {
2969 g_free(ref_id_list->next->data);
2970 ref_id_list = g_slist_remove
2971 (ref_id_list, ref_id_list->next->data);
2973 slist_free_strings(ref_id_list);
2974 g_slist_free(ref_id_list);
2981 new_ref = g_string_new("");
2982 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2983 if (new_ref->len > 0)
2984 g_string_append(new_ref, "\n\t");
2985 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2988 slist_free_strings(ref_id_list);
2989 g_slist_free(ref_id_list);
2991 new_ref_str = new_ref->str;
2992 g_string_free(new_ref, FALSE);
2997 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2998 const gchar *fmt, const gchar *qmark,
2999 const gchar *body, gboolean rewrap,
3000 gboolean need_unescape,
3001 const gchar *err_msg)
3003 MsgInfo* dummyinfo = NULL;
3004 gchar *quote_str = NULL;
3006 gboolean prev_autowrap;
3007 const gchar *trimmed_body = body;
3008 gint cursor_pos = -1;
3009 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3010 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3015 SIGNAL_BLOCK(buffer);
3018 dummyinfo = compose_msginfo_new_from_compose(compose);
3019 msginfo = dummyinfo;
3022 if (qmark != NULL) {
3024 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3025 compose->gtkaspell);
3027 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3029 quote_fmt_scan_string(qmark);
3032 buf = quote_fmt_get_buffer();
3034 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3036 Xstrdup_a(quote_str, buf, goto error)
3039 if (fmt && *fmt != '\0') {
3042 while (*trimmed_body == '\n')
3046 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3047 compose->gtkaspell);
3049 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3051 if (need_unescape) {
3054 /* decode \-escape sequences in the internal representation of the quote format */
3055 tmp = g_malloc(strlen(fmt)+1);
3056 pref_get_unescaped_pref(tmp, fmt);
3057 quote_fmt_scan_string(tmp);
3061 quote_fmt_scan_string(fmt);
3065 buf = quote_fmt_get_buffer();
3067 gint line = quote_fmt_get_line();
3068 alertpanel_error(err_msg, line);
3074 prev_autowrap = compose->autowrap;
3075 compose->autowrap = FALSE;
3077 mark = gtk_text_buffer_get_insert(buffer);
3078 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3079 if (g_utf8_validate(buf, -1, NULL)) {
3080 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3082 gchar *tmpout = NULL;
3083 tmpout = conv_codeset_strdup
3084 (buf, conv_get_locale_charset_str_no_utf8(),
3086 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3088 tmpout = g_malloc(strlen(buf)*2+1);
3089 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3091 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3095 cursor_pos = quote_fmt_get_cursor_pos();
3096 if (cursor_pos == -1)
3097 cursor_pos = gtk_text_iter_get_offset(&iter);
3098 compose->set_cursor_pos = cursor_pos;
3100 gtk_text_buffer_get_start_iter(buffer, &iter);
3101 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3102 gtk_text_buffer_place_cursor(buffer, &iter);
3104 compose->autowrap = prev_autowrap;
3105 if (compose->autowrap && rewrap)
3106 compose_wrap_all(compose);
3113 SIGNAL_UNBLOCK(buffer);
3115 procmsg_msginfo_free( dummyinfo );
3120 /* if ml_post is of type addr@host and from is of type
3121 * addr-anything@host, return TRUE
3123 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3125 gchar *left_ml = NULL;
3126 gchar *right_ml = NULL;
3127 gchar *left_from = NULL;
3128 gchar *right_from = NULL;
3129 gboolean result = FALSE;
3131 if (!ml_post || !from)
3134 left_ml = g_strdup(ml_post);
3135 if (strstr(left_ml, "@")) {
3136 right_ml = strstr(left_ml, "@")+1;
3137 *(strstr(left_ml, "@")) = '\0';
3140 left_from = g_strdup(from);
3141 if (strstr(left_from, "@")) {
3142 right_from = strstr(left_from, "@")+1;
3143 *(strstr(left_from, "@")) = '\0';
3146 if (left_ml && left_from && right_ml && right_from
3147 && !strncmp(left_from, left_ml, strlen(left_ml))
3148 && !strcmp(right_from, right_ml)) {
3157 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3158 gboolean respect_default_to)
3162 if (!folder || !folder->prefs)
3165 if (respect_default_to && folder->prefs->enable_default_to) {
3166 compose_entry_append(compose, folder->prefs->default_to,
3167 COMPOSE_TO, PREF_FOLDER);
3168 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3170 if (folder->prefs->enable_default_cc)
3171 compose_entry_append(compose, folder->prefs->default_cc,
3172 COMPOSE_CC, PREF_FOLDER);
3173 if (folder->prefs->enable_default_bcc)
3174 compose_entry_append(compose, folder->prefs->default_bcc,
3175 COMPOSE_BCC, PREF_FOLDER);
3176 if (folder->prefs->enable_default_replyto)
3177 compose_entry_append(compose, folder->prefs->default_replyto,
3178 COMPOSE_REPLYTO, PREF_FOLDER);
3181 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3186 if (!compose || !msginfo)
3189 if (msginfo->subject && *msginfo->subject) {
3190 buf = p = g_strdup(msginfo->subject);
3191 p += subject_get_prefix_length(p);
3192 memmove(buf, p, strlen(p) + 1);
3194 buf2 = g_strdup_printf("Re: %s", buf);
3195 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3200 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3203 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3204 gboolean to_all, gboolean to_ml,
3206 gboolean followup_and_reply_to)
3208 GSList *cc_list = NULL;
3211 gchar *replyto = NULL;
3212 gchar *ac_email = NULL;
3214 gboolean reply_to_ml = FALSE;
3215 gboolean default_reply_to = FALSE;
3217 cm_return_if_fail(compose->account != NULL);
3218 cm_return_if_fail(msginfo != NULL);
3220 reply_to_ml = to_ml && compose->ml_post;
3222 default_reply_to = msginfo->folder &&
3223 msginfo->folder->prefs->enable_default_reply_to;
3225 if (compose->account->protocol != A_NNTP) {
3226 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3228 if (reply_to_ml && !default_reply_to) {
3230 gboolean is_subscr = is_subscription(compose->ml_post,
3233 /* normal answer to ml post with a reply-to */
3234 compose_entry_append(compose,
3236 COMPOSE_TO, PREF_ML);
3237 if (compose->replyto)
3238 compose_entry_append(compose,
3240 COMPOSE_CC, PREF_ML);
3242 /* answer to subscription confirmation */
3243 if (compose->replyto)
3244 compose_entry_append(compose,
3246 COMPOSE_TO, PREF_ML);
3247 else if (msginfo->from)
3248 compose_entry_append(compose,
3250 COMPOSE_TO, PREF_ML);
3253 else if (!(to_all || to_sender) && default_reply_to) {
3254 compose_entry_append(compose,
3255 msginfo->folder->prefs->default_reply_to,
3256 COMPOSE_TO, PREF_FOLDER);
3257 compose_entry_mark_default_to(compose,
3258 msginfo->folder->prefs->default_reply_to);
3263 Xstrdup_a(tmp1, msginfo->from, return);
3264 extract_address(tmp1);
3265 if (to_all || to_sender ||
3266 !account_find_from_address(tmp1, FALSE))
3267 compose_entry_append(compose,
3268 (compose->replyto && !to_sender)
3269 ? compose->replyto :
3270 msginfo->from ? msginfo->from : "",
3271 COMPOSE_TO, PREF_NONE);
3272 else if (!to_all && !to_sender) {
3273 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3274 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3275 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3276 if (compose->replyto) {
3277 compose_entry_append(compose,
3279 COMPOSE_TO, PREF_NONE);
3281 compose_entry_append(compose,
3282 msginfo->from ? msginfo->from : "",
3283 COMPOSE_TO, PREF_NONE);
3286 /* replying to own mail, use original recp */
3287 compose_entry_append(compose,
3288 msginfo->to ? msginfo->to : "",
3289 COMPOSE_TO, PREF_NONE);
3290 compose_entry_append(compose,
3291 msginfo->cc ? msginfo->cc : "",
3292 COMPOSE_CC, PREF_NONE);
3297 if (to_sender || (compose->followup_to &&
3298 !strncmp(compose->followup_to, "poster", 6)))
3299 compose_entry_append
3301 (compose->replyto ? compose->replyto :
3302 msginfo->from ? msginfo->from : ""),
3303 COMPOSE_TO, PREF_NONE);
3305 else if (followup_and_reply_to || to_all) {
3306 compose_entry_append
3308 (compose->replyto ? compose->replyto :
3309 msginfo->from ? msginfo->from : ""),
3310 COMPOSE_TO, PREF_NONE);
3312 compose_entry_append
3314 compose->followup_to ? compose->followup_to :
3315 compose->newsgroups ? compose->newsgroups : "",
3316 COMPOSE_NEWSGROUPS, PREF_NONE);
3319 compose_entry_append
3321 compose->followup_to ? compose->followup_to :
3322 compose->newsgroups ? compose->newsgroups : "",
3323 COMPOSE_NEWSGROUPS, PREF_NONE);
3325 compose_reply_set_subject(compose, msginfo);
3327 if (to_ml && compose->ml_post) return;
3328 if (!to_all || compose->account->protocol == A_NNTP) return;
3330 if (compose->replyto) {
3331 Xstrdup_a(replyto, compose->replyto, return);
3332 extract_address(replyto);
3334 if (msginfo->from) {
3335 Xstrdup_a(from, msginfo->from, return);
3336 extract_address(from);
3339 if (replyto && from)
3340 cc_list = address_list_append_with_comments(cc_list, from);
3341 if (to_all && msginfo->folder &&
3342 msginfo->folder->prefs->enable_default_reply_to)
3343 cc_list = address_list_append_with_comments(cc_list,
3344 msginfo->folder->prefs->default_reply_to);
3345 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3346 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3348 ac_email = g_utf8_strdown(compose->account->address, -1);
3351 for (cur = cc_list; cur != NULL; cur = cur->next) {
3352 gchar *addr = g_utf8_strdown(cur->data, -1);
3353 extract_address(addr);
3355 if (strcmp(ac_email, addr))
3356 compose_entry_append(compose, (gchar *)cur->data,
3357 COMPOSE_CC, PREF_NONE);
3359 debug_print("Cc address same as compose account's, ignoring\n");
3364 slist_free_strings(cc_list);
3365 g_slist_free(cc_list);
3371 #define SET_ENTRY(entry, str) \
3374 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3377 #define SET_ADDRESS(type, str) \
3380 compose_entry_append(compose, str, type, PREF_NONE); \
3383 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3385 cm_return_if_fail(msginfo != NULL);
3387 SET_ENTRY(subject_entry, msginfo->subject);
3388 SET_ENTRY(from_name, msginfo->from);
3389 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3390 SET_ADDRESS(COMPOSE_CC, compose->cc);
3391 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3392 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3393 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3394 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3396 compose_update_priority_menu_item(compose);
3397 compose_update_privacy_system_menu_item(compose, FALSE);
3398 compose_show_first_last_header(compose, TRUE);
3404 static void compose_insert_sig(Compose *compose, gboolean replace)
3406 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3407 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3409 GtkTextIter iter, iter_end;
3410 gint cur_pos, ins_pos;
3411 gboolean prev_autowrap;
3412 gboolean found = FALSE;
3413 gboolean exists = FALSE;
3415 cm_return_if_fail(compose->account != NULL);
3419 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3420 G_CALLBACK(compose_changed_cb),
3423 mark = gtk_text_buffer_get_insert(buffer);
3424 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3425 cur_pos = gtk_text_iter_get_offset (&iter);
3428 gtk_text_buffer_get_end_iter(buffer, &iter);
3430 exists = (compose->sig_str != NULL);
3433 GtkTextIter first_iter, start_iter, end_iter;
3435 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3437 if (!exists || compose->sig_str[0] == '\0')
3440 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3441 compose->signature_tag);
3444 /* include previous \n\n */
3445 gtk_text_iter_backward_chars(&first_iter, 1);
3446 start_iter = first_iter;
3447 end_iter = first_iter;
3449 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3450 compose->signature_tag);
3451 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3452 compose->signature_tag);
3454 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3460 g_free(compose->sig_str);
3461 compose->sig_str = account_get_signature_str(compose->account);
3463 cur_pos = gtk_text_iter_get_offset(&iter);
3465 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3466 g_free(compose->sig_str);
3467 compose->sig_str = NULL;
3469 if (compose->sig_inserted == FALSE)
3470 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3471 compose->sig_inserted = TRUE;
3473 cur_pos = gtk_text_iter_get_offset(&iter);
3474 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3476 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3477 gtk_text_iter_forward_chars(&iter, 1);
3478 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3479 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3481 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3482 cur_pos = gtk_text_buffer_get_char_count (buffer);
3485 /* put the cursor where it should be
3486 * either where the quote_fmt says, either where it was */
3487 if (compose->set_cursor_pos < 0)
3488 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3490 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3491 compose->set_cursor_pos);
3493 compose->set_cursor_pos = -1;
3494 gtk_text_buffer_place_cursor(buffer, &iter);
3495 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3496 G_CALLBACK(compose_changed_cb),
3502 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3505 GtkTextBuffer *buffer;
3508 const gchar *cur_encoding;
3509 gchar buf[BUFFSIZE];
3512 gboolean prev_autowrap;
3513 gboolean badtxt = FALSE;
3514 struct stat file_stat;
3517 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3519 /* get the size of the file we are about to insert */
3520 ret = g_stat(file, &file_stat);
3522 gchar *shortfile = g_path_get_basename(file);
3523 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3525 return COMPOSE_INSERT_NO_FILE;
3526 } else if (prefs_common.warn_large_insert == TRUE) {
3528 /* ask user for confirmation if the file is large */
3529 if (prefs_common.warn_large_insert_size < 0 ||
3530 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3534 msg = g_strdup_printf(_("You are about to insert a file of %s "
3535 "in the message body. Are you sure you want to do that?"),
3536 to_human_readable(file_stat.st_size));
3537 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3538 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3541 /* do we ask for confirmation next time? */
3542 if (aval & G_ALERTDISABLE) {
3543 /* no confirmation next time, disable feature in preferences */
3544 aval &= ~G_ALERTDISABLE;
3545 prefs_common.warn_large_insert = FALSE;
3548 /* abort file insertion if user canceled action */
3549 if (aval != G_ALERTALTERNATE) {
3550 return COMPOSE_INSERT_NO_FILE;
3556 if ((fp = g_fopen(file, "rb")) == NULL) {
3557 FILE_OP_ERROR(file, "fopen");
3558 return COMPOSE_INSERT_READ_ERROR;
3561 prev_autowrap = compose->autowrap;
3562 compose->autowrap = FALSE;
3564 text = GTK_TEXT_VIEW(compose->text);
3565 buffer = gtk_text_view_get_buffer(text);
3566 mark = gtk_text_buffer_get_insert(buffer);
3567 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3569 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3570 G_CALLBACK(text_inserted),
3573 cur_encoding = conv_get_locale_charset_str_no_utf8();
3575 while (fgets(buf, sizeof(buf), fp) != NULL) {
3578 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3579 str = g_strdup(buf);
3581 str = conv_codeset_strdup
3582 (buf, cur_encoding, CS_INTERNAL);
3585 /* strip <CR> if DOS/Windows file,
3586 replace <CR> with <LF> if Macintosh file. */
3589 if (len > 0 && str[len - 1] != '\n') {
3591 if (str[len] == '\r') str[len] = '\n';
3594 gtk_text_buffer_insert(buffer, &iter, str, -1);
3598 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3599 G_CALLBACK(text_inserted),
3601 compose->autowrap = prev_autowrap;
3602 if (compose->autowrap)
3603 compose_wrap_all(compose);
3608 return COMPOSE_INSERT_INVALID_CHARACTER;
3610 return COMPOSE_INSERT_SUCCESS;
3613 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3614 const gchar *filename,
3615 const gchar *content_type,
3616 const gchar *charset)
3624 GtkListStore *store;
3626 gboolean has_binary = FALSE;
3628 if (!is_file_exist(file)) {
3629 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3630 gboolean result = FALSE;
3631 if (file_from_uri && is_file_exist(file_from_uri)) {
3632 result = compose_attach_append(
3633 compose, file_from_uri,
3634 filename, content_type,
3637 g_free(file_from_uri);
3640 alertpanel_error("File %s doesn't exist\n", filename);
3643 if ((size = get_file_size(file)) < 0) {
3644 alertpanel_error("Can't get file size of %s\n", filename);
3648 alertpanel_error(_("File %s is empty."), filename);
3651 if ((fp = g_fopen(file, "rb")) == NULL) {
3652 alertpanel_error(_("Can't read %s."), filename);
3657 ainfo = g_new0(AttachInfo, 1);
3658 auto_ainfo = g_auto_pointer_new_with_free
3659 (ainfo, (GFreeFunc) compose_attach_info_free);
3660 ainfo->file = g_strdup(file);
3663 ainfo->content_type = g_strdup(content_type);
3664 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3666 MsgFlags flags = {0, 0};
3668 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3669 ainfo->encoding = ENC_7BIT;
3671 ainfo->encoding = ENC_8BIT;
3673 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3674 if (msginfo && msginfo->subject)
3675 name = g_strdup(msginfo->subject);
3677 name = g_path_get_basename(filename ? filename : file);
3679 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3681 procmsg_msginfo_free(msginfo);
3683 if (!g_ascii_strncasecmp(content_type, "text/", 5)) {
3684 ainfo->charset = g_strdup(charset);
3685 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3687 ainfo->encoding = ENC_BASE64;
3689 name = g_path_get_basename(filename ? filename : file);
3690 ainfo->name = g_strdup(name);
3694 ainfo->content_type = procmime_get_mime_type(file);
3695 if (!ainfo->content_type) {
3696 ainfo->content_type =
3697 g_strdup("application/octet-stream");
3698 ainfo->encoding = ENC_BASE64;
3699 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text/", 5))
3701 procmime_get_encoding_for_text_file(file, &has_binary);
3703 ainfo->encoding = ENC_BASE64;
3704 name = g_path_get_basename(filename ? filename : file);
3705 ainfo->name = g_strdup(name);
3709 if (ainfo->name != NULL
3710 && !strcmp(ainfo->name, ".")) {
3711 g_free(ainfo->name);
3715 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3716 g_free(ainfo->content_type);
3717 ainfo->content_type = g_strdup("application/octet-stream");
3718 g_free(ainfo->charset);
3719 ainfo->charset = NULL;
3722 ainfo->size = (goffset)size;
3723 size_text = to_human_readable((goffset)size);
3725 store = GTK_LIST_STORE(gtk_tree_view_get_model
3726 (GTK_TREE_VIEW(compose->attach_clist)));
3728 gtk_list_store_append(store, &iter);
3729 gtk_list_store_set(store, &iter,
3730 COL_MIMETYPE, ainfo->content_type,
3731 COL_SIZE, size_text,
3732 COL_NAME, ainfo->name,
3733 COL_CHARSET, ainfo->charset,
3735 COL_AUTODATA, auto_ainfo,
3738 g_auto_pointer_free(auto_ainfo);
3739 compose_attach_update_label(compose);
3743 static void compose_use_signing(Compose *compose, gboolean use_signing)
3745 compose->use_signing = use_signing;
3746 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3749 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3751 compose->use_encryption = use_encryption;
3752 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3755 #define NEXT_PART_NOT_CHILD(info) \
3757 node = info->node; \
3758 while (node->children) \
3759 node = g_node_last_child(node); \
3760 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3763 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3767 MimeInfo *firsttext = NULL;
3768 MimeInfo *encrypted = NULL;
3771 const gchar *partname = NULL;
3773 mimeinfo = procmime_scan_message(msginfo);
3774 if (!mimeinfo) return;
3776 if (mimeinfo->node->children == NULL) {
3777 procmime_mimeinfo_free_all(mimeinfo);
3781 /* find first content part */
3782 child = (MimeInfo *) mimeinfo->node->children->data;
3783 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3784 child = (MimeInfo *)child->node->children->data;
3787 if (child->type == MIMETYPE_TEXT) {
3789 debug_print("First text part found\n");
3790 } else if (compose->mode == COMPOSE_REEDIT &&
3791 child->type == MIMETYPE_APPLICATION &&
3792 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3793 encrypted = (MimeInfo *)child->node->parent->data;
3796 child = (MimeInfo *) mimeinfo->node->children->data;
3797 while (child != NULL) {
3800 if (child == encrypted) {
3801 /* skip this part of tree */
3802 NEXT_PART_NOT_CHILD(child);
3806 if (child->type == MIMETYPE_MULTIPART) {
3807 /* get the actual content */
3808 child = procmime_mimeinfo_next(child);
3812 if (child == firsttext) {
3813 child = procmime_mimeinfo_next(child);
3817 outfile = procmime_get_tmp_file_name(child);
3818 if ((err = procmime_get_part(outfile, child)) < 0)
3819 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3821 gchar *content_type;
3823 content_type = procmime_get_content_type_str(child->type, child->subtype);
3825 /* if we meet a pgp signature, we don't attach it, but
3826 * we force signing. */
3827 if ((strcmp(content_type, "application/pgp-signature") &&
3828 strcmp(content_type, "application/pkcs7-signature") &&
3829 strcmp(content_type, "application/x-pkcs7-signature"))
3830 || compose->mode == COMPOSE_REDIRECT) {
3831 partname = procmime_mimeinfo_get_parameter(child, "filename");
3832 if (partname == NULL)
3833 partname = procmime_mimeinfo_get_parameter(child, "name");
3834 if (partname == NULL)
3836 compose_attach_append(compose, outfile,
3837 partname, content_type,
3838 procmime_mimeinfo_get_parameter(child, "charset"));
3840 compose_force_signing(compose, compose->account, NULL);
3842 g_free(content_type);
3845 NEXT_PART_NOT_CHILD(child);
3847 procmime_mimeinfo_free_all(mimeinfo);
3850 #undef NEXT_PART_NOT_CHILD
3855 WAIT_FOR_INDENT_CHAR,
3856 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3859 /* return indent length, we allow:
3860 indent characters followed by indent characters or spaces/tabs,
3861 alphabets and numbers immediately followed by indent characters,
3862 and the repeating sequences of the above
3863 If quote ends with multiple spaces, only the first one is included. */
3864 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3865 const GtkTextIter *start, gint *len)
3867 GtkTextIter iter = *start;
3871 IndentState state = WAIT_FOR_INDENT_CHAR;
3874 gint alnum_count = 0;
3875 gint space_count = 0;
3878 if (prefs_common.quote_chars == NULL) {
3882 while (!gtk_text_iter_ends_line(&iter)) {
3883 wc = gtk_text_iter_get_char(&iter);
3884 if (g_unichar_iswide(wc))
3886 clen = g_unichar_to_utf8(wc, ch);
3890 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3891 is_space = g_unichar_isspace(wc);
3893 if (state == WAIT_FOR_INDENT_CHAR) {
3894 if (!is_indent && !g_unichar_isalnum(wc))
3897 quote_len += alnum_count + space_count + 1;
3898 alnum_count = space_count = 0;
3899 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3902 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3903 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3907 else if (is_indent) {
3908 quote_len += alnum_count + space_count + 1;
3909 alnum_count = space_count = 0;
3912 state = WAIT_FOR_INDENT_CHAR;
3916 gtk_text_iter_forward_char(&iter);
3919 if (quote_len > 0 && space_count > 0)
3925 if (quote_len > 0) {
3927 gtk_text_iter_forward_chars(&iter, quote_len);
3928 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3934 /* return >0 if the line is itemized */
3935 static int compose_itemized_length(GtkTextBuffer *buffer,
3936 const GtkTextIter *start)
3938 GtkTextIter iter = *start;
3943 if (gtk_text_iter_ends_line(&iter))
3948 wc = gtk_text_iter_get_char(&iter);
3949 if (!g_unichar_isspace(wc))
3951 gtk_text_iter_forward_char(&iter);
3952 if (gtk_text_iter_ends_line(&iter))
3956 clen = g_unichar_to_utf8(wc, ch);
3960 if (!strchr("*-+", ch[0]))
3963 gtk_text_iter_forward_char(&iter);
3964 if (gtk_text_iter_ends_line(&iter))
3966 wc = gtk_text_iter_get_char(&iter);
3967 if (g_unichar_isspace(wc)) {
3973 /* return the string at the start of the itemization */
3974 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3975 const GtkTextIter *start)
3977 GtkTextIter iter = *start;
3980 GString *item_chars = g_string_new("");
3983 if (gtk_text_iter_ends_line(&iter))
3988 wc = gtk_text_iter_get_char(&iter);
3989 if (!g_unichar_isspace(wc))
3991 gtk_text_iter_forward_char(&iter);
3992 if (gtk_text_iter_ends_line(&iter))
3994 g_string_append_unichar(item_chars, wc);
3997 str = item_chars->str;
3998 g_string_free(item_chars, FALSE);
4002 /* return the number of spaces at a line's start */
4003 static int compose_left_offset_length(GtkTextBuffer *buffer,
4004 const GtkTextIter *start)
4006 GtkTextIter iter = *start;
4009 if (gtk_text_iter_ends_line(&iter))
4013 wc = gtk_text_iter_get_char(&iter);
4014 if (!g_unichar_isspace(wc))
4017 gtk_text_iter_forward_char(&iter);
4018 if (gtk_text_iter_ends_line(&iter))
4022 gtk_text_iter_forward_char(&iter);
4023 if (gtk_text_iter_ends_line(&iter))
4028 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
4029 const GtkTextIter *start,
4030 GtkTextIter *break_pos,
4034 GtkTextIter iter = *start, line_end = *start;
4035 PangoLogAttr *attrs;
4042 gboolean can_break = FALSE;
4043 gboolean do_break = FALSE;
4044 gboolean was_white = FALSE;
4045 gboolean prev_dont_break = FALSE;
4047 gtk_text_iter_forward_to_line_end(&line_end);
4048 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
4049 len = g_utf8_strlen(str, -1);
4053 g_warning("compose_get_line_break_pos: len = 0!\n");
4057 /* g_print("breaking line: %d: %s (len = %d)\n",
4058 gtk_text_iter_get_line(&iter), str, len); */
4060 attrs = g_new(PangoLogAttr, len + 1);
4062 pango_default_break(str, -1, NULL, attrs, len + 1);
4066 /* skip quote and leading spaces */
4067 for (i = 0; *p != '\0' && i < len; i++) {
4070 wc = g_utf8_get_char(p);
4071 if (i >= quote_len && !g_unichar_isspace(wc))
4073 if (g_unichar_iswide(wc))
4075 else if (*p == '\t')
4079 p = g_utf8_next_char(p);
4082 for (; *p != '\0' && i < len; i++) {
4083 PangoLogAttr *attr = attrs + i;
4087 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
4090 was_white = attr->is_white;
4092 /* don't wrap URI */
4093 if ((uri_len = get_uri_len(p)) > 0) {
4095 if (pos > 0 && col > max_col) {
4105 wc = g_utf8_get_char(p);
4106 if (g_unichar_iswide(wc)) {
4108 if (prev_dont_break && can_break && attr->is_line_break)
4110 } else if (*p == '\t')
4114 if (pos > 0 && col > max_col) {
4119 if (*p == '-' || *p == '/')
4120 prev_dont_break = TRUE;
4122 prev_dont_break = FALSE;
4124 p = g_utf8_next_char(p);
4128 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4133 *break_pos = *start;
4134 gtk_text_iter_set_line_offset(break_pos, pos);
4139 static gboolean compose_join_next_line(Compose *compose,
4140 GtkTextBuffer *buffer,
4142 const gchar *quote_str)
4144 GtkTextIter iter_ = *iter, cur, prev, next, end;
4145 PangoLogAttr attrs[3];
4147 gchar *next_quote_str;
4150 gboolean keep_cursor = FALSE;
4152 if (!gtk_text_iter_forward_line(&iter_) ||
4153 gtk_text_iter_ends_line(&iter_)) {
4156 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4158 if ((quote_str || next_quote_str) &&
4159 strcmp2(quote_str, next_quote_str) != 0) {
4160 g_free(next_quote_str);
4163 g_free(next_quote_str);
4166 if (quote_len > 0) {
4167 gtk_text_iter_forward_chars(&end, quote_len);
4168 if (gtk_text_iter_ends_line(&end)) {
4173 /* don't join itemized lines */
4174 if (compose_itemized_length(buffer, &end) > 0) {
4178 /* don't join signature separator */
4179 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4182 /* delete quote str */
4184 gtk_text_buffer_delete(buffer, &iter_, &end);
4186 /* don't join line breaks put by the user */
4188 gtk_text_iter_backward_char(&cur);
4189 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4190 gtk_text_iter_forward_char(&cur);
4194 gtk_text_iter_forward_char(&cur);
4195 /* delete linebreak and extra spaces */
4196 while (gtk_text_iter_backward_char(&cur)) {
4197 wc1 = gtk_text_iter_get_char(&cur);
4198 if (!g_unichar_isspace(wc1))
4203 while (!gtk_text_iter_ends_line(&cur)) {
4204 wc1 = gtk_text_iter_get_char(&cur);
4205 if (!g_unichar_isspace(wc1))
4207 gtk_text_iter_forward_char(&cur);
4210 if (!gtk_text_iter_equal(&prev, &next)) {
4213 mark = gtk_text_buffer_get_insert(buffer);
4214 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4215 if (gtk_text_iter_equal(&prev, &cur))
4217 gtk_text_buffer_delete(buffer, &prev, &next);
4221 /* insert space if required */
4222 gtk_text_iter_backward_char(&prev);
4223 wc1 = gtk_text_iter_get_char(&prev);
4224 wc2 = gtk_text_iter_get_char(&next);
4225 gtk_text_iter_forward_char(&next);
4226 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4227 pango_default_break(str, -1, NULL, attrs, 3);
4228 if (!attrs[1].is_line_break ||
4229 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4230 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4232 gtk_text_iter_backward_char(&iter_);
4233 gtk_text_buffer_place_cursor(buffer, &iter_);
4242 #define ADD_TXT_POS(bp_, ep_, pti_) \
4243 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4244 last = last->next; \
4245 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4246 last->next = NULL; \
4248 g_warning("alloc error scanning URIs\n"); \
4251 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4253 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4254 GtkTextBuffer *buffer;
4255 GtkTextIter iter, break_pos, end_of_line;
4256 gchar *quote_str = NULL;
4258 gboolean wrap_quote = prefs_common.linewrap_quote;
4259 gboolean prev_autowrap = compose->autowrap;
4260 gint startq_offset = -1, noq_offset = -1;
4261 gint uri_start = -1, uri_stop = -1;
4262 gint nouri_start = -1, nouri_stop = -1;
4263 gint num_blocks = 0;
4264 gint quotelevel = -1;
4265 gboolean modified = force;
4266 gboolean removed = FALSE;
4267 gboolean modified_before_remove = FALSE;
4269 gboolean start = TRUE;
4270 gint itemized_len = 0, rem_item_len = 0;
4271 gchar *itemized_chars = NULL;
4272 gboolean item_continuation = FALSE;
4277 if (compose->draft_timeout_tag == -2) {
4281 compose->autowrap = FALSE;
4283 buffer = gtk_text_view_get_buffer(text);
4284 undo_wrapping(compose->undostruct, TRUE);
4289 mark = gtk_text_buffer_get_insert(buffer);
4290 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4294 if (compose->draft_timeout_tag == -2) {
4295 if (gtk_text_iter_ends_line(&iter)) {
4296 while (gtk_text_iter_ends_line(&iter) &&
4297 gtk_text_iter_forward_line(&iter))
4300 while (gtk_text_iter_backward_line(&iter)) {
4301 if (gtk_text_iter_ends_line(&iter)) {
4302 gtk_text_iter_forward_line(&iter);
4308 /* move to line start */
4309 gtk_text_iter_set_line_offset(&iter, 0);
4312 itemized_len = compose_itemized_length(buffer, &iter);
4314 if (!itemized_len) {
4315 itemized_len = compose_left_offset_length(buffer, &iter);
4316 item_continuation = TRUE;
4320 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4322 /* go until paragraph end (empty line) */
4323 while (start || !gtk_text_iter_ends_line(&iter)) {
4324 gchar *scanpos = NULL;
4325 /* parse table - in order of priority */
4327 const gchar *needle; /* token */
4329 /* token search function */
4330 gchar *(*search) (const gchar *haystack,
4331 const gchar *needle);
4332 /* part parsing function */
4333 gboolean (*parse) (const gchar *start,
4334 const gchar *scanpos,
4338 /* part to URI function */
4339 gchar *(*build_uri) (const gchar *bp,
4343 static struct table parser[] = {
4344 {"http://", strcasestr, get_uri_part, make_uri_string},
4345 {"https://", strcasestr, get_uri_part, make_uri_string},
4346 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4347 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4348 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4349 {"www.", strcasestr, get_uri_part, make_http_string},
4350 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4351 {"@", strcasestr, get_email_part, make_email_string}
4353 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4354 gint last_index = PARSE_ELEMS;
4356 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4360 if (!prev_autowrap && num_blocks == 0) {
4362 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4363 G_CALLBACK(text_inserted),
4366 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4369 uri_start = uri_stop = -1;
4371 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4374 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4375 if (startq_offset == -1)
4376 startq_offset = gtk_text_iter_get_offset(&iter);
4377 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4378 if (quotelevel > 2) {
4379 /* recycle colors */
4380 if (prefs_common.recycle_quote_colors)
4389 if (startq_offset == -1)
4390 noq_offset = gtk_text_iter_get_offset(&iter);
4394 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4397 if (gtk_text_iter_ends_line(&iter)) {
4399 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4400 prefs_common.linewrap_len,
4402 GtkTextIter prev, next, cur;
4403 if (prev_autowrap != FALSE || force) {
4404 compose->automatic_break = TRUE;
4406 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4407 compose->automatic_break = FALSE;
4408 if (itemized_len && compose->autoindent) {
4409 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4410 if (!item_continuation)
4411 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4413 } else if (quote_str && wrap_quote) {
4414 compose->automatic_break = TRUE;
4416 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4417 compose->automatic_break = FALSE;
4418 if (itemized_len && compose->autoindent) {
4419 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4420 if (!item_continuation)
4421 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4425 /* remove trailing spaces */
4427 rem_item_len = itemized_len;
4428 while (compose->autoindent && rem_item_len-- > 0)
4429 gtk_text_iter_backward_char(&cur);
4430 gtk_text_iter_backward_char(&cur);
4433 while (!gtk_text_iter_starts_line(&cur)) {
4436 gtk_text_iter_backward_char(&cur);
4437 wc = gtk_text_iter_get_char(&cur);
4438 if (!g_unichar_isspace(wc))
4442 if (!gtk_text_iter_equal(&prev, &next)) {
4443 gtk_text_buffer_delete(buffer, &prev, &next);
4445 gtk_text_iter_forward_char(&break_pos);
4449 gtk_text_buffer_insert(buffer, &break_pos,
4453 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4455 /* move iter to current line start */
4456 gtk_text_iter_set_line_offset(&iter, 0);
4463 /* move iter to next line start */
4469 if (!prev_autowrap && num_blocks > 0) {
4471 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4472 G_CALLBACK(text_inserted),
4476 while (!gtk_text_iter_ends_line(&end_of_line)) {
4477 gtk_text_iter_forward_char(&end_of_line);
4479 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4481 nouri_start = gtk_text_iter_get_offset(&iter);
4482 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4484 walk_pos = gtk_text_iter_get_offset(&iter);
4485 /* FIXME: this looks phony. scanning for anything in the parse table */
4486 for (n = 0; n < PARSE_ELEMS; n++) {
4489 tmp = parser[n].search(walk, parser[n].needle);
4491 if (scanpos == NULL || tmp < scanpos) {
4500 /* check if URI can be parsed */
4501 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4502 (const gchar **)&ep, FALSE)
4503 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4507 strlen(parser[last_index].needle);
4510 uri_start = walk_pos + (bp - o_walk);
4511 uri_stop = walk_pos + (ep - o_walk);
4515 gtk_text_iter_forward_line(&iter);
4518 if (startq_offset != -1) {
4519 GtkTextIter startquote, endquote;
4520 gtk_text_buffer_get_iter_at_offset(
4521 buffer, &startquote, startq_offset);
4524 switch (quotelevel) {
4526 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4527 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4528 gtk_text_buffer_apply_tag_by_name(
4529 buffer, "quote0", &startquote, &endquote);
4530 gtk_text_buffer_remove_tag_by_name(
4531 buffer, "quote1", &startquote, &endquote);
4532 gtk_text_buffer_remove_tag_by_name(
4533 buffer, "quote2", &startquote, &endquote);
4538 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4539 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4540 gtk_text_buffer_apply_tag_by_name(
4541 buffer, "quote1", &startquote, &endquote);
4542 gtk_text_buffer_remove_tag_by_name(
4543 buffer, "quote0", &startquote, &endquote);
4544 gtk_text_buffer_remove_tag_by_name(
4545 buffer, "quote2", &startquote, &endquote);
4550 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4551 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4552 gtk_text_buffer_apply_tag_by_name(
4553 buffer, "quote2", &startquote, &endquote);
4554 gtk_text_buffer_remove_tag_by_name(
4555 buffer, "quote0", &startquote, &endquote);
4556 gtk_text_buffer_remove_tag_by_name(
4557 buffer, "quote1", &startquote, &endquote);
4563 } else if (noq_offset != -1) {
4564 GtkTextIter startnoquote, endnoquote;
4565 gtk_text_buffer_get_iter_at_offset(
4566 buffer, &startnoquote, noq_offset);
4569 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4570 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4571 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4572 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4573 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4574 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4575 gtk_text_buffer_remove_tag_by_name(
4576 buffer, "quote0", &startnoquote, &endnoquote);
4577 gtk_text_buffer_remove_tag_by_name(
4578 buffer, "quote1", &startnoquote, &endnoquote);
4579 gtk_text_buffer_remove_tag_by_name(
4580 buffer, "quote2", &startnoquote, &endnoquote);
4586 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4587 GtkTextIter nouri_start_iter, nouri_end_iter;
4588 gtk_text_buffer_get_iter_at_offset(
4589 buffer, &nouri_start_iter, nouri_start);
4590 gtk_text_buffer_get_iter_at_offset(
4591 buffer, &nouri_end_iter, nouri_stop);
4592 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4593 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4594 gtk_text_buffer_remove_tag_by_name(
4595 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4596 modified_before_remove = modified;
4601 if (uri_start >= 0 && uri_stop > 0) {
4602 GtkTextIter uri_start_iter, uri_end_iter, back;
4603 gtk_text_buffer_get_iter_at_offset(
4604 buffer, &uri_start_iter, uri_start);
4605 gtk_text_buffer_get_iter_at_offset(
4606 buffer, &uri_end_iter, uri_stop);
4607 back = uri_end_iter;
4608 gtk_text_iter_backward_char(&back);
4609 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4610 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4611 gtk_text_buffer_apply_tag_by_name(
4612 buffer, "link", &uri_start_iter, &uri_end_iter);
4614 if (removed && !modified_before_remove) {
4620 // debug_print("not modified, out after %d lines\n", lines);
4624 // debug_print("modified, out after %d lines\n", lines);
4626 g_free(itemized_chars);
4629 undo_wrapping(compose->undostruct, FALSE);
4630 compose->autowrap = prev_autowrap;
4635 void compose_action_cb(void *data)
4637 Compose *compose = (Compose *)data;
4638 compose_wrap_all(compose);
4641 static void compose_wrap_all(Compose *compose)
4643 compose_wrap_all_full(compose, FALSE);
4646 static void compose_wrap_all_full(Compose *compose, gboolean force)
4648 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4649 GtkTextBuffer *buffer;
4651 gboolean modified = TRUE;
4653 buffer = gtk_text_view_get_buffer(text);
4655 gtk_text_buffer_get_start_iter(buffer, &iter);
4656 while (!gtk_text_iter_is_end(&iter) && modified)
4657 modified = compose_beautify_paragraph(compose, &iter, force);
4661 static void compose_set_title(Compose *compose)
4667 edited = compose->modified ? _(" [Edited]") : "";
4669 subject = gtk_editable_get_chars(
4670 GTK_EDITABLE(compose->subject_entry), 0, -1);
4672 #ifndef GENERIC_UMPC
4673 if (subject && strlen(subject))
4674 str = g_strdup_printf(_("%s - Compose message%s"),
4677 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4679 str = g_strdup(_("Compose message"));
4682 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4688 * compose_current_mail_account:
4690 * Find a current mail account (the currently selected account, or the
4691 * default account, if a news account is currently selected). If a
4692 * mail account cannot be found, display an error message.
4694 * Return value: Mail account, or NULL if not found.
4696 static PrefsAccount *
4697 compose_current_mail_account(void)
4701 if (cur_account && cur_account->protocol != A_NNTP)
4704 ac = account_get_default();
4705 if (!ac || ac->protocol == A_NNTP) {
4706 alertpanel_error(_("Account for sending mail is not specified.\n"
4707 "Please select a mail account before sending."));
4714 #define QUOTE_IF_REQUIRED(out, str) \
4716 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4720 len = strlen(str) + 3; \
4721 if ((__tmp = alloca(len)) == NULL) { \
4722 g_warning("can't allocate memory\n"); \
4723 g_string_free(header, TRUE); \
4726 g_snprintf(__tmp, len, "\"%s\"", str); \
4731 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4732 g_warning("can't allocate memory\n"); \
4733 g_string_free(header, TRUE); \
4736 strcpy(__tmp, str); \
4742 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4744 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4748 len = strlen(str) + 3; \
4749 if ((__tmp = alloca(len)) == NULL) { \
4750 g_warning("can't allocate memory\n"); \
4753 g_snprintf(__tmp, len, "\"%s\"", str); \
4758 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4759 g_warning("can't allocate memory\n"); \
4762 strcpy(__tmp, str); \
4768 static void compose_select_account(Compose *compose, PrefsAccount *account,
4771 gchar *from = NULL, *header;
4772 ComposeHeaderEntry *header_entry;
4774 cm_return_if_fail(account != NULL);
4776 compose->account = account;
4777 if (account->name && *account->name) {
4779 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4780 from = g_strdup_printf("%s <%s>",
4781 buf, account->address);
4782 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4784 from = g_strdup_printf("<%s>",
4786 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4791 compose_set_title(compose);
4793 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4794 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4796 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4797 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4798 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4800 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4802 activate_privacy_system(compose, account, FALSE);
4804 if (!init && compose->mode != COMPOSE_REDIRECT) {
4805 undo_block(compose->undostruct);
4806 compose_insert_sig(compose, TRUE);
4807 undo_unblock(compose->undostruct);
4810 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4811 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4813 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4814 if (account->protocol == A_NNTP) {
4815 if (!strcmp(header, _("To:")))
4816 combobox_select_by_text(
4817 GTK_COMBO_BOX(header_entry->combo),
4820 if (!strcmp(header, _("Newsgroups:")))
4821 combobox_select_by_text(
4822 GTK_COMBO_BOX(header_entry->combo),
4830 /* use account's dict info if set */
4831 if (compose->gtkaspell) {
4832 if (account->enable_default_dictionary)
4833 gtkaspell_change_dict(compose->gtkaspell,
4834 account->default_dictionary, FALSE);
4835 if (account->enable_default_alt_dictionary)
4836 gtkaspell_change_alt_dict(compose->gtkaspell,
4837 account->default_alt_dictionary);
4838 if (account->enable_default_dictionary
4839 || account->enable_default_alt_dictionary)
4840 compose_spell_menu_changed(compose);
4845 gboolean compose_check_for_valid_recipient(Compose *compose) {
4846 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4847 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4848 gboolean recipient_found = FALSE;
4852 /* free to and newsgroup list */
4853 slist_free_strings(compose->to_list);
4854 g_slist_free(compose->to_list);
4855 compose->to_list = NULL;
4857 slist_free_strings(compose->newsgroup_list);
4858 g_slist_free(compose->newsgroup_list);
4859 compose->newsgroup_list = NULL;
4861 /* search header entries for to and newsgroup entries */
4862 for (list = compose->header_list; list; list = list->next) {
4865 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4866 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4869 if (entry[0] != '\0') {
4870 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4871 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4872 compose->to_list = address_list_append(compose->to_list, entry);
4873 recipient_found = TRUE;
4876 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4877 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4878 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4879 recipient_found = TRUE;
4886 return recipient_found;
4889 static gboolean compose_check_for_set_recipients(Compose *compose)
4891 if (compose->account->set_autocc && compose->account->auto_cc) {
4892 gboolean found_other = FALSE;
4894 /* search header entries for to and newsgroup entries */
4895 for (list = compose->header_list; list; list = list->next) {
4898 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4899 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4902 if (strcmp(entry, compose->account->auto_cc)
4903 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4913 if (compose->batch) {
4914 gtk_widget_show_all(compose->window);
4916 aval = alertpanel(_("Send"),
4917 _("The only recipient is the default CC address. Send anyway?"),
4918 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4919 if (aval != G_ALERTALTERNATE)
4923 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4924 gboolean found_other = FALSE;
4926 /* search header entries for to and newsgroup entries */
4927 for (list = compose->header_list; list; list = list->next) {
4930 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4931 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4934 if (strcmp(entry, compose->account->auto_bcc)
4935 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4945 if (compose->batch) {
4946 gtk_widget_show_all(compose->window);
4948 aval = alertpanel(_("Send"),
4949 _("The only recipient is the default BCC address. Send anyway?"),
4950 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4951 if (aval != G_ALERTALTERNATE)
4958 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4962 if (compose_check_for_valid_recipient(compose) == FALSE) {
4963 if (compose->batch) {
4964 gtk_widget_show_all(compose->window);
4966 alertpanel_error(_("Recipient is not specified."));
4970 if (compose_check_for_set_recipients(compose) == FALSE) {
4974 if (!compose->batch) {
4975 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4976 if (*str == '\0' && check_everything == TRUE &&
4977 compose->mode != COMPOSE_REDIRECT) {
4979 gchar *button_label;
4982 if (compose->sending)
4983 button_label = _("+_Send");
4985 button_label = _("+_Queue");
4986 message = g_strdup_printf(_("Subject is empty. %s"),
4987 compose->sending?_("Send it anyway?"):
4988 _("Queue it anyway?"));
4990 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4991 GTK_STOCK_CANCEL, button_label, NULL);
4993 if (aval != G_ALERTALTERNATE)
4998 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
5004 gint compose_send(Compose *compose)
5007 FolderItem *folder = NULL;
5009 gchar *msgpath = NULL;
5010 gboolean discard_window = FALSE;
5011 gchar *errstr = NULL;
5012 gchar *tmsgid = NULL;
5013 MainWindow *mainwin = mainwindow_get_mainwindow();
5014 gboolean queued_removed = FALSE;
5016 if (prefs_common.send_dialog_invisible
5017 || compose->batch == TRUE)
5018 discard_window = TRUE;
5020 compose_allow_user_actions (compose, FALSE);
5021 compose->sending = TRUE;
5023 if (compose_check_entries(compose, TRUE) == FALSE) {
5024 if (compose->batch) {
5025 gtk_widget_show_all(compose->window);
5031 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
5034 if (compose->batch) {
5035 gtk_widget_show_all(compose->window);
5038 alertpanel_error(_("Could not queue message for sending:\n\n"
5039 "Charset conversion failed."));
5040 } else if (val == -5) {
5041 alertpanel_error(_("Could not queue message for sending:\n\n"
5042 "Couldn't get recipient encryption key."));
5043 } else if (val == -6) {
5045 } else if (val == -3) {
5046 if (privacy_peek_error())
5047 alertpanel_error(_("Could not queue message for sending:\n\n"
5048 "Signature failed: %s"), privacy_get_error());
5049 } else if (val == -2 && errno != 0) {
5050 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
5052 alertpanel_error(_("Could not queue message for sending."));
5057 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
5058 if (discard_window) {
5059 compose->sending = FALSE;
5060 compose_close(compose);
5061 /* No more compose access in the normal codepath
5062 * after this point! */
5067 alertpanel_error(_("The message was queued but could not be "
5068 "sent.\nUse \"Send queued messages\" from "
5069 "the main window to retry."));
5070 if (!discard_window) {
5077 if (msgpath == NULL) {
5078 msgpath = folder_item_fetch_msg(folder, msgnum);
5079 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5082 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5083 claws_unlink(msgpath);
5086 if (!discard_window) {
5088 if (!queued_removed)
5089 folder_item_remove_msg(folder, msgnum);
5090 folder_item_scan(folder);
5092 /* make sure we delete that */
5093 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5095 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5096 folder_item_remove_msg(folder, tmp->msgnum);
5097 procmsg_msginfo_free(tmp);
5104 if (!queued_removed)
5105 folder_item_remove_msg(folder, msgnum);
5106 folder_item_scan(folder);
5108 /* make sure we delete that */
5109 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5111 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5112 folder_item_remove_msg(folder, tmp->msgnum);
5113 procmsg_msginfo_free(tmp);
5116 if (!discard_window) {
5117 compose->sending = FALSE;
5118 compose_allow_user_actions (compose, TRUE);
5119 compose_close(compose);
5123 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5124 "the main window to retry."), errstr);
5127 alertpanel_error_log(_("The message was queued but could not be "
5128 "sent.\nUse \"Send queued messages\" from "
5129 "the main window to retry."));
5131 if (!discard_window) {
5140 toolbar_main_set_sensitive(mainwin);
5141 main_window_set_menu_sensitive(mainwin);
5147 compose_allow_user_actions (compose, TRUE);
5148 compose->sending = FALSE;
5149 compose->modified = TRUE;
5150 toolbar_main_set_sensitive(mainwin);
5151 main_window_set_menu_sensitive(mainwin);
5156 static gboolean compose_use_attach(Compose *compose)
5158 GtkTreeModel *model = gtk_tree_view_get_model
5159 (GTK_TREE_VIEW(compose->attach_clist));
5160 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5163 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5166 gchar buf[BUFFSIZE];
5168 gboolean first_to_address;
5169 gboolean first_cc_address;
5171 ComposeHeaderEntry *headerentry;
5172 const gchar *headerentryname;
5173 const gchar *cc_hdr;
5174 const gchar *to_hdr;
5175 gboolean err = FALSE;
5177 debug_print("Writing redirect header\n");
5179 cc_hdr = prefs_common_translated_header_name("Cc:");
5180 to_hdr = prefs_common_translated_header_name("To:");
5182 first_to_address = TRUE;
5183 for (list = compose->header_list; list; list = list->next) {
5184 headerentry = ((ComposeHeaderEntry *)list->data);
5185 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5187 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5188 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5189 Xstrdup_a(str, entstr, return -1);
5191 if (str[0] != '\0') {
5192 compose_convert_header
5193 (compose, buf, sizeof(buf), str,
5194 strlen("Resent-To") + 2, TRUE);
5196 if (first_to_address) {
5197 err |= (fprintf(fp, "Resent-To: ") < 0);
5198 first_to_address = FALSE;
5200 err |= (fprintf(fp, ",") < 0);
5202 err |= (fprintf(fp, "%s", buf) < 0);
5206 if (!first_to_address) {
5207 err |= (fprintf(fp, "\n") < 0);
5210 first_cc_address = TRUE;
5211 for (list = compose->header_list; list; list = list->next) {
5212 headerentry = ((ComposeHeaderEntry *)list->data);
5213 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5215 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5216 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5217 Xstrdup_a(str, strg, return -1);
5219 if (str[0] != '\0') {
5220 compose_convert_header
5221 (compose, buf, sizeof(buf), str,
5222 strlen("Resent-Cc") + 2, TRUE);
5224 if (first_cc_address) {
5225 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5226 first_cc_address = FALSE;
5228 err |= (fprintf(fp, ",") < 0);
5230 err |= (fprintf(fp, "%s", buf) < 0);
5234 if (!first_cc_address) {
5235 err |= (fprintf(fp, "\n") < 0);
5238 return (err ? -1:0);
5241 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5243 gchar buf[BUFFSIZE];
5245 const gchar *entstr;
5246 /* struct utsname utsbuf; */
5247 gboolean err = FALSE;
5249 cm_return_val_if_fail(fp != NULL, -1);
5250 cm_return_val_if_fail(compose->account != NULL, -1);
5251 cm_return_val_if_fail(compose->account->address != NULL, -1);
5254 get_rfc822_date(buf, sizeof(buf));
5255 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5258 if (compose->account->name && *compose->account->name) {
5259 compose_convert_header
5260 (compose, buf, sizeof(buf), compose->account->name,
5261 strlen("From: "), TRUE);
5262 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5263 buf, compose->account->address) < 0);
5265 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5268 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5269 if (*entstr != '\0') {
5270 Xstrdup_a(str, entstr, return -1);
5273 compose_convert_header(compose, buf, sizeof(buf), str,
5274 strlen("Subject: "), FALSE);
5275 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5279 /* Resent-Message-ID */
5280 if (compose->account->set_domain && compose->account->domain) {
5281 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5282 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5283 g_snprintf(buf, sizeof(buf), "%s",
5284 strchr(compose->account->address, '@') ?
5285 strchr(compose->account->address, '@')+1 :
5286 compose->account->address);
5288 g_snprintf(buf, sizeof(buf), "%s", "");
5291 if (compose->account->gen_msgid) {
5293 if (compose->account->msgid_with_addr) {
5294 addr = compose->account->address;
5296 generate_msgid(buf, sizeof(buf), addr);
5297 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5298 compose->msgid = g_strdup(buf);
5300 compose->msgid = NULL;
5303 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5306 /* separator between header and body */
5307 err |= (fputs("\n", fp) == EOF);
5309 return (err ? -1:0);
5312 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5316 gchar buf[BUFFSIZE];
5318 gboolean skip = FALSE;
5319 gboolean err = FALSE;
5320 gchar *not_included[]={
5321 "Return-Path:", "Delivered-To:", "Received:",
5322 "Subject:", "X-UIDL:", "AF:",
5323 "NF:", "PS:", "SRH:",
5324 "SFN:", "DSR:", "MID:",
5325 "CFG:", "PT:", "S:",
5326 "RQ:", "SSV:", "NSV:",
5327 "SSH:", "R:", "MAID:",
5328 "NAID:", "RMID:", "FMID:",
5329 "SCF:", "RRCPT:", "NG:",
5330 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5331 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5332 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5333 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5334 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5337 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5338 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5342 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5344 for (i = 0; not_included[i] != NULL; i++) {
5345 if (g_ascii_strncasecmp(buf, not_included[i],
5346 strlen(not_included[i])) == 0) {
5353 if (fputs(buf, fdest) == -1)
5356 if (!prefs_common.redirect_keep_from) {
5357 if (g_ascii_strncasecmp(buf, "From:",
5358 strlen("From:")) == 0) {
5359 err |= (fputs(" (by way of ", fdest) == EOF);
5360 if (compose->account->name
5361 && *compose->account->name) {
5362 compose_convert_header
5363 (compose, buf, sizeof(buf),
5364 compose->account->name,
5367 err |= (fprintf(fdest, "%s <%s>",
5369 compose->account->address) < 0);
5371 err |= (fprintf(fdest, "%s",
5372 compose->account->address) < 0);
5373 err |= (fputs(")", fdest) == EOF);
5377 if (fputs("\n", fdest) == -1)
5384 if (compose_redirect_write_headers(compose, fdest))
5387 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5388 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5401 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5403 GtkTextBuffer *buffer;
5404 GtkTextIter start, end;
5407 const gchar *out_codeset;
5408 EncodingType encoding = ENC_UNKNOWN;
5409 MimeInfo *mimemsg, *mimetext;
5411 const gchar *src_codeset = CS_INTERNAL;
5412 gchar *from_addr = NULL;
5413 gchar *from_name = NULL;
5415 if (action == COMPOSE_WRITE_FOR_SEND)
5416 attach_parts = TRUE;
5418 /* create message MimeInfo */
5419 mimemsg = procmime_mimeinfo_new();
5420 mimemsg->type = MIMETYPE_MESSAGE;
5421 mimemsg->subtype = g_strdup("rfc822");
5422 mimemsg->content = MIMECONTENT_MEM;
5423 mimemsg->tmp = TRUE; /* must free content later */
5424 mimemsg->data.mem = compose_get_header(compose);
5426 /* Create text part MimeInfo */
5427 /* get all composed text */
5428 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5429 gtk_text_buffer_get_start_iter(buffer, &start);
5430 gtk_text_buffer_get_end_iter(buffer, &end);
5431 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5433 out_codeset = conv_get_charset_str(compose->out_encoding);
5435 if (!out_codeset && is_ascii_str(chars)) {
5436 out_codeset = CS_US_ASCII;
5437 } else if (prefs_common.outgoing_fallback_to_ascii &&
5438 is_ascii_str(chars)) {
5439 out_codeset = CS_US_ASCII;
5440 encoding = ENC_7BIT;
5444 gchar *test_conv_global_out = NULL;
5445 gchar *test_conv_reply = NULL;
5447 /* automatic mode. be automatic. */
5448 codeconv_set_strict(TRUE);
5450 out_codeset = conv_get_outgoing_charset_str();
5452 debug_print("trying to convert to %s\n", out_codeset);
5453 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5456 if (!test_conv_global_out && compose->orig_charset
5457 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5458 out_codeset = compose->orig_charset;
5459 debug_print("failure; trying to convert to %s\n", out_codeset);
5460 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5463 if (!test_conv_global_out && !test_conv_reply) {
5465 out_codeset = CS_INTERNAL;
5466 debug_print("failure; finally using %s\n", out_codeset);
5468 g_free(test_conv_global_out);
5469 g_free(test_conv_reply);
5470 codeconv_set_strict(FALSE);
5473 if (encoding == ENC_UNKNOWN) {
5474 if (prefs_common.encoding_method == CTE_BASE64)
5475 encoding = ENC_BASE64;
5476 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5477 encoding = ENC_QUOTED_PRINTABLE;
5478 else if (prefs_common.encoding_method == CTE_8BIT)
5479 encoding = ENC_8BIT;
5481 encoding = procmime_get_encoding_for_charset(out_codeset);
5484 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5485 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5487 if (action == COMPOSE_WRITE_FOR_SEND) {
5488 codeconv_set_strict(TRUE);
5489 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5490 codeconv_set_strict(FALSE);
5496 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5497 "to the specified %s charset.\n"
5498 "Send it as %s?"), out_codeset, src_codeset);
5499 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5500 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5503 if (aval != G_ALERTALTERNATE) {
5508 out_codeset = src_codeset;
5514 out_codeset = src_codeset;
5519 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5520 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5521 strstr(buf, "\nFrom ") != NULL) {
5522 encoding = ENC_QUOTED_PRINTABLE;
5526 mimetext = procmime_mimeinfo_new();
5527 mimetext->content = MIMECONTENT_MEM;
5528 mimetext->tmp = TRUE; /* must free content later */
5529 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5530 * and free the data, which we need later. */
5531 mimetext->data.mem = g_strdup(buf);
5532 mimetext->type = MIMETYPE_TEXT;
5533 mimetext->subtype = g_strdup("plain");
5534 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5535 g_strdup(out_codeset));
5537 /* protect trailing spaces when signing message */
5538 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5539 privacy_system_can_sign(compose->privacy_system)) {
5540 encoding = ENC_QUOTED_PRINTABLE;
5543 debug_print("main text: %zd bytes encoded as %s in %d\n",
5544 strlen(buf), out_codeset, encoding);
5546 /* check for line length limit */
5547 if (action == COMPOSE_WRITE_FOR_SEND &&
5548 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5549 check_line_length(buf, 1000, &line) < 0) {
5553 msg = g_strdup_printf
5554 (_("Line %d exceeds the line length limit (998 bytes).\n"
5555 "The contents of the message might be broken on the way to the delivery.\n"
5557 "Send it anyway?"), line + 1);
5558 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5560 if (aval != G_ALERTALTERNATE) {
5566 if (encoding != ENC_UNKNOWN)
5567 procmime_encode_content(mimetext, encoding);
5569 /* append attachment parts */
5570 if (compose_use_attach(compose) && attach_parts) {
5571 MimeInfo *mimempart;
5572 gchar *boundary = NULL;
5573 mimempart = procmime_mimeinfo_new();
5574 mimempart->content = MIMECONTENT_EMPTY;
5575 mimempart->type = MIMETYPE_MULTIPART;
5576 mimempart->subtype = g_strdup("mixed");
5580 boundary = generate_mime_boundary(NULL);
5581 } while (strstr(buf, boundary) != NULL);
5583 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5586 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5588 g_node_append(mimempart->node, mimetext->node);
5589 g_node_append(mimemsg->node, mimempart->node);
5591 if (compose_add_attachments(compose, mimempart) < 0)
5594 g_node_append(mimemsg->node, mimetext->node);
5598 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5599 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5600 /* extract name and address */
5601 if (strstr(spec, " <") && strstr(spec, ">")) {
5602 from_addr = g_strdup(strrchr(spec, '<')+1);
5603 *(strrchr(from_addr, '>')) = '\0';
5604 from_name = g_strdup(spec);
5605 *(strrchr(from_name, '<')) = '\0';
5612 /* sign message if sending */
5613 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5614 privacy_system_can_sign(compose->privacy_system))
5615 if (!privacy_sign(compose->privacy_system, mimemsg,
5616 compose->account, from_addr)) {
5623 procmime_write_mimeinfo(mimemsg, fp);
5625 procmime_mimeinfo_free_all(mimemsg);
5630 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5632 GtkTextBuffer *buffer;
5633 GtkTextIter start, end;
5638 if ((fp = g_fopen(file, "wb")) == NULL) {
5639 FILE_OP_ERROR(file, "fopen");
5643 /* chmod for security */
5644 if (change_file_mode_rw(fp, file) < 0) {
5645 FILE_OP_ERROR(file, "chmod");
5646 g_warning("can't change file mode\n");
5649 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5650 gtk_text_buffer_get_start_iter(buffer, &start);
5651 gtk_text_buffer_get_end_iter(buffer, &end);
5652 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5654 chars = conv_codeset_strdup
5655 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5658 if (!chars) return -1;
5661 len = strlen(chars);
5662 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5663 FILE_OP_ERROR(file, "fwrite");
5672 if (fclose(fp) == EOF) {
5673 FILE_OP_ERROR(file, "fclose");
5680 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5683 MsgInfo *msginfo = compose->targetinfo;
5685 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5686 if (!msginfo) return -1;
5688 if (!force && MSG_IS_LOCKED(msginfo->flags))
5691 item = msginfo->folder;
5692 cm_return_val_if_fail(item != NULL, -1);
5694 if (procmsg_msg_exist(msginfo) &&
5695 (folder_has_parent_of_type(item, F_QUEUE) ||
5696 folder_has_parent_of_type(item, F_DRAFT)
5697 || msginfo == compose->autosaved_draft)) {
5698 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5699 g_warning("can't remove the old message\n");
5702 debug_print("removed reedit target %d\n", msginfo->msgnum);
5709 static void compose_remove_draft(Compose *compose)
5712 MsgInfo *msginfo = compose->targetinfo;
5713 drafts = account_get_special_folder(compose->account, F_DRAFT);
5715 if (procmsg_msg_exist(msginfo)) {
5716 folder_item_remove_msg(drafts, msginfo->msgnum);
5721 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5722 gboolean remove_reedit_target)
5724 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5727 static gboolean compose_warn_encryption(Compose *compose)
5729 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5730 AlertValue val = G_ALERTALTERNATE;
5732 if (warning == NULL)
5735 val = alertpanel_full(_("Encryption warning"), warning,
5736 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5737 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5738 if (val & G_ALERTDISABLE) {
5739 val &= ~G_ALERTDISABLE;
5740 if (val == G_ALERTALTERNATE)
5741 privacy_inhibit_encrypt_warning(compose->privacy_system,
5745 if (val == G_ALERTALTERNATE) {
5752 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5753 gchar **msgpath, gboolean check_subject,
5754 gboolean remove_reedit_target)
5761 static gboolean lock = FALSE;
5762 PrefsAccount *mailac = NULL, *newsac = NULL;
5763 gboolean err = FALSE;
5765 debug_print("queueing message...\n");
5766 cm_return_val_if_fail(compose->account != NULL, -1);
5770 if (compose_check_entries(compose, check_subject) == FALSE) {
5772 if (compose->batch) {
5773 gtk_widget_show_all(compose->window);
5778 if (!compose->to_list && !compose->newsgroup_list) {
5779 g_warning("can't get recipient list.");
5784 if (compose->to_list) {
5785 if (compose->account->protocol != A_NNTP)
5786 mailac = compose->account;
5787 else if (cur_account && cur_account->protocol != A_NNTP)
5788 mailac = cur_account;
5789 else if (!(mailac = compose_current_mail_account())) {
5791 alertpanel_error(_("No account for sending mails available!"));
5796 if (compose->newsgroup_list) {
5797 if (compose->account->protocol == A_NNTP)
5798 newsac = compose->account;
5801 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5806 /* write queue header */
5807 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5808 G_DIR_SEPARATOR, compose, (guint) rand());
5809 debug_print("queuing to %s\n", tmp);
5810 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5811 FILE_OP_ERROR(tmp, "fopen");
5817 if (change_file_mode_rw(fp, tmp) < 0) {
5818 FILE_OP_ERROR(tmp, "chmod");
5819 g_warning("can't change file mode\n");
5822 /* queueing variables */
5823 err |= (fprintf(fp, "AF:\n") < 0);
5824 err |= (fprintf(fp, "NF:0\n") < 0);
5825 err |= (fprintf(fp, "PS:10\n") < 0);
5826 err |= (fprintf(fp, "SRH:1\n") < 0);
5827 err |= (fprintf(fp, "SFN:\n") < 0);
5828 err |= (fprintf(fp, "DSR:\n") < 0);
5830 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5832 err |= (fprintf(fp, "MID:\n") < 0);
5833 err |= (fprintf(fp, "CFG:\n") < 0);
5834 err |= (fprintf(fp, "PT:0\n") < 0);
5835 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5836 err |= (fprintf(fp, "RQ:\n") < 0);
5838 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5840 err |= (fprintf(fp, "SSV:\n") < 0);
5842 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5844 err |= (fprintf(fp, "NSV:\n") < 0);
5845 err |= (fprintf(fp, "SSH:\n") < 0);
5846 /* write recepient list */
5847 if (compose->to_list) {
5848 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5849 for (cur = compose->to_list->next; cur != NULL;
5851 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5852 err |= (fprintf(fp, "\n") < 0);
5854 /* write newsgroup list */
5855 if (compose->newsgroup_list) {
5856 err |= (fprintf(fp, "NG:") < 0);
5857 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5858 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5859 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5860 err |= (fprintf(fp, "\n") < 0);
5862 /* Sylpheed account IDs */
5864 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5866 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5869 if (compose->privacy_system != NULL) {
5870 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5871 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5872 if (compose->use_encryption) {
5874 if (!compose_warn_encryption(compose)) {
5881 if (mailac && mailac->encrypt_to_self) {
5882 GSList *tmp_list = g_slist_copy(compose->to_list);
5883 tmp_list = g_slist_append(tmp_list, compose->account->address);
5884 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5885 g_slist_free(tmp_list);
5887 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5889 if (encdata != NULL) {
5890 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5891 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5892 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5894 } /* else we finally dont want to encrypt */
5896 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5897 /* and if encdata was null, it means there's been a problem in
5909 /* Save copy folder */
5910 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5911 gchar *savefolderid;
5913 savefolderid = compose_get_save_to(compose);
5914 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5915 g_free(savefolderid);
5917 /* Save copy folder */
5918 if (compose->return_receipt) {
5919 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5921 /* Message-ID of message replying to */
5922 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5925 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5926 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5929 /* Message-ID of message forwarding to */
5930 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5933 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5934 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5938 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5939 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5941 /* end of headers */
5942 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5944 if (compose->redirect_filename != NULL) {
5945 if (compose_redirect_write_to_file(compose, fp) < 0) {
5954 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5959 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5963 g_warning("failed to write queue message\n");
5970 if (fclose(fp) == EOF) {
5971 FILE_OP_ERROR(tmp, "fclose");
5978 if (item && *item) {
5981 queue = account_get_special_folder(compose->account, F_QUEUE);
5984 g_warning("can't find queue folder\n");
5990 folder_item_scan(queue);
5991 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5992 g_warning("can't queue the message\n");
5999 if (msgpath == NULL) {
6005 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
6006 compose_remove_reedit_target(compose, FALSE);
6009 if ((msgnum != NULL) && (item != NULL)) {
6017 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
6020 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
6022 struct stat statbuf;
6023 gchar *type, *subtype;
6024 GtkTreeModel *model;
6027 model = gtk_tree_view_get_model(tree_view);
6029 if (!gtk_tree_model_get_iter_first(model, &iter))
6032 gtk_tree_model_get(model, &iter,
6036 if (!is_file_exist(ainfo->file)) {
6037 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
6038 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
6039 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
6041 if (val == G_ALERTDEFAULT) {
6046 mimepart = procmime_mimeinfo_new();
6047 mimepart->content = MIMECONTENT_FILE;
6048 mimepart->data.filename = g_strdup(ainfo->file);
6049 mimepart->tmp = FALSE; /* or we destroy our attachment */
6050 mimepart->offset = 0;
6052 g_stat(ainfo->file, &statbuf);
6053 mimepart->length = statbuf.st_size;
6055 type = g_strdup(ainfo->content_type);
6057 if (!strchr(type, '/')) {
6059 type = g_strdup("application/octet-stream");
6062 subtype = strchr(type, '/') + 1;
6063 *(subtype - 1) = '\0';
6064 mimepart->type = procmime_get_media_type(type);
6065 mimepart->subtype = g_strdup(subtype);
6068 if (mimepart->type == MIMETYPE_MESSAGE &&
6069 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
6070 mimepart->disposition = DISPOSITIONTYPE_INLINE;
6071 } else if (mimepart->type == MIMETYPE_TEXT) {
6072 if (!ainfo->name && compose->mode == COMPOSE_FORWARD_INLINE) {
6073 /* Text parts with no name come from multipart/alternative
6074 * forwards. Make sure the recipient won't look at the
6075 * original HTML part by mistake. */
6076 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6077 ainfo->name = g_strdup_printf(_("Original %s part"),
6081 g_hash_table_insert(mimepart->typeparameters,
6082 g_strdup("charset"), g_strdup(ainfo->charset));
6084 if (ainfo->name && mimepart->type != MIMETYPE_MESSAGE) {
6085 if (mimepart->type == MIMETYPE_APPLICATION &&
6086 !strcmp2(mimepart->subtype, "octet-stream"))
6087 g_hash_table_insert(mimepart->typeparameters,
6088 g_strdup("name"), g_strdup(ainfo->name));
6089 g_hash_table_insert(mimepart->dispositionparameters,
6090 g_strdup("filename"), g_strdup(ainfo->name));
6091 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6094 if (mimepart->type == MIMETYPE_MESSAGE
6095 || mimepart->type == MIMETYPE_MULTIPART)
6096 ainfo->encoding = ENC_BINARY;
6097 else if (compose->use_signing) {
6098 if (ainfo->encoding == ENC_7BIT)
6099 ainfo->encoding = ENC_QUOTED_PRINTABLE;
6100 else if (ainfo->encoding == ENC_8BIT)
6101 ainfo->encoding = ENC_BASE64;
6106 procmime_encode_content(mimepart, ainfo->encoding);
6108 g_node_append(parent->node, mimepart->node);
6109 } while (gtk_tree_model_iter_next(model, &iter));
6114 #define IS_IN_CUSTOM_HEADER(header) \
6115 (compose->account->add_customhdr && \
6116 custom_header_find(compose->account->customhdr_list, header) != NULL)
6118 static void compose_add_headerfield_from_headerlist(Compose *compose,
6120 const gchar *fieldname,
6121 const gchar *seperator)
6123 gchar *str, *fieldname_w_colon;
6124 gboolean add_field = FALSE;
6126 ComposeHeaderEntry *headerentry;
6127 const gchar *headerentryname;
6128 const gchar *trans_fieldname;
6131 if (IS_IN_CUSTOM_HEADER(fieldname))
6134 debug_print("Adding %s-fields\n", fieldname);
6136 fieldstr = g_string_sized_new(64);
6138 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6139 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6141 for (list = compose->header_list; list; list = list->next) {
6142 headerentry = ((ComposeHeaderEntry *)list->data);
6143 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6145 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6146 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6148 if (str[0] != '\0') {
6150 g_string_append(fieldstr, seperator);
6151 g_string_append(fieldstr, str);
6160 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6161 compose_convert_header
6162 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6163 strlen(fieldname) + 2, TRUE);
6164 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6168 g_free(fieldname_w_colon);
6169 g_string_free(fieldstr, TRUE);
6174 static gchar *compose_get_manual_headers_info(Compose *compose)
6176 GString *sh_header = g_string_new(" ");
6178 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6180 for (list = compose->header_list; list; list = list->next) {
6181 ComposeHeaderEntry *headerentry;
6184 gchar *headername_wcolon;
6185 const gchar *headername_trans;
6187 gboolean standard_header = FALSE;
6189 headerentry = ((ComposeHeaderEntry *)list->data);
6191 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6193 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6198 if (!strstr(tmp, ":")) {
6199 headername_wcolon = g_strconcat(tmp, ":", NULL);
6200 headername = g_strdup(tmp);
6202 headername_wcolon = g_strdup(tmp);
6203 headername = g_strdup(strtok(tmp, ":"));
6207 string = std_headers;
6208 while (*string != NULL) {
6209 headername_trans = prefs_common_translated_header_name(*string);
6210 if (!strcmp(headername_trans, headername_wcolon))
6211 standard_header = TRUE;
6214 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6215 g_string_append_printf(sh_header, "%s ", headername);
6217 g_free(headername_wcolon);
6219 g_string_truncate(sh_header, strlen(sh_header->str) - 1); /* remove last space */
6220 return g_string_free(sh_header, FALSE);
6223 static gchar *compose_get_header(Compose *compose)
6225 gchar buf[BUFFSIZE];
6226 const gchar *entry_str;
6230 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6232 gchar *from_name = NULL, *from_address = NULL;
6235 cm_return_val_if_fail(compose->account != NULL, NULL);
6236 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6238 header = g_string_sized_new(64);
6241 get_rfc822_date(buf, sizeof(buf));
6242 g_string_append_printf(header, "Date: %s\n", buf);
6246 if (compose->account->name && *compose->account->name) {
6248 QUOTE_IF_REQUIRED(buf, compose->account->name);
6249 tmp = g_strdup_printf("%s <%s>",
6250 buf, compose->account->address);
6252 tmp = g_strdup_printf("%s",
6253 compose->account->address);
6255 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6256 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6258 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6259 from_address = g_strdup(compose->account->address);
6261 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6262 /* extract name and address */
6263 if (strstr(spec, " <") && strstr(spec, ">")) {
6264 from_address = g_strdup(strrchr(spec, '<')+1);
6265 *(strrchr(from_address, '>')) = '\0';
6266 from_name = g_strdup(spec);
6267 *(strrchr(from_name, '<')) = '\0';
6270 from_address = g_strdup(spec);
6277 if (from_name && *from_name) {
6278 compose_convert_header
6279 (compose, buf, sizeof(buf), from_name,
6280 strlen("From: "), TRUE);
6281 QUOTE_IF_REQUIRED(name, buf);
6283 g_string_append_printf(header, "From: %s <%s>\n",
6284 name, from_address);
6286 g_string_append_printf(header, "From: %s\n", from_address);
6289 g_free(from_address);
6292 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6295 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6298 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6302 * If this account is a NNTP account remove Bcc header from
6303 * message body since it otherwise will be publicly shown
6305 if (compose->account->protocol != A_NNTP)
6306 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6309 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6311 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6314 compose_convert_header(compose, buf, sizeof(buf), str,
6315 strlen("Subject: "), FALSE);
6316 g_string_append_printf(header, "Subject: %s\n", buf);
6322 if (compose->account->set_domain && compose->account->domain) {
6323 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6324 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6325 g_snprintf(buf, sizeof(buf), "%s",
6326 strchr(compose->account->address, '@') ?
6327 strchr(compose->account->address, '@')+1 :
6328 compose->account->address);
6330 g_snprintf(buf, sizeof(buf), "%s", "");
6333 if (compose->account->gen_msgid) {
6335 if (compose->account->msgid_with_addr) {
6336 addr = compose->account->address;
6338 generate_msgid(buf, sizeof(buf), addr);
6339 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6340 compose->msgid = g_strdup(buf);
6342 compose->msgid = NULL;
6345 if (compose->remove_references == FALSE) {
6347 if (compose->inreplyto && compose->to_list)
6348 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6351 if (compose->references)
6352 g_string_append_printf(header, "References: %s\n", compose->references);
6356 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6359 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6362 if (compose->account->organization &&
6363 strlen(compose->account->organization) &&
6364 !IS_IN_CUSTOM_HEADER("Organization")) {
6365 compose_convert_header(compose, buf, sizeof(buf),
6366 compose->account->organization,
6367 strlen("Organization: "), FALSE);
6368 g_string_append_printf(header, "Organization: %s\n", buf);
6371 /* Program version and system info */
6372 if (compose->account->gen_xmailer &&
6373 g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6374 !compose->newsgroup_list) {
6375 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6377 gtk_major_version, gtk_minor_version, gtk_micro_version,
6380 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6381 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6383 gtk_major_version, gtk_minor_version, gtk_micro_version,
6387 /* custom headers */
6388 if (compose->account->add_customhdr) {
6391 for (cur = compose->account->customhdr_list; cur != NULL;
6393 CustomHeader *chdr = (CustomHeader *)cur->data;
6395 if (custom_header_is_allowed(chdr->name)
6396 && chdr->value != NULL
6397 && *(chdr->value) != '\0') {
6398 compose_convert_header
6399 (compose, buf, sizeof(buf),
6401 strlen(chdr->name) + 2, FALSE);
6402 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6407 /* Automatic Faces and X-Faces */
6408 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6409 g_string_append_printf(header, "X-Face: %s\n", buf);
6411 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6412 g_string_append_printf(header, "X-Face: %s\n", buf);
6414 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6415 g_string_append_printf(header, "Face: %s\n", buf);
6417 else if (get_default_face (buf, sizeof(buf)) == 0) {
6418 g_string_append_printf(header, "Face: %s\n", buf);
6422 switch (compose->priority) {
6423 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6424 "X-Priority: 1 (Highest)\n");
6426 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6427 "X-Priority: 2 (High)\n");
6429 case PRIORITY_NORMAL: break;
6430 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6431 "X-Priority: 4 (Low)\n");
6433 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6434 "X-Priority: 5 (Lowest)\n");
6436 default: debug_print("compose: priority unknown : %d\n",
6440 /* Request Return Receipt */
6441 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6442 if (compose->return_receipt) {
6443 if (compose->account->name
6444 && *compose->account->name) {
6445 compose_convert_header(compose, buf, sizeof(buf),
6446 compose->account->name,
6447 strlen("Disposition-Notification-To: "),
6449 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6451 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6455 /* get special headers */
6456 for (list = compose->header_list; list; list = list->next) {
6457 ComposeHeaderEntry *headerentry;
6460 gchar *headername_wcolon;
6461 const gchar *headername_trans;
6464 gboolean standard_header = FALSE;
6466 headerentry = ((ComposeHeaderEntry *)list->data);
6468 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6470 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6475 if (!strstr(tmp, ":")) {
6476 headername_wcolon = g_strconcat(tmp, ":", NULL);
6477 headername = g_strdup(tmp);
6479 headername_wcolon = g_strdup(tmp);
6480 headername = g_strdup(strtok(tmp, ":"));
6484 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6485 Xstrdup_a(headervalue, entry_str, return NULL);
6486 subst_char(headervalue, '\r', ' ');
6487 subst_char(headervalue, '\n', ' ');
6488 string = std_headers;
6489 while (*string != NULL) {
6490 headername_trans = prefs_common_translated_header_name(*string);
6491 if (!strcmp(headername_trans, headername_wcolon))
6492 standard_header = TRUE;
6495 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6496 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6499 g_free(headername_wcolon);
6503 g_string_free(header, FALSE);
6508 #undef IS_IN_CUSTOM_HEADER
6510 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6511 gint header_len, gboolean addr_field)
6513 gchar *tmpstr = NULL;
6514 const gchar *out_codeset = NULL;
6516 cm_return_if_fail(src != NULL);
6517 cm_return_if_fail(dest != NULL);
6519 if (len < 1) return;
6521 tmpstr = g_strdup(src);
6523 subst_char(tmpstr, '\n', ' ');
6524 subst_char(tmpstr, '\r', ' ');
6527 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6528 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6529 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6534 codeconv_set_strict(TRUE);
6535 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6536 conv_get_charset_str(compose->out_encoding));
6537 codeconv_set_strict(FALSE);
6539 if (!dest || *dest == '\0') {
6540 gchar *test_conv_global_out = NULL;
6541 gchar *test_conv_reply = NULL;
6543 /* automatic mode. be automatic. */
6544 codeconv_set_strict(TRUE);
6546 out_codeset = conv_get_outgoing_charset_str();
6548 debug_print("trying to convert to %s\n", out_codeset);
6549 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6552 if (!test_conv_global_out && compose->orig_charset
6553 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6554 out_codeset = compose->orig_charset;
6555 debug_print("failure; trying to convert to %s\n", out_codeset);
6556 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6559 if (!test_conv_global_out && !test_conv_reply) {
6561 out_codeset = CS_INTERNAL;
6562 debug_print("finally using %s\n", out_codeset);
6564 g_free(test_conv_global_out);
6565 g_free(test_conv_reply);
6566 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6568 codeconv_set_strict(FALSE);
6573 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6577 cm_return_if_fail(user_data != NULL);
6579 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6580 g_strstrip(address);
6581 if (*address != '\0') {
6582 gchar *name = procheader_get_fromname(address);
6583 extract_address(address);
6584 #ifndef USE_NEW_ADDRBOOK
6585 addressbook_add_contact(name, address, NULL, NULL);
6587 debug_print("%s: %s\n", name, address);
6588 if (addressadd_selection(name, address, NULL, NULL)) {
6589 debug_print( "addressbook_add_contact - added\n" );
6596 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6598 GtkWidget *menuitem;
6601 cm_return_if_fail(menu != NULL);
6602 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6604 menuitem = gtk_separator_menu_item_new();
6605 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6606 gtk_widget_show(menuitem);
6608 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6609 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6611 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6612 g_strstrip(address);
6613 if (*address == '\0') {
6614 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6617 g_signal_connect(G_OBJECT(menuitem), "activate",
6618 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6619 gtk_widget_show(menuitem);
6622 static void compose_create_header_entry(Compose *compose)
6624 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6631 const gchar *header = NULL;
6632 ComposeHeaderEntry *headerentry;
6633 gboolean standard_header = FALSE;
6634 GtkListStore *model;
6636 #if !(GTK_CHECK_VERSION(2,12,0))
6637 GtkTooltips *tips = compose->tooltips;
6640 headerentry = g_new0(ComposeHeaderEntry, 1);
6643 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6644 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6645 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6647 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6649 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6651 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6652 COMPOSE_NEWSGROUPS);
6653 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6655 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6656 COMPOSE_FOLLOWUPTO);
6658 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6659 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo))), "grab_focus",
6660 G_CALLBACK(compose_grab_focus_cb), compose);
6661 gtk_widget_show(combo);
6664 l = g_list_prepend(l, gtk_bin_get_child(GTK_BIN(combo)));
6665 gtk_container_set_focus_chain(GTK_CONTAINER(combo), l);
6668 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6669 compose->header_nextrow, compose->header_nextrow+1,
6670 GTK_SHRINK, GTK_FILL, 0, 0);
6671 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6672 const gchar *last_header_entry = gtk_entry_get_text(
6673 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6675 while (*string != NULL) {
6676 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6677 standard_header = TRUE;
6680 if (standard_header)
6681 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6683 if (!compose->header_last || !standard_header) {
6684 switch(compose->account->protocol) {
6686 header = prefs_common_translated_header_name("Newsgroups:");
6689 header = prefs_common_translated_header_name("To:");
6694 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6696 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6697 G_CALLBACK(compose_grab_focus_cb), compose);
6699 /* Entry field with cleanup button */
6700 button = gtk_button_new();
6701 gtk_button_set_image(GTK_BUTTON(button),
6702 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6703 gtk_widget_show(button);
6704 CLAWS_SET_TIP(button,
6705 _("Delete entry contents"));
6706 entry = gtk_entry_new();
6707 gtk_widget_show(entry);
6708 CLAWS_SET_TIP(entry,
6709 _("Use <tab> to autocomplete from addressbook"));
6710 hbox = gtk_hbox_new (FALSE, 0);
6711 gtk_widget_show(hbox);
6712 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6713 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6714 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6715 compose->header_nextrow, compose->header_nextrow+1,
6716 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6718 g_signal_connect(G_OBJECT(entry), "key-press-event",
6719 G_CALLBACK(compose_headerentry_key_press_event_cb),
6721 g_signal_connect(G_OBJECT(entry), "changed",
6722 G_CALLBACK(compose_headerentry_changed_cb),
6724 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6725 G_CALLBACK(compose_grab_focus_cb), compose);
6727 g_signal_connect(G_OBJECT(button), "clicked",
6728 G_CALLBACK(compose_headerentry_button_clicked_cb),
6732 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6733 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6734 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6735 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6736 G_CALLBACK(compose_header_drag_received_cb),
6738 g_signal_connect(G_OBJECT(entry), "drag-drop",
6739 G_CALLBACK(compose_drag_drop),
6741 g_signal_connect(G_OBJECT(entry), "populate-popup",
6742 G_CALLBACK(compose_entry_popup_extend),
6745 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6747 headerentry->compose = compose;
6748 headerentry->combo = combo;
6749 headerentry->entry = entry;
6750 headerentry->button = button;
6751 headerentry->hbox = hbox;
6752 headerentry->headernum = compose->header_nextrow;
6753 headerentry->type = PREF_NONE;
6755 compose->header_nextrow++;
6756 compose->header_last = headerentry;
6757 compose->header_list =
6758 g_slist_append(compose->header_list,
6762 static void compose_add_header_entry(Compose *compose, const gchar *header,
6763 gchar *text, ComposePrefType pref_type)
6765 ComposeHeaderEntry *last_header = compose->header_last;
6766 gchar *tmp = g_strdup(text), *email;
6767 gboolean replyto_hdr;
6769 replyto_hdr = (!strcasecmp(header,
6770 prefs_common_translated_header_name("Reply-To:")) ||
6772 prefs_common_translated_header_name("Followup-To:")) ||
6774 prefs_common_translated_header_name("In-Reply-To:")));
6776 extract_address(tmp);
6777 email = g_utf8_strdown(tmp, -1);
6779 if (replyto_hdr == FALSE &&
6780 g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6782 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6783 header, text, (gint) pref_type);
6789 if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6790 gtk_entry_set_text(GTK_ENTRY(
6791 gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6793 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6794 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6795 last_header->type = pref_type;
6797 if (replyto_hdr == FALSE)
6798 g_hash_table_insert(compose->email_hashtable, email,
6799 GUINT_TO_POINTER(1));
6806 static void compose_destroy_headerentry(Compose *compose,
6807 ComposeHeaderEntry *headerentry)
6809 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6812 extract_address(text);
6813 email = g_utf8_strdown(text, -1);
6814 g_hash_table_remove(compose->email_hashtable, email);
6818 gtk_widget_destroy(headerentry->combo);
6819 gtk_widget_destroy(headerentry->entry);
6820 gtk_widget_destroy(headerentry->button);
6821 gtk_widget_destroy(headerentry->hbox);
6822 g_free(headerentry);
6825 static void compose_remove_header_entries(Compose *compose)
6828 for (list = compose->header_list; list; list = list->next)
6829 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6831 compose->header_last = NULL;
6832 g_slist_free(compose->header_list);
6833 compose->header_list = NULL;
6834 compose->header_nextrow = 1;
6835 compose_create_header_entry(compose);
6838 static GtkWidget *compose_create_header(Compose *compose)
6840 GtkWidget *from_optmenu_hbox;
6841 GtkWidget *header_scrolledwin_main;
6842 GtkWidget *header_table_main;
6843 GtkWidget *header_scrolledwin;
6844 GtkWidget *header_table;
6846 /* parent with account selection and from header */
6847 header_scrolledwin_main = gtk_scrolled_window_new(NULL, NULL);
6848 gtk_widget_show(header_scrolledwin_main);
6849 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin_main), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6851 header_table_main = gtk_table_new(2, 2, FALSE);
6852 gtk_widget_show(header_table_main);
6853 gtk_container_set_border_width(GTK_CONTAINER(header_table_main), BORDER_WIDTH);
6854 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin_main), header_table_main);
6855 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin_main)))), GTK_SHADOW_NONE);
6857 from_optmenu_hbox = compose_account_option_menu_create(compose);
6858 gtk_table_attach(GTK_TABLE(header_table_main), from_optmenu_hbox,
6859 0, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6861 /* child with header labels and entries */
6862 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6863 gtk_widget_show(header_scrolledwin);
6864 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6866 header_table = gtk_table_new(2, 2, FALSE);
6867 gtk_widget_show(header_table);
6868 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6869 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6870 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6872 gtk_table_attach(GTK_TABLE(header_table_main), header_scrolledwin,
6873 0, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 2);
6875 compose->header_table = header_table;
6876 compose->header_list = NULL;
6877 compose->header_nextrow = 0;
6879 compose_create_header_entry(compose);
6881 compose->table = NULL;
6883 return header_scrolledwin_main;
6886 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6888 Compose *compose = (Compose *)data;
6889 GdkEventButton event;
6892 event.time = gtk_get_current_event_time();
6894 return attach_button_pressed(compose->attach_clist, &event, compose);
6897 static GtkWidget *compose_create_attach(Compose *compose)
6899 GtkWidget *attach_scrwin;
6900 GtkWidget *attach_clist;
6902 GtkListStore *store;
6903 GtkCellRenderer *renderer;
6904 GtkTreeViewColumn *column;
6905 GtkTreeSelection *selection;
6907 /* attachment list */
6908 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6909 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6910 GTK_POLICY_AUTOMATIC,
6911 GTK_POLICY_AUTOMATIC);
6912 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6914 store = gtk_list_store_new(N_ATTACH_COLS,
6920 G_TYPE_AUTO_POINTER,
6922 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6923 (GTK_TREE_MODEL(store)));
6924 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6925 g_object_unref(store);
6927 renderer = gtk_cell_renderer_text_new();
6928 column = gtk_tree_view_column_new_with_attributes
6929 (_("Mime type"), renderer, "text",
6930 COL_MIMETYPE, NULL);
6931 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6933 renderer = gtk_cell_renderer_text_new();
6934 column = gtk_tree_view_column_new_with_attributes
6935 (_("Size"), renderer, "text",
6937 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6939 renderer = gtk_cell_renderer_text_new();
6940 column = gtk_tree_view_column_new_with_attributes
6941 (_("Name"), renderer, "text",
6943 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6945 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6946 prefs_common.use_stripes_everywhere);
6947 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6948 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6950 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6951 G_CALLBACK(attach_selected), compose);
6952 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6953 G_CALLBACK(attach_button_pressed), compose);
6955 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6956 G_CALLBACK(popup_attach_button_pressed), compose);
6958 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6959 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6960 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6961 G_CALLBACK(popup_attach_button_pressed), compose);
6963 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6964 G_CALLBACK(attach_key_pressed), compose);
6967 gtk_drag_dest_set(attach_clist,
6968 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6969 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6970 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6971 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6972 G_CALLBACK(compose_attach_drag_received_cb),
6974 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6975 G_CALLBACK(compose_drag_drop),
6978 compose->attach_scrwin = attach_scrwin;
6979 compose->attach_clist = attach_clist;
6981 return attach_scrwin;
6984 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6985 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6987 static GtkWidget *compose_create_others(Compose *compose)
6990 GtkWidget *savemsg_checkbtn;
6991 GtkWidget *savemsg_combo;
6992 GtkWidget *savemsg_select;
6995 gchar *folderidentifier;
6997 /* Table for settings */
6998 table = gtk_table_new(3, 1, FALSE);
6999 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
7000 gtk_widget_show(table);
7001 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
7004 /* Save Message to folder */
7005 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
7006 gtk_widget_show(savemsg_checkbtn);
7007 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7008 if (account_get_special_folder(compose->account, F_OUTBOX)) {
7009 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
7011 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
7012 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
7014 savemsg_combo = gtk_combo_box_entry_new_text();
7015 compose->savemsg_checkbtn = savemsg_checkbtn;
7016 compose->savemsg_combo = savemsg_combo;
7017 gtk_widget_show(savemsg_combo);
7019 if (prefs_common.compose_save_to_history)
7020 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
7021 prefs_common.compose_save_to_history);
7023 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
7024 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
7025 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
7026 G_CALLBACK(compose_grab_focus_cb), compose);
7027 if (account_get_special_folder(compose->account, F_OUTBOX)) {
7028 folderidentifier = folder_item_get_identifier(account_get_special_folder
7029 (compose->account, F_OUTBOX));
7030 compose_set_save_to(compose, folderidentifier);
7031 g_free(folderidentifier);
7034 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
7035 gtk_widget_show(savemsg_select);
7036 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7037 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
7038 G_CALLBACK(compose_savemsg_select_cb),
7046 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
7048 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
7049 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
7052 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
7057 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
7060 path = folder_item_get_identifier(dest);
7062 compose_set_save_to(compose, path);
7066 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
7067 GdkAtom clip, GtkTextIter *insert_place);
7070 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
7074 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7076 if (event->button == 3) {
7078 GtkTextIter sel_start, sel_end;
7079 gboolean stuff_selected;
7081 /* move the cursor to allow GtkAspell to check the word
7082 * under the mouse */
7083 if (event->x && event->y) {
7084 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7085 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7087 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7090 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
7091 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7094 stuff_selected = gtk_text_buffer_get_selection_bounds(
7096 &sel_start, &sel_end);
7098 gtk_text_buffer_place_cursor (buffer, &iter);
7099 /* reselect stuff */
7101 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
7102 gtk_text_buffer_select_range(buffer,
7103 &sel_start, &sel_end);
7105 return FALSE; /* pass the event so that the right-click goes through */
7108 if (event->button == 2) {
7113 /* get the middle-click position to paste at the correct place */
7114 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7115 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7117 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7120 entry_paste_clipboard(compose, text,
7121 prefs_common.linewrap_pastes,
7122 GDK_SELECTION_PRIMARY, &iter);
7130 static void compose_spell_menu_changed(void *data)
7132 Compose *compose = (Compose *)data;
7134 GtkWidget *menuitem;
7135 GtkWidget *parent_item;
7136 GtkMenu *menu = GTK_MENU(gtk_menu_new());
7139 if (compose->gtkaspell == NULL)
7142 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
7143 "/Menu/Spelling/Options");
7145 /* setting the submenu removes /Spelling/Options from the factory
7146 * so we need to save it */
7148 if (parent_item == NULL) {
7149 parent_item = compose->aspell_options_menu;
7150 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
7152 compose->aspell_options_menu = parent_item;
7154 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
7156 spell_menu = g_slist_reverse(spell_menu);
7157 for (items = spell_menu;
7158 items; items = items->next) {
7159 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
7160 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
7161 gtk_widget_show(GTK_WIDGET(menuitem));
7163 g_slist_free(spell_menu);
7165 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7166 gtk_widget_show(parent_item);
7169 static void compose_dict_changed(void *data)
7171 Compose *compose = (Compose *) data;
7173 if(compose->gtkaspell &&
7174 compose->gtkaspell->recheck_when_changing_dict == FALSE)
7177 gtkaspell_highlight_all(compose->gtkaspell);
7178 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7182 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7184 Compose *compose = (Compose *)data;
7185 GdkEventButton event;
7188 event.time = gtk_get_current_event_time();
7192 return text_clicked(compose->text, &event, compose);
7195 static gboolean compose_force_window_origin = TRUE;
7196 static Compose *compose_create(PrefsAccount *account,
7205 GtkWidget *handlebox;
7207 GtkWidget *notebook;
7209 GtkWidget *attach_hbox;
7210 GtkWidget *attach_lab1;
7211 GtkWidget *attach_lab2;
7216 GtkWidget *subject_hbox;
7217 GtkWidget *subject_frame;
7218 GtkWidget *subject_entry;
7222 GtkWidget *edit_vbox;
7223 GtkWidget *ruler_hbox;
7225 GtkWidget *scrolledwin;
7227 GtkTextBuffer *buffer;
7228 GtkClipboard *clipboard;
7230 UndoMain *undostruct;
7232 gchar *titles[N_ATTACH_COLS];
7233 GtkWidget *popupmenu;
7234 GtkWidget *tmpl_menu;
7235 GtkActionGroup *action_group = NULL;
7238 GtkAspell * gtkaspell = NULL;
7241 static GdkGeometry geometry;
7243 cm_return_val_if_fail(account != NULL, NULL);
7245 debug_print("Creating compose window...\n");
7246 compose = g_new0(Compose, 1);
7248 titles[COL_MIMETYPE] = _("MIME type");
7249 titles[COL_SIZE] = _("Size");
7250 titles[COL_NAME] = _("Name");
7251 titles[COL_CHARSET] = _("Charset");
7253 compose->batch = batch;
7254 compose->account = account;
7255 compose->folder = folder;
7257 compose->mutex = g_mutex_new();
7258 compose->set_cursor_pos = -1;
7260 #if !(GTK_CHECK_VERSION(2,12,0))
7261 compose->tooltips = tips;
7264 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7266 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7267 gtk_widget_set_size_request(window, prefs_common.compose_width,
7268 prefs_common.compose_height);
7270 if (!geometry.max_width) {
7271 geometry.max_width = gdk_screen_width();
7272 geometry.max_height = gdk_screen_height();
7275 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7276 &geometry, GDK_HINT_MAX_SIZE);
7277 if (!geometry.min_width) {
7278 geometry.min_width = 600;
7279 geometry.min_height = 440;
7281 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7282 &geometry, GDK_HINT_MIN_SIZE);
7284 #ifndef GENERIC_UMPC
7285 if (compose_force_window_origin)
7286 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7287 prefs_common.compose_y);
7289 g_signal_connect(G_OBJECT(window), "delete_event",
7290 G_CALLBACK(compose_delete_cb), compose);
7291 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7292 gtk_widget_realize(window);
7294 gtkut_widget_set_composer_icon(window);
7296 vbox = gtk_vbox_new(FALSE, 0);
7297 gtk_container_add(GTK_CONTAINER(window), vbox);
7299 compose->ui_manager = gtk_ui_manager_new();
7300 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7301 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7302 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7303 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7304 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7305 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7306 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7307 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7308 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7309 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7312 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7314 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7317 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7318 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7320 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7322 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7323 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7324 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7327 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7328 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7329 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7330 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7331 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7332 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7333 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7334 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7335 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7336 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM)
7337 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7338 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7341 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7342 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7343 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7345 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7346 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7347 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7349 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7350 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7351 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7352 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7354 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7356 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7357 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7358 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7359 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7360 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7361 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7362 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7363 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7364 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7365 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7366 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7367 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7368 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7369 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7370 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7372 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7374 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7375 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7376 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7377 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7378 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7380 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7382 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7386 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7387 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7388 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7389 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7390 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7391 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7395 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7396 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7397 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7398 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7399 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7401 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7402 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7403 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7404 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7405 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7408 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7409 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7410 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7411 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7412 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7413 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7414 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7416 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7417 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7418 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7419 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7420 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7422 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7424 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7425 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7426 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7427 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7428 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7430 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7431 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)
7432 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)
7433 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7435 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7437 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7438 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)
7439 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)
7441 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7443 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7444 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)
7445 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7447 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7448 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)
7449 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7451 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7453 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7454 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)
7455 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7456 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7457 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7459 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7460 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)
7461 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)
7462 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7463 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7465 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7466 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7467 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7468 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7469 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7470 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7472 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7473 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7474 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)
7476 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7477 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7478 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7482 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7483 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7484 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7485 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7486 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7487 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7490 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7492 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7493 gtk_widget_show_all(menubar);
7495 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7497 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7499 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7502 if (prefs_common.toolbar_detachable) {
7503 handlebox = gtk_handle_box_new();
7505 handlebox = gtk_hbox_new(FALSE, 0);
7507 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7509 gtk_widget_realize(handlebox);
7511 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7514 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7518 vbox2 = gtk_vbox_new(FALSE, 2);
7519 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7520 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7523 notebook = gtk_notebook_new();
7524 gtk_widget_set_size_request(notebook, -1, prefs_common.compose_notebook_height);
7525 gtk_widget_show(notebook);
7527 /* header labels and entries */
7528 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7529 compose_create_header(compose),
7530 gtk_label_new_with_mnemonic(_("Hea_der")));
7531 /* attachment list */
7532 attach_hbox = gtk_hbox_new(FALSE, 0);
7533 gtk_widget_show(attach_hbox);
7535 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7536 gtk_widget_show(attach_lab1);
7537 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7539 attach_lab2 = gtk_label_new("");
7540 gtk_widget_show(attach_lab2);
7541 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7543 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7544 compose_create_attach(compose),
7547 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7548 compose_create_others(compose),
7549 gtk_label_new_with_mnemonic(_("Othe_rs")));
7552 subject_hbox = gtk_hbox_new(FALSE, 0);
7553 gtk_widget_show(subject_hbox);
7555 subject_frame = gtk_frame_new(NULL);
7556 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7557 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7558 gtk_widget_show(subject_frame);
7560 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7561 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7562 gtk_widget_show(subject);
7564 label = gtk_label_new(_("Subject:"));
7565 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7566 gtk_widget_show(label);
7569 subject_entry = claws_spell_entry_new();
7571 subject_entry = gtk_entry_new();
7573 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7574 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7575 G_CALLBACK(compose_grab_focus_cb), compose);
7576 gtk_widget_show(subject_entry);
7577 compose->subject_entry = subject_entry;
7578 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7580 edit_vbox = gtk_vbox_new(FALSE, 0);
7582 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7585 ruler_hbox = gtk_hbox_new(FALSE, 0);
7586 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7588 ruler = gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL);
7589 gtk_shruler_set_range(GTK_SHRULER(ruler), 0.0, 100.0, 1.0);
7590 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7594 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7595 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7596 GTK_POLICY_AUTOMATIC,
7597 GTK_POLICY_AUTOMATIC);
7598 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7600 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7602 text = gtk_text_view_new();
7603 if (prefs_common.show_compose_margin) {
7604 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7605 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7607 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7608 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7609 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7610 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7611 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7613 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7614 g_signal_connect(G_OBJECT(notebook), "size_allocate",
7615 G_CALLBACK(compose_notebook_size_alloc), compose);
7616 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7617 G_CALLBACK(compose_edit_size_alloc),
7619 g_signal_connect(G_OBJECT(buffer), "changed",
7620 G_CALLBACK(compose_changed_cb), compose);
7621 g_signal_connect(G_OBJECT(text), "grab_focus",
7622 G_CALLBACK(compose_grab_focus_cb), compose);
7623 g_signal_connect(G_OBJECT(buffer), "insert_text",
7624 G_CALLBACK(text_inserted), compose);
7625 g_signal_connect(G_OBJECT(text), "button_press_event",
7626 G_CALLBACK(text_clicked), compose);
7628 g_signal_connect(G_OBJECT(text), "popup-menu",
7629 G_CALLBACK(compose_popup_menu), compose);
7631 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7632 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7633 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7634 G_CALLBACK(compose_popup_menu), compose);
7636 g_signal_connect(G_OBJECT(subject_entry), "changed",
7637 G_CALLBACK(compose_changed_cb), compose);
7640 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7641 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7642 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7643 g_signal_connect(G_OBJECT(text), "drag_data_received",
7644 G_CALLBACK(compose_insert_drag_received_cb),
7646 g_signal_connect(G_OBJECT(text), "drag-drop",
7647 G_CALLBACK(compose_drag_drop),
7649 g_signal_connect(G_OBJECT(text), "key-press-event",
7650 G_CALLBACK(completion_set_focus_to_subject),
7652 gtk_widget_show_all(vbox);
7654 /* pane between attach clist and text */
7655 paned = gtk_vpaned_new();
7656 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7658 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7659 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7661 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7663 gtk_paned_add1(GTK_PANED(paned), notebook);
7664 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7665 gtk_widget_show_all(paned);
7668 if (prefs_common.textfont) {
7669 PangoFontDescription *font_desc;
7671 font_desc = pango_font_description_from_string
7672 (prefs_common.textfont);
7674 gtk_widget_modify_font(text, font_desc);
7675 pango_font_description_free(font_desc);
7679 gtk_action_group_add_actions(action_group, compose_popup_entries,
7680 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7681 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7682 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7683 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7684 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7685 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7686 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7688 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7690 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7691 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7692 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7694 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7696 undostruct = undo_init(text);
7697 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7700 address_completion_start(window);
7702 compose->window = window;
7703 compose->vbox = vbox;
7704 compose->menubar = menubar;
7705 compose->handlebox = handlebox;
7707 compose->vbox2 = vbox2;
7709 compose->paned = paned;
7711 compose->attach_label = attach_lab2;
7713 compose->notebook = notebook;
7714 compose->edit_vbox = edit_vbox;
7715 compose->ruler_hbox = ruler_hbox;
7716 compose->ruler = ruler;
7717 compose->scrolledwin = scrolledwin;
7718 compose->text = text;
7720 compose->focused_editable = NULL;
7722 compose->popupmenu = popupmenu;
7724 compose->tmpl_menu = tmpl_menu;
7726 compose->mode = mode;
7727 compose->rmode = mode;
7729 compose->targetinfo = NULL;
7730 compose->replyinfo = NULL;
7731 compose->fwdinfo = NULL;
7733 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7734 g_str_equal, (GDestroyNotify) g_free, NULL);
7736 compose->replyto = NULL;
7738 compose->bcc = NULL;
7739 compose->followup_to = NULL;
7741 compose->ml_post = NULL;
7743 compose->inreplyto = NULL;
7744 compose->references = NULL;
7745 compose->msgid = NULL;
7746 compose->boundary = NULL;
7748 compose->autowrap = prefs_common.autowrap;
7749 compose->autoindent = prefs_common.auto_indent;
7750 compose->use_signing = FALSE;
7751 compose->use_encryption = FALSE;
7752 compose->privacy_system = NULL;
7754 compose->modified = FALSE;
7756 compose->return_receipt = FALSE;
7758 compose->to_list = NULL;
7759 compose->newsgroup_list = NULL;
7761 compose->undostruct = undostruct;
7763 compose->sig_str = NULL;
7765 compose->exteditor_file = NULL;
7766 compose->exteditor_pid = -1;
7767 compose->exteditor_tag = -1;
7768 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7771 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7772 if (mode != COMPOSE_REDIRECT) {
7773 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7774 strcmp(prefs_common.dictionary, "")) {
7775 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7776 prefs_common.alt_dictionary,
7777 conv_get_locale_charset_str(),
7778 prefs_common.misspelled_col,
7779 prefs_common.check_while_typing,
7780 prefs_common.recheck_when_changing_dict,
7781 prefs_common.use_alternate,
7782 prefs_common.use_both_dicts,
7783 GTK_TEXT_VIEW(text),
7784 GTK_WINDOW(compose->window),
7785 compose_dict_changed,
7786 compose_spell_menu_changed,
7789 alertpanel_error(_("Spell checker could not "
7791 gtkaspell_checkers_strerror());
7792 gtkaspell_checkers_reset_error();
7794 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7798 compose->gtkaspell = gtkaspell;
7799 compose_spell_menu_changed(compose);
7800 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7803 compose_select_account(compose, account, TRUE);
7805 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7806 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7808 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7809 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7811 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7812 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7814 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7815 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7817 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7818 if (account->protocol != A_NNTP)
7819 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7820 prefs_common_translated_header_name("To:"));
7822 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7823 prefs_common_translated_header_name("Newsgroups:"));
7825 #ifndef USE_NEW_ADDRBOOK
7826 addressbook_set_target_compose(compose);
7828 if (mode != COMPOSE_REDIRECT)
7829 compose_set_template_menu(compose);
7831 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7834 compose_list = g_list_append(compose_list, compose);
7836 if (!prefs_common.show_ruler)
7837 gtk_widget_hide(ruler_hbox);
7839 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7842 compose->priority = PRIORITY_NORMAL;
7843 compose_update_priority_menu_item(compose);
7845 compose_set_out_encoding(compose);
7848 compose_update_actions_menu(compose);
7850 /* Privacy Systems menu */
7851 compose_update_privacy_systems_menu(compose);
7853 activate_privacy_system(compose, account, TRUE);
7854 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7856 gtk_widget_realize(window);
7858 gtk_widget_show(window);
7860 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7861 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7868 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7873 GtkWidget *optmenubox;
7876 GtkWidget *from_name = NULL;
7877 #if !(GTK_CHECK_VERSION(2,12,0))
7878 GtkTooltips *tips = compose->tooltips;
7881 gint num = 0, def_menu = 0;
7883 accounts = account_get_list();
7884 cm_return_val_if_fail(accounts != NULL, NULL);
7886 optmenubox = gtk_event_box_new();
7887 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7888 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7890 hbox = gtk_hbox_new(FALSE, 6);
7891 from_name = gtk_entry_new();
7893 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7894 G_CALLBACK(compose_grab_focus_cb), compose);
7896 for (; accounts != NULL; accounts = accounts->next, num++) {
7897 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7898 gchar *name, *from = NULL;
7900 if (ac == compose->account) def_menu = num;
7902 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7905 if (ac == compose->account) {
7906 if (ac->name && *ac->name) {
7908 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7909 from = g_strdup_printf("%s <%s>",
7911 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7913 from = g_strdup_printf("%s",
7915 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7918 COMBOBOX_ADD(menu, name, ac->account_id);
7923 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7925 g_signal_connect(G_OBJECT(optmenu), "changed",
7926 G_CALLBACK(account_activated),
7928 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7929 G_CALLBACK(compose_entry_popup_extend),
7932 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7933 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7935 CLAWS_SET_TIP(optmenubox,
7936 _("Account to use for this email"));
7937 CLAWS_SET_TIP(from_name,
7938 _("Sender address to be used"));
7940 compose->account_combo = optmenu;
7941 compose->from_name = from_name;
7946 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7948 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7949 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7950 Compose *compose = (Compose *) data;
7952 compose->priority = value;
7956 static void compose_reply_change_mode(Compose *compose,
7959 gboolean was_modified = compose->modified;
7961 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7963 cm_return_if_fail(compose->replyinfo != NULL);
7965 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7967 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7969 if (action == COMPOSE_REPLY_TO_ALL)
7971 if (action == COMPOSE_REPLY_TO_SENDER)
7973 if (action == COMPOSE_REPLY_TO_LIST)
7976 compose_remove_header_entries(compose);
7977 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7978 if (compose->account->set_autocc && compose->account->auto_cc)
7979 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7981 if (compose->account->set_autobcc && compose->account->auto_bcc)
7982 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7984 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7985 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7986 compose_show_first_last_header(compose, TRUE);
7987 compose->modified = was_modified;
7988 compose_set_title(compose);
7991 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7993 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7994 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7995 Compose *compose = (Compose *) data;
7998 compose_reply_change_mode(compose, value);
8001 static void compose_update_priority_menu_item(Compose * compose)
8003 GtkWidget *menuitem = NULL;
8004 switch (compose->priority) {
8005 case PRIORITY_HIGHEST:
8006 menuitem = gtk_ui_manager_get_widget
8007 (compose->ui_manager, "/Menu/Options/Priority/Highest");
8010 menuitem = gtk_ui_manager_get_widget
8011 (compose->ui_manager, "/Menu/Options/Priority/High");
8013 case PRIORITY_NORMAL:
8014 menuitem = gtk_ui_manager_get_widget
8015 (compose->ui_manager, "/Menu/Options/Priority/Normal");
8018 menuitem = gtk_ui_manager_get_widget
8019 (compose->ui_manager, "/Menu/Options/Priority/Low");
8021 case PRIORITY_LOWEST:
8022 menuitem = gtk_ui_manager_get_widget
8023 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
8026 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8029 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
8031 Compose *compose = (Compose *) data;
8033 gboolean can_sign = FALSE, can_encrypt = FALSE;
8035 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
8037 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
8040 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
8041 g_free(compose->privacy_system);
8042 compose->privacy_system = NULL;
8043 if (systemid != NULL) {
8044 compose->privacy_system = g_strdup(systemid);
8046 can_sign = privacy_system_can_sign(systemid);
8047 can_encrypt = privacy_system_can_encrypt(systemid);
8050 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
8052 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8053 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8056 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
8058 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8059 GtkWidget *menuitem = NULL;
8060 GList *children, *amenu;
8061 gboolean can_sign = FALSE, can_encrypt = FALSE;
8062 gboolean found = FALSE;
8064 if (compose->privacy_system != NULL) {
8066 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8067 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
8068 cm_return_if_fail(menuitem != NULL);
8070 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
8073 while (amenu != NULL) {
8074 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
8075 if (systemid != NULL) {
8076 if (strcmp(systemid, compose->privacy_system) == 0 &&
8077 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8078 menuitem = GTK_WIDGET(amenu->data);
8080 can_sign = privacy_system_can_sign(systemid);
8081 can_encrypt = privacy_system_can_encrypt(systemid);
8085 } else if (strlen(compose->privacy_system) == 0 &&
8086 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8087 menuitem = GTK_WIDGET(amenu->data);
8090 can_encrypt = FALSE;
8095 amenu = amenu->next;
8097 g_list_free(children);
8098 if (menuitem != NULL)
8099 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8101 if (warn && !found && strlen(compose->privacy_system)) {
8102 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8103 "will not be able to sign or encrypt this message."),
8104 compose->privacy_system);
8108 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8109 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8112 static void compose_set_out_encoding(Compose *compose)
8114 CharSet out_encoding;
8115 const gchar *branch = NULL;
8116 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
8118 switch(out_encoding) {
8119 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8120 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
8121 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
8122 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
8123 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
8124 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
8125 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
8126 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
8127 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
8128 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
8129 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
8130 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
8131 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
8132 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
8133 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
8134 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
8135 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
8136 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
8137 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
8138 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
8139 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
8140 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
8141 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
8142 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
8143 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
8144 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
8145 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
8146 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
8147 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
8148 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
8149 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
8150 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
8151 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8153 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8156 static void compose_set_template_menu(Compose *compose)
8158 GSList *tmpl_list, *cur;
8162 tmpl_list = template_get_config();
8164 menu = gtk_menu_new();
8166 gtk_menu_set_accel_group (GTK_MENU (menu),
8167 gtk_ui_manager_get_accel_group(compose->ui_manager));
8168 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8169 Template *tmpl = (Template *)cur->data;
8170 gchar *accel_path = NULL;
8171 item = gtk_menu_item_new_with_label(tmpl->name);
8172 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8173 g_signal_connect(G_OBJECT(item), "activate",
8174 G_CALLBACK(compose_template_activate_cb),
8176 g_object_set_data(G_OBJECT(item), "template", tmpl);
8177 gtk_widget_show(item);
8178 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8179 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8183 gtk_widget_show(menu);
8184 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8187 void compose_update_actions_menu(Compose *compose)
8189 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8192 static void compose_update_privacy_systems_menu(Compose *compose)
8194 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8195 GSList *systems, *cur;
8197 GtkWidget *system_none;
8199 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8200 GtkWidget *privacy_menu = gtk_menu_new();
8202 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8203 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8205 g_signal_connect(G_OBJECT(system_none), "activate",
8206 G_CALLBACK(compose_set_privacy_system_cb), compose);
8208 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8209 gtk_widget_show(system_none);
8211 systems = privacy_get_system_ids();
8212 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8213 gchar *systemid = cur->data;
8215 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8216 widget = gtk_radio_menu_item_new_with_label(group,
8217 privacy_system_get_name(systemid));
8218 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8219 g_strdup(systemid), g_free);
8220 g_signal_connect(G_OBJECT(widget), "activate",
8221 G_CALLBACK(compose_set_privacy_system_cb), compose);
8223 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8224 gtk_widget_show(widget);
8227 g_slist_free(systems);
8228 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8229 gtk_widget_show_all(privacy_menu);
8230 gtk_widget_show_all(privacy_menuitem);
8233 void compose_reflect_prefs_all(void)
8238 for (cur = compose_list; cur != NULL; cur = cur->next) {
8239 compose = (Compose *)cur->data;
8240 compose_set_template_menu(compose);
8244 void compose_reflect_prefs_pixmap_theme(void)
8249 for (cur = compose_list; cur != NULL; cur = cur->next) {
8250 compose = (Compose *)cur->data;
8251 toolbar_update(TOOLBAR_COMPOSE, compose);
8255 static const gchar *compose_quote_char_from_context(Compose *compose)
8257 const gchar *qmark = NULL;
8259 cm_return_val_if_fail(compose != NULL, NULL);
8261 switch (compose->mode) {
8262 /* use forward-specific quote char */
8263 case COMPOSE_FORWARD:
8264 case COMPOSE_FORWARD_AS_ATTACH:
8265 case COMPOSE_FORWARD_INLINE:
8266 if (compose->folder && compose->folder->prefs &&
8267 compose->folder->prefs->forward_with_format)
8268 qmark = compose->folder->prefs->forward_quotemark;
8269 else if (compose->account->forward_with_format)
8270 qmark = compose->account->forward_quotemark;
8272 qmark = prefs_common.fw_quotemark;
8275 /* use reply-specific quote char in all other modes */
8277 if (compose->folder && compose->folder->prefs &&
8278 compose->folder->prefs->reply_with_format)
8279 qmark = compose->folder->prefs->reply_quotemark;
8280 else if (compose->account->reply_with_format)
8281 qmark = compose->account->reply_quotemark;
8283 qmark = prefs_common.quotemark;
8287 if (qmark == NULL || *qmark == '\0')
8293 static void compose_template_apply(Compose *compose, Template *tmpl,
8297 GtkTextBuffer *buffer;
8301 gchar *parsed_str = NULL;
8302 gint cursor_pos = 0;
8303 const gchar *err_msg = _("The body of the template has an error at line %d.");
8306 /* process the body */
8308 text = GTK_TEXT_VIEW(compose->text);
8309 buffer = gtk_text_view_get_buffer(text);
8312 qmark = compose_quote_char_from_context(compose);
8314 if (compose->replyinfo != NULL) {
8317 gtk_text_buffer_set_text(buffer, "", -1);
8318 mark = gtk_text_buffer_get_insert(buffer);
8319 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8321 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8322 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8324 } else if (compose->fwdinfo != NULL) {
8327 gtk_text_buffer_set_text(buffer, "", -1);
8328 mark = gtk_text_buffer_get_insert(buffer);
8329 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8331 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8332 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8335 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8337 GtkTextIter start, end;
8340 gtk_text_buffer_get_start_iter(buffer, &start);
8341 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8342 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8344 /* clear the buffer now */
8346 gtk_text_buffer_set_text(buffer, "", -1);
8348 parsed_str = compose_quote_fmt(compose, dummyinfo,
8349 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8350 procmsg_msginfo_free( dummyinfo );
8356 gtk_text_buffer_set_text(buffer, "", -1);
8357 mark = gtk_text_buffer_get_insert(buffer);
8358 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8361 if (replace && parsed_str && compose->account->auto_sig)
8362 compose_insert_sig(compose, FALSE);
8364 if (replace && parsed_str) {
8365 gtk_text_buffer_get_start_iter(buffer, &iter);
8366 gtk_text_buffer_place_cursor(buffer, &iter);
8370 cursor_pos = quote_fmt_get_cursor_pos();
8371 compose->set_cursor_pos = cursor_pos;
8372 if (cursor_pos == -1)
8374 gtk_text_buffer_get_start_iter(buffer, &iter);
8375 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8376 gtk_text_buffer_place_cursor(buffer, &iter);
8379 /* process the other fields */
8381 compose_template_apply_fields(compose, tmpl);
8382 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8383 quote_fmt_reset_vartable();
8384 compose_changed_cb(NULL, compose);
8387 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8388 gtkaspell_highlight_all(compose->gtkaspell);
8392 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8394 MsgInfo* dummyinfo = NULL;
8395 MsgInfo *msginfo = NULL;
8398 if (compose->replyinfo != NULL)
8399 msginfo = compose->replyinfo;
8400 else if (compose->fwdinfo != NULL)
8401 msginfo = compose->fwdinfo;
8403 dummyinfo = compose_msginfo_new_from_compose(compose);
8404 msginfo = dummyinfo;
8407 if (tmpl->from && *tmpl->from != '\0') {
8409 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8410 compose->gtkaspell);
8412 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8414 quote_fmt_scan_string(tmpl->from);
8417 buf = quote_fmt_get_buffer();
8419 alertpanel_error(_("Template From format error."));
8421 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8425 if (tmpl->to && *tmpl->to != '\0') {
8427 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8428 compose->gtkaspell);
8430 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8432 quote_fmt_scan_string(tmpl->to);
8435 buf = quote_fmt_get_buffer();
8437 alertpanel_error(_("Template To format error."));
8439 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8443 if (tmpl->cc && *tmpl->cc != '\0') {
8445 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8446 compose->gtkaspell);
8448 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8450 quote_fmt_scan_string(tmpl->cc);
8453 buf = quote_fmt_get_buffer();
8455 alertpanel_error(_("Template Cc format error."));
8457 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8461 if (tmpl->bcc && *tmpl->bcc != '\0') {
8463 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8464 compose->gtkaspell);
8466 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8468 quote_fmt_scan_string(tmpl->bcc);
8471 buf = quote_fmt_get_buffer();
8473 alertpanel_error(_("Template Bcc format error."));
8475 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8479 /* process the subject */
8480 if (tmpl->subject && *tmpl->subject != '\0') {
8482 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8483 compose->gtkaspell);
8485 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8487 quote_fmt_scan_string(tmpl->subject);
8490 buf = quote_fmt_get_buffer();
8492 alertpanel_error(_("Template subject format error."));
8494 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8498 procmsg_msginfo_free( dummyinfo );
8501 static void compose_destroy(Compose *compose)
8503 GtkAllocation allocation;
8504 GtkTextBuffer *buffer;
8505 GtkClipboard *clipboard;
8507 compose_list = g_list_remove(compose_list, compose);
8509 if (compose->updating) {
8510 debug_print("danger, not destroying anything now\n");
8511 compose->deferred_destroy = TRUE;
8514 /* NOTE: address_completion_end() does nothing with the window
8515 * however this may change. */
8516 address_completion_end(compose->window);
8518 slist_free_strings(compose->to_list);
8519 g_slist_free(compose->to_list);
8520 slist_free_strings(compose->newsgroup_list);
8521 g_slist_free(compose->newsgroup_list);
8522 slist_free_strings(compose->header_list);
8523 g_slist_free(compose->header_list);
8525 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8527 g_hash_table_destroy(compose->email_hashtable);
8529 procmsg_msginfo_free(compose->targetinfo);
8530 procmsg_msginfo_free(compose->replyinfo);
8531 procmsg_msginfo_free(compose->fwdinfo);
8533 g_free(compose->replyto);
8534 g_free(compose->cc);
8535 g_free(compose->bcc);
8536 g_free(compose->newsgroups);
8537 g_free(compose->followup_to);
8539 g_free(compose->ml_post);
8541 g_free(compose->inreplyto);
8542 g_free(compose->references);
8543 g_free(compose->msgid);
8544 g_free(compose->boundary);
8546 g_free(compose->redirect_filename);
8547 if (compose->undostruct)
8548 undo_destroy(compose->undostruct);
8550 g_free(compose->sig_str);
8552 g_free(compose->exteditor_file);
8554 g_free(compose->orig_charset);
8556 g_free(compose->privacy_system);
8558 #ifndef USE_NEW_ADDRBOOK
8559 if (addressbook_get_target_compose() == compose)
8560 addressbook_set_target_compose(NULL);
8563 if (compose->gtkaspell) {
8564 gtkaspell_delete(compose->gtkaspell);
8565 compose->gtkaspell = NULL;
8569 if (!compose->batch) {
8570 gtk_widget_get_allocation(compose->window, &allocation);
8571 prefs_common.compose_width = allocation.width;
8572 prefs_common.compose_height = allocation.height;
8575 if (!gtk_widget_get_parent(compose->paned))
8576 gtk_widget_destroy(compose->paned);
8577 gtk_widget_destroy(compose->popupmenu);
8579 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8580 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8581 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8583 gtk_widget_destroy(compose->window);
8584 toolbar_destroy(compose->toolbar);
8585 g_free(compose->toolbar);
8586 g_mutex_free(compose->mutex);
8590 static void compose_attach_info_free(AttachInfo *ainfo)
8592 g_free(ainfo->file);
8593 g_free(ainfo->content_type);
8594 g_free(ainfo->name);
8595 g_free(ainfo->charset);
8599 static void compose_attach_update_label(Compose *compose)
8604 GtkTreeModel *model;
8609 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8610 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8611 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8615 while(gtk_tree_model_iter_next(model, &iter))
8618 text = g_strdup_printf("(%d)", i);
8619 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8623 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8625 Compose *compose = (Compose *)data;
8626 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8627 GtkTreeSelection *selection;
8629 GtkTreeModel *model;
8631 selection = gtk_tree_view_get_selection(tree_view);
8632 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8637 for (cur = sel; cur != NULL; cur = cur->next) {
8638 GtkTreePath *path = cur->data;
8639 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8642 gtk_tree_path_free(path);
8645 for (cur = sel; cur != NULL; cur = cur->next) {
8646 GtkTreeRowReference *ref = cur->data;
8647 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8650 if (gtk_tree_model_get_iter(model, &iter, path))
8651 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8653 gtk_tree_path_free(path);
8654 gtk_tree_row_reference_free(ref);
8658 compose_attach_update_label(compose);
8661 static struct _AttachProperty
8664 GtkWidget *mimetype_entry;
8665 GtkWidget *encoding_optmenu;
8666 GtkWidget *path_entry;
8667 GtkWidget *filename_entry;
8669 GtkWidget *cancel_btn;
8672 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8674 gtk_tree_path_free((GtkTreePath *)ptr);
8677 static void compose_attach_property(GtkAction *action, gpointer data)
8679 Compose *compose = (Compose *)data;
8680 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8682 GtkComboBox *optmenu;
8683 GtkTreeSelection *selection;
8685 GtkTreeModel *model;
8688 static gboolean cancelled;
8690 /* only if one selected */
8691 selection = gtk_tree_view_get_selection(tree_view);
8692 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8695 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8699 path = (GtkTreePath *) sel->data;
8700 gtk_tree_model_get_iter(model, &iter, path);
8701 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8704 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8710 if (!attach_prop.window)
8711 compose_attach_property_create(&cancelled);
8712 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8713 gtk_widget_grab_focus(attach_prop.ok_btn);
8714 gtk_widget_show(attach_prop.window);
8715 gtk_window_set_transient_for(GTK_WINDOW(attach_prop.window),
8716 GTK_WINDOW(compose->window));
8718 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8719 if (ainfo->encoding == ENC_UNKNOWN)
8720 combobox_select_by_data(optmenu, ENC_BASE64);
8722 combobox_select_by_data(optmenu, ainfo->encoding);
8724 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8725 ainfo->content_type ? ainfo->content_type : "");
8726 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8727 ainfo->file ? ainfo->file : "");
8728 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8729 ainfo->name ? ainfo->name : "");
8732 const gchar *entry_text;
8734 gchar *cnttype = NULL;
8741 gtk_widget_hide(attach_prop.window);
8742 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8747 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8748 if (*entry_text != '\0') {
8751 text = g_strstrip(g_strdup(entry_text));
8752 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8753 cnttype = g_strdup(text);
8756 alertpanel_error(_("Invalid MIME type."));
8762 ainfo->encoding = combobox_get_active_data(optmenu);
8764 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8765 if (*entry_text != '\0') {
8766 if (is_file_exist(entry_text) &&
8767 (size = get_file_size(entry_text)) > 0)
8768 file = g_strdup(entry_text);
8771 (_("File doesn't exist or is empty."));
8777 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8778 if (*entry_text != '\0') {
8779 g_free(ainfo->name);
8780 ainfo->name = g_strdup(entry_text);
8784 g_free(ainfo->content_type);
8785 ainfo->content_type = cnttype;
8788 g_free(ainfo->file);
8792 ainfo->size = (goffset)size;
8794 /* update tree store */
8795 text = to_human_readable(ainfo->size);
8796 gtk_tree_model_get_iter(model, &iter, path);
8797 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8798 COL_MIMETYPE, ainfo->content_type,
8800 COL_NAME, ainfo->name,
8801 COL_CHARSET, ainfo->charset,
8807 gtk_tree_path_free(path);
8810 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8812 label = gtk_label_new(str); \
8813 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8814 GTK_FILL, 0, 0, 0); \
8815 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8817 entry = gtk_entry_new(); \
8818 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8819 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8822 static void compose_attach_property_create(gboolean *cancelled)
8828 GtkWidget *mimetype_entry;
8831 GtkListStore *optmenu_menu;
8832 GtkWidget *path_entry;
8833 GtkWidget *filename_entry;
8836 GtkWidget *cancel_btn;
8837 GList *mime_type_list, *strlist;
8840 debug_print("Creating attach_property window...\n");
8842 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8843 gtk_widget_set_size_request(window, 480, -1);
8844 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8845 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8846 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8847 g_signal_connect(G_OBJECT(window), "delete_event",
8848 G_CALLBACK(attach_property_delete_event),
8850 g_signal_connect(G_OBJECT(window), "key_press_event",
8851 G_CALLBACK(attach_property_key_pressed),
8854 vbox = gtk_vbox_new(FALSE, 8);
8855 gtk_container_add(GTK_CONTAINER(window), vbox);
8857 table = gtk_table_new(4, 2, FALSE);
8858 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8859 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8860 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8862 label = gtk_label_new(_("MIME type"));
8863 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8865 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8866 mimetype_entry = gtk_combo_box_entry_new_text();
8867 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8868 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8870 /* stuff with list */
8871 mime_type_list = procmime_get_mime_type_list();
8873 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8874 MimeType *type = (MimeType *) mime_type_list->data;
8877 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8879 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8882 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8883 (GCompareFunc)strcmp2);
8886 for (mime_type_list = strlist; mime_type_list != NULL;
8887 mime_type_list = mime_type_list->next) {
8888 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8889 g_free(mime_type_list->data);
8891 g_list_free(strlist);
8892 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8893 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8895 label = gtk_label_new(_("Encoding"));
8896 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8898 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8900 hbox = gtk_hbox_new(FALSE, 0);
8901 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8902 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8904 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8905 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8907 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8908 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8909 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8910 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8911 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8913 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8915 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8916 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8918 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8919 &ok_btn, GTK_STOCK_OK,
8921 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8922 gtk_widget_grab_default(ok_btn);
8924 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8925 G_CALLBACK(attach_property_ok),
8927 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8928 G_CALLBACK(attach_property_cancel),
8931 gtk_widget_show_all(vbox);
8933 attach_prop.window = window;
8934 attach_prop.mimetype_entry = mimetype_entry;
8935 attach_prop.encoding_optmenu = optmenu;
8936 attach_prop.path_entry = path_entry;
8937 attach_prop.filename_entry = filename_entry;
8938 attach_prop.ok_btn = ok_btn;
8939 attach_prop.cancel_btn = cancel_btn;
8942 #undef SET_LABEL_AND_ENTRY
8944 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8950 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8956 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8957 gboolean *cancelled)
8965 static gboolean attach_property_key_pressed(GtkWidget *widget,
8967 gboolean *cancelled)
8969 if (event && event->keyval == GDK_KEY_Escape) {
8973 if (event && event->keyval == GDK_KEY_Return) {
8981 static void compose_exec_ext_editor(Compose *compose)
8988 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8989 G_DIR_SEPARATOR, compose);
8991 if (pipe(pipe_fds) < 0) {
8997 if ((pid = fork()) < 0) {
9004 /* close the write side of the pipe */
9007 compose->exteditor_file = g_strdup(tmp);
9008 compose->exteditor_pid = pid;
9010 compose_set_ext_editor_sensitive(compose, FALSE);
9013 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
9015 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
9017 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
9021 } else { /* process-monitoring process */
9027 /* close the read side of the pipe */
9030 if (compose_write_body_to_file(compose, tmp) < 0) {
9031 fd_write_all(pipe_fds[1], "2\n", 2);
9035 pid_ed = compose_exec_ext_editor_real(tmp);
9037 fd_write_all(pipe_fds[1], "1\n", 2);
9041 /* wait until editor is terminated */
9042 waitpid(pid_ed, NULL, 0);
9044 fd_write_all(pipe_fds[1], "0\n", 2);
9051 #endif /* G_OS_UNIX */
9055 static gint compose_exec_ext_editor_real(const gchar *file)
9062 cm_return_val_if_fail(file != NULL, -1);
9064 if ((pid = fork()) < 0) {
9069 if (pid != 0) return pid;
9071 /* grandchild process */
9073 if (setpgid(0, getppid()))
9076 if (prefs_common_get_ext_editor_cmd() &&
9077 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
9078 *(p + 1) == 's' && !strchr(p + 2, '%')) {
9079 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
9081 if (prefs_common_get_ext_editor_cmd())
9082 g_warning("External editor command-line is invalid: '%s'\n",
9083 prefs_common_get_ext_editor_cmd());
9084 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
9087 cmdline = strsplit_with_quote(buf, " ", 1024);
9088 execvp(cmdline[0], cmdline);
9091 g_strfreev(cmdline);
9096 static gboolean compose_ext_editor_kill(Compose *compose)
9098 pid_t pgid = compose->exteditor_pid * -1;
9101 ret = kill(pgid, 0);
9103 if (ret == 0 || (ret == -1 && EPERM == errno)) {
9107 msg = g_strdup_printf
9108 (_("The external editor is still working.\n"
9109 "Force terminating the process?\n"
9110 "process group id: %d"), -pgid);
9111 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
9112 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
9116 if (val == G_ALERTALTERNATE) {
9117 g_source_remove(compose->exteditor_tag);
9118 g_io_channel_shutdown(compose->exteditor_ch,
9120 g_io_channel_unref(compose->exteditor_ch);
9122 if (kill(pgid, SIGTERM) < 0) perror("kill");
9123 waitpid(compose->exteditor_pid, NULL, 0);
9125 g_warning("Terminated process group id: %d", -pgid);
9126 g_warning("Temporary file: %s",
9127 compose->exteditor_file);
9129 compose_set_ext_editor_sensitive(compose, TRUE);
9131 g_free(compose->exteditor_file);
9132 compose->exteditor_file = NULL;
9133 compose->exteditor_pid = -1;
9134 compose->exteditor_ch = NULL;
9135 compose->exteditor_tag = -1;
9143 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
9147 Compose *compose = (Compose *)data;
9150 debug_print("Compose: input from monitoring process\n");
9152 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
9154 g_io_channel_shutdown(source, FALSE, NULL);
9155 g_io_channel_unref(source);
9157 waitpid(compose->exteditor_pid, NULL, 0);
9159 if (buf[0] == '0') { /* success */
9160 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9161 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9163 gtk_text_buffer_set_text(buffer, "", -1);
9164 compose_insert_file(compose, compose->exteditor_file);
9165 compose_changed_cb(NULL, compose);
9166 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9168 if (claws_unlink(compose->exteditor_file) < 0)
9169 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9170 } else if (buf[0] == '1') { /* failed */
9171 g_warning("Couldn't exec external editor\n");
9172 if (claws_unlink(compose->exteditor_file) < 0)
9173 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9174 } else if (buf[0] == '2') {
9175 g_warning("Couldn't write to file\n");
9176 } else if (buf[0] == '3') {
9177 g_warning("Pipe read failed\n");
9180 compose_set_ext_editor_sensitive(compose, TRUE);
9182 g_free(compose->exteditor_file);
9183 compose->exteditor_file = NULL;
9184 compose->exteditor_pid = -1;
9185 compose->exteditor_ch = NULL;
9186 compose->exteditor_tag = -1;
9191 static void compose_set_ext_editor_sensitive(Compose *compose,
9194 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9195 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9196 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9197 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9198 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9199 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9200 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9202 gtk_widget_set_sensitive(compose->text, sensitive);
9203 if (compose->toolbar->send_btn)
9204 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
9205 if (compose->toolbar->sendl_btn)
9206 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
9207 if (compose->toolbar->draft_btn)
9208 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
9209 if (compose->toolbar->insert_btn)
9210 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
9211 if (compose->toolbar->sig_btn)
9212 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
9213 if (compose->toolbar->exteditor_btn)
9214 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9215 if (compose->toolbar->linewrap_current_btn)
9216 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9217 if (compose->toolbar->linewrap_all_btn)
9218 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9220 #endif /* G_OS_UNIX */
9223 * compose_undo_state_changed:
9225 * Change the sensivity of the menuentries undo and redo
9227 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9228 gint redo_state, gpointer data)
9230 Compose *compose = (Compose *)data;
9232 switch (undo_state) {
9233 case UNDO_STATE_TRUE:
9234 if (!undostruct->undo_state) {
9235 undostruct->undo_state = TRUE;
9236 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9239 case UNDO_STATE_FALSE:
9240 if (undostruct->undo_state) {
9241 undostruct->undo_state = FALSE;
9242 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9245 case UNDO_STATE_UNCHANGED:
9247 case UNDO_STATE_REFRESH:
9248 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9251 g_warning("Undo state not recognized");
9255 switch (redo_state) {
9256 case UNDO_STATE_TRUE:
9257 if (!undostruct->redo_state) {
9258 undostruct->redo_state = TRUE;
9259 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9262 case UNDO_STATE_FALSE:
9263 if (undostruct->redo_state) {
9264 undostruct->redo_state = FALSE;
9265 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9268 case UNDO_STATE_UNCHANGED:
9270 case UNDO_STATE_REFRESH:
9271 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9274 g_warning("Redo state not recognized");
9279 /* callback functions */
9281 static void compose_notebook_size_alloc(GtkNotebook *notebook,
9282 GtkAllocation *allocation,
9285 prefs_common.compose_notebook_height = allocation->height;
9288 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9289 * includes "non-client" (windows-izm) in calculation, so this calculation
9290 * may not be accurate.
9292 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9293 GtkAllocation *allocation,
9294 GtkSHRuler *shruler)
9296 if (prefs_common.show_ruler) {
9297 gint char_width = 0, char_height = 0;
9298 gint line_width_in_chars;
9300 gtkut_get_font_size(GTK_WIDGET(widget),
9301 &char_width, &char_height);
9302 line_width_in_chars =
9303 (allocation->width - allocation->x) / char_width;
9305 /* got the maximum */
9306 gtk_shruler_set_range(GTK_SHRULER(shruler),
9307 0.0, line_width_in_chars, 0);
9316 ComposePrefType type;
9317 gboolean entry_marked;
9320 static void account_activated(GtkComboBox *optmenu, gpointer data)
9322 Compose *compose = (Compose *)data;
9325 gchar *folderidentifier;
9326 gint account_id = 0;
9329 GSList *list, *saved_list = NULL;
9330 HeaderEntryState *state;
9331 GtkRcStyle *style = NULL;
9332 static GdkColor yellow;
9333 static gboolean color_set = FALSE;
9335 /* Get ID of active account in the combo box */
9336 menu = gtk_combo_box_get_model(optmenu);
9337 gtk_combo_box_get_active_iter(optmenu, &iter);
9338 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9340 ac = account_find_from_id(account_id);
9341 cm_return_if_fail(ac != NULL);
9343 if (ac != compose->account) {
9344 compose_select_account(compose, ac, FALSE);
9346 for (list = compose->header_list; list; list = list->next) {
9347 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9349 if (hentry->type == PREF_ACCOUNT || !list->next) {
9350 compose_destroy_headerentry(compose, hentry);
9354 state = g_malloc0(sizeof(HeaderEntryState));
9355 state->header = gtk_editable_get_chars(GTK_EDITABLE(
9356 gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9357 state->entry = gtk_editable_get_chars(
9358 GTK_EDITABLE(hentry->entry), 0, -1);
9359 state->type = hentry->type;
9362 gdk_color_parse("#f5f6be", &yellow);
9363 color_set = gdk_colormap_alloc_color(
9364 gdk_colormap_get_system(),
9365 &yellow, FALSE, TRUE);
9368 style = gtk_widget_get_modifier_style(hentry->entry);
9369 state->entry_marked = gdk_color_equal(&yellow,
9370 &style->base[GTK_STATE_NORMAL]);
9372 saved_list = g_slist_append(saved_list, state);
9373 compose_destroy_headerentry(compose, hentry);
9376 compose->header_last = NULL;
9377 g_slist_free(compose->header_list);
9378 compose->header_list = NULL;
9379 compose->header_nextrow = 1;
9380 compose_create_header_entry(compose);
9382 if (ac->set_autocc && ac->auto_cc)
9383 compose_entry_append(compose, ac->auto_cc,
9384 COMPOSE_CC, PREF_ACCOUNT);
9386 if (ac->set_autobcc && ac->auto_bcc)
9387 compose_entry_append(compose, ac->auto_bcc,
9388 COMPOSE_BCC, PREF_ACCOUNT);
9390 if (ac->set_autoreplyto && ac->auto_replyto)
9391 compose_entry_append(compose, ac->auto_replyto,
9392 COMPOSE_REPLYTO, PREF_ACCOUNT);
9394 for (list = saved_list; list; list = list->next) {
9395 state = (HeaderEntryState *) list->data;
9397 compose_add_header_entry(compose, state->header,
9398 state->entry, state->type);
9399 if (state->entry_marked)
9400 compose_entry_mark_default_to(compose, state->entry);
9402 g_free(state->header);
9403 g_free(state->entry);
9406 g_slist_free(saved_list);
9408 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9409 (ac->protocol == A_NNTP) ?
9410 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9413 /* Set message save folder */
9414 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9415 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9417 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9418 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9420 compose_set_save_to(compose, NULL);
9421 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9422 folderidentifier = folder_item_get_identifier(account_get_special_folder
9423 (compose->account, F_OUTBOX));
9424 compose_set_save_to(compose, folderidentifier);
9425 g_free(folderidentifier);
9429 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9430 GtkTreeViewColumn *column, Compose *compose)
9432 compose_attach_property(NULL, compose);
9435 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9438 Compose *compose = (Compose *)data;
9439 GtkTreeSelection *attach_selection;
9440 gint attach_nr_selected;
9442 if (!event) return FALSE;
9444 if (event->button == 3) {
9445 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9446 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9448 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", (attach_nr_selected > 0));
9449 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", (attach_nr_selected > 0));
9451 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9452 NULL, NULL, event->button, event->time);
9459 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9462 Compose *compose = (Compose *)data;
9464 if (!event) return FALSE;
9466 switch (event->keyval) {
9467 case GDK_KEY_Delete:
9468 compose_attach_remove_selected(NULL, compose);
9474 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9476 toolbar_comp_set_sensitive(compose, allow);
9477 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9478 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9480 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9482 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9483 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9484 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9486 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9490 static void compose_send_cb(GtkAction *action, gpointer data)
9492 Compose *compose = (Compose *)data;
9494 if (prefs_common.work_offline &&
9495 !inc_offline_should_override(TRUE,
9496 _("Claws Mail needs network access in order "
9497 "to send this email.")))
9500 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9501 g_source_remove(compose->draft_timeout_tag);
9502 compose->draft_timeout_tag = -1;
9505 compose_send(compose);
9508 static void compose_send_later_cb(GtkAction *action, gpointer data)
9510 Compose *compose = (Compose *)data;
9514 compose_allow_user_actions(compose, FALSE);
9515 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9516 compose_allow_user_actions(compose, TRUE);
9520 compose_close(compose);
9521 } else if (val == -1) {
9522 alertpanel_error(_("Could not queue message."));
9523 } else if (val == -2) {
9524 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9525 } else if (val == -3) {
9526 if (privacy_peek_error())
9527 alertpanel_error(_("Could not queue message for sending:\n\n"
9528 "Signature failed: %s"), privacy_get_error());
9529 } else if (val == -4) {
9530 alertpanel_error(_("Could not queue message for sending:\n\n"
9531 "Charset conversion failed."));
9532 } else if (val == -5) {
9533 alertpanel_error(_("Could not queue message for sending:\n\n"
9534 "Couldn't get recipient encryption key."));
9535 } else if (val == -6) {
9538 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9541 #define DRAFTED_AT_EXIT "drafted_at_exit"
9542 static void compose_register_draft(MsgInfo *info)
9544 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9545 DRAFTED_AT_EXIT, NULL);
9546 FILE *fp = g_fopen(filepath, "ab");
9549 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9557 gboolean compose_draft (gpointer data, guint action)
9559 Compose *compose = (Compose *)data;
9564 MsgFlags flag = {0, 0};
9565 static gboolean lock = FALSE;
9566 MsgInfo *newmsginfo;
9568 gboolean target_locked = FALSE;
9569 gboolean err = FALSE;
9571 if (lock) return FALSE;
9573 if (compose->sending)
9576 draft = account_get_special_folder(compose->account, F_DRAFT);
9577 cm_return_val_if_fail(draft != NULL, FALSE);
9579 if (!g_mutex_trylock(compose->mutex)) {
9580 /* we don't want to lock the mutex once it's available,
9581 * because as the only other part of compose.c locking
9582 * it is compose_close - which means once unlocked,
9583 * the compose struct will be freed */
9584 debug_print("couldn't lock mutex, probably sending\n");
9590 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9591 G_DIR_SEPARATOR, compose);
9592 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9593 FILE_OP_ERROR(tmp, "fopen");
9597 /* chmod for security */
9598 if (change_file_mode_rw(fp, tmp) < 0) {
9599 FILE_OP_ERROR(tmp, "chmod");
9600 g_warning("can't change file mode\n");
9603 /* Save draft infos */
9604 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9605 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9607 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9608 gchar *savefolderid;
9610 savefolderid = compose_get_save_to(compose);
9611 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9612 g_free(savefolderid);
9614 if (compose->return_receipt) {
9615 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9617 if (compose->privacy_system) {
9618 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9619 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9620 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9623 /* Message-ID of message replying to */
9624 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9627 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9628 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9631 /* Message-ID of message forwarding to */
9632 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9635 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9636 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9640 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9641 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9643 sheaders = compose_get_manual_headers_info(compose);
9644 err |= (fprintf(fp, "X-Claws-Manual-Headers:%s\n", sheaders) < 0);
9647 /* end of headers */
9648 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9655 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9659 if (fclose(fp) == EOF) {
9663 if (compose->targetinfo) {
9664 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9665 flag.perm_flags = target_locked?MSG_LOCKED:0;
9667 flag.tmp_flags = MSG_DRAFT;
9669 folder_item_scan(draft);
9670 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9671 MsgInfo *tmpinfo = NULL;
9672 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9673 if (compose->msgid) {
9674 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9677 msgnum = tmpinfo->msgnum;
9678 procmsg_msginfo_free(tmpinfo);
9679 debug_print("got draft msgnum %d from scanning\n", msgnum);
9681 debug_print("didn't get draft msgnum after scanning\n");
9684 debug_print("got draft msgnum %d from adding\n", msgnum);
9690 if (action != COMPOSE_AUTO_SAVE) {
9691 if (action != COMPOSE_DRAFT_FOR_EXIT)
9692 alertpanel_error(_("Could not save draft."));
9695 gtkut_window_popup(compose->window);
9696 val = alertpanel_full(_("Could not save draft"),
9697 _("Could not save draft.\n"
9698 "Do you want to cancel exit or discard this email?"),
9699 _("_Cancel exit"), _("_Discard email"), NULL,
9700 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9701 if (val == G_ALERTALTERNATE) {
9703 g_mutex_unlock(compose->mutex); /* must be done before closing */
9704 compose_close(compose);
9708 g_mutex_unlock(compose->mutex); /* must be done before closing */
9717 if (compose->mode == COMPOSE_REEDIT) {
9718 compose_remove_reedit_target(compose, TRUE);
9721 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9724 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9726 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9728 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9729 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9730 procmsg_msginfo_set_flags(newmsginfo, 0,
9731 MSG_HAS_ATTACHMENT);
9733 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9734 compose_register_draft(newmsginfo);
9736 procmsg_msginfo_free(newmsginfo);
9739 folder_item_scan(draft);
9741 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9743 g_mutex_unlock(compose->mutex); /* must be done before closing */
9744 compose_close(compose);
9750 path = folder_item_fetch_msg(draft, msgnum);
9752 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9755 if (g_stat(path, &s) < 0) {
9756 FILE_OP_ERROR(path, "stat");
9762 procmsg_msginfo_free(compose->targetinfo);
9763 compose->targetinfo = procmsg_msginfo_new();
9764 compose->targetinfo->msgnum = msgnum;
9765 compose->targetinfo->size = (goffset)s.st_size;
9766 compose->targetinfo->mtime = s.st_mtime;
9767 compose->targetinfo->folder = draft;
9769 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9770 compose->mode = COMPOSE_REEDIT;
9772 if (action == COMPOSE_AUTO_SAVE) {
9773 compose->autosaved_draft = compose->targetinfo;
9775 compose->modified = FALSE;
9776 compose_set_title(compose);
9780 g_mutex_unlock(compose->mutex);
9784 void compose_clear_exit_drafts(void)
9786 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9787 DRAFTED_AT_EXIT, NULL);
9788 if (is_file_exist(filepath))
9789 claws_unlink(filepath);
9794 void compose_reopen_exit_drafts(void)
9796 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9797 DRAFTED_AT_EXIT, NULL);
9798 FILE *fp = g_fopen(filepath, "rb");
9802 while (fgets(buf, sizeof(buf), fp)) {
9803 gchar **parts = g_strsplit(buf, "\t", 2);
9804 const gchar *folder = parts[0];
9805 int msgnum = parts[1] ? atoi(parts[1]):-1;
9807 if (folder && *folder && msgnum > -1) {
9808 FolderItem *item = folder_find_item_from_identifier(folder);
9809 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9811 compose_reedit(info, FALSE);
9818 compose_clear_exit_drafts();
9821 static void compose_save_cb(GtkAction *action, gpointer data)
9823 Compose *compose = (Compose *)data;
9824 compose_draft(compose, COMPOSE_KEEP_EDITING);
9825 compose->rmode = COMPOSE_REEDIT;
9828 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9830 if (compose && file_list) {
9833 for ( tmp = file_list; tmp; tmp = tmp->next) {
9834 gchar *file = (gchar *) tmp->data;
9835 gchar *utf8_filename = conv_filename_to_utf8(file);
9836 compose_attach_append(compose, file, utf8_filename, NULL, NULL);
9837 compose_changed_cb(NULL, compose);
9842 g_free(utf8_filename);
9847 static void compose_attach_cb(GtkAction *action, gpointer data)
9849 Compose *compose = (Compose *)data;
9852 if (compose->redirect_filename != NULL)
9855 /* Set focus_window properly, in case we were called via popup menu,
9856 * which unsets it (via focus_out_event callback on compose window). */
9857 manage_window_focus_in(compose->window, NULL, NULL);
9859 file_list = filesel_select_multiple_files_open(_("Select file"));
9862 compose_attach_from_list(compose, file_list, TRUE);
9863 g_list_free(file_list);
9867 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9869 Compose *compose = (Compose *)data;
9871 gint files_inserted = 0;
9873 file_list = filesel_select_multiple_files_open(_("Select file"));
9878 for ( tmp = file_list; tmp; tmp = tmp->next) {
9879 gchar *file = (gchar *) tmp->data;
9880 gchar *filedup = g_strdup(file);
9881 gchar *shortfile = g_path_get_basename(filedup);
9882 ComposeInsertResult res;
9883 /* insert the file if the file is short or if the user confirmed that
9884 he/she wants to insert the large file */
9885 res = compose_insert_file(compose, file);
9886 if (res == COMPOSE_INSERT_READ_ERROR) {
9887 alertpanel_error(_("File '%s' could not be read."), shortfile);
9888 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9889 alertpanel_error(_("File '%s' contained invalid characters\n"
9890 "for the current encoding, insertion may be incorrect."),
9892 } else if (res == COMPOSE_INSERT_SUCCESS)
9899 g_list_free(file_list);
9903 if (files_inserted > 0 && compose->gtkaspell &&
9904 compose->gtkaspell->check_while_typing)
9905 gtkaspell_highlight_all(compose->gtkaspell);
9909 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9911 Compose *compose = (Compose *)data;
9913 compose_insert_sig(compose, FALSE);
9916 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9920 Compose *compose = (Compose *)data;
9922 gtkut_widget_get_uposition(widget, &x, &y);
9923 if (!compose->batch) {
9924 prefs_common.compose_x = x;
9925 prefs_common.compose_y = y;
9927 if (compose->sending || compose->updating)
9929 compose_close_cb(NULL, compose);
9933 void compose_close_toolbar(Compose *compose)
9935 compose_close_cb(NULL, compose);
9938 static void compose_close_cb(GtkAction *action, gpointer data)
9940 Compose *compose = (Compose *)data;
9944 if (compose->exteditor_tag != -1) {
9945 if (!compose_ext_editor_kill(compose))
9950 if (compose->modified) {
9951 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9952 if (!g_mutex_trylock(compose->mutex)) {
9953 /* we don't want to lock the mutex once it's available,
9954 * because as the only other part of compose.c locking
9955 * it is compose_close - which means once unlocked,
9956 * the compose struct will be freed */
9957 debug_print("couldn't lock mutex, probably sending\n");
9961 val = alertpanel(_("Discard message"),
9962 _("This message has been modified. Discard it?"),
9963 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9965 val = alertpanel(_("Save changes"),
9966 _("This message has been modified. Save the latest changes?"),
9967 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9969 g_mutex_unlock(compose->mutex);
9971 case G_ALERTDEFAULT:
9972 if (prefs_common.autosave && !reedit)
9973 compose_remove_draft(compose);
9975 case G_ALERTALTERNATE:
9976 compose_draft(data, COMPOSE_QUIT_EDITING);
9983 compose_close(compose);
9986 static void compose_print_cb(GtkAction *action, gpointer data)
9988 Compose *compose = (Compose *) data;
9990 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9991 if (compose->targetinfo)
9992 messageview_print(compose->targetinfo, FALSE, -1, -1, 0);
9995 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9997 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9998 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9999 Compose *compose = (Compose *) data;
10002 compose->out_encoding = (CharSet)value;
10005 static void compose_address_cb(GtkAction *action, gpointer data)
10007 Compose *compose = (Compose *)data;
10009 #ifndef USE_NEW_ADDRBOOK
10010 addressbook_open(compose);
10012 GError* error = NULL;
10013 addressbook_connect_signals(compose);
10014 addressbook_dbus_open(TRUE, &error);
10016 g_warning("%s", error->message);
10017 g_error_free(error);
10022 static void about_show_cb(GtkAction *action, gpointer data)
10027 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
10029 Compose *compose = (Compose *)data;
10034 tmpl = g_object_get_data(G_OBJECT(widget), "template");
10035 cm_return_if_fail(tmpl != NULL);
10037 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
10039 val = alertpanel(_("Apply template"), msg,
10040 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
10043 if (val == G_ALERTDEFAULT)
10044 compose_template_apply(compose, tmpl, TRUE);
10045 else if (val == G_ALERTALTERNATE)
10046 compose_template_apply(compose, tmpl, FALSE);
10049 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
10051 Compose *compose = (Compose *)data;
10053 compose_exec_ext_editor(compose);
10056 static void compose_undo_cb(GtkAction *action, gpointer data)
10058 Compose *compose = (Compose *)data;
10059 gboolean prev_autowrap = compose->autowrap;
10061 compose->autowrap = FALSE;
10062 undo_undo(compose->undostruct);
10063 compose->autowrap = prev_autowrap;
10066 static void compose_redo_cb(GtkAction *action, gpointer data)
10068 Compose *compose = (Compose *)data;
10069 gboolean prev_autowrap = compose->autowrap;
10071 compose->autowrap = FALSE;
10072 undo_redo(compose->undostruct);
10073 compose->autowrap = prev_autowrap;
10076 static void entry_cut_clipboard(GtkWidget *entry)
10078 if (GTK_IS_EDITABLE(entry))
10079 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
10080 else if (GTK_IS_TEXT_VIEW(entry))
10081 gtk_text_buffer_cut_clipboard(
10082 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10083 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
10087 static void entry_copy_clipboard(GtkWidget *entry)
10089 if (GTK_IS_EDITABLE(entry))
10090 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
10091 else if (GTK_IS_TEXT_VIEW(entry))
10092 gtk_text_buffer_copy_clipboard(
10093 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10094 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
10097 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
10098 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
10100 if (GTK_IS_TEXT_VIEW(entry)) {
10101 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10102 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
10103 GtkTextIter start_iter, end_iter;
10105 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
10107 if (contents == NULL)
10110 /* we shouldn't delete the selection when middle-click-pasting, or we
10111 * can't mid-click-paste our own selection */
10112 if (clip != GDK_SELECTION_PRIMARY) {
10113 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
10114 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
10117 if (insert_place == NULL) {
10118 /* if insert_place isn't specified, insert at the cursor.
10119 * used for Ctrl-V pasting */
10120 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10121 start = gtk_text_iter_get_offset(&start_iter);
10122 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
10124 /* if insert_place is specified, paste here.
10125 * used for mid-click-pasting */
10126 start = gtk_text_iter_get_offset(insert_place);
10127 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
10128 if (prefs_common.primary_paste_unselects)
10129 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
10133 /* paste unwrapped: mark the paste so it's not wrapped later */
10134 end = start + strlen(contents);
10135 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
10136 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
10137 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
10138 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
10139 /* rewrap paragraph now (after a mid-click-paste) */
10140 mark_start = gtk_text_buffer_get_insert(buffer);
10141 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10142 gtk_text_iter_backward_char(&start_iter);
10143 compose_beautify_paragraph(compose, &start_iter, TRUE);
10145 } else if (GTK_IS_EDITABLE(entry))
10146 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
10148 compose->modified = TRUE;
10151 static void entry_allsel(GtkWidget *entry)
10153 if (GTK_IS_EDITABLE(entry))
10154 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
10155 else if (GTK_IS_TEXT_VIEW(entry)) {
10156 GtkTextIter startiter, enditer;
10157 GtkTextBuffer *textbuf;
10159 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10160 gtk_text_buffer_get_start_iter(textbuf, &startiter);
10161 gtk_text_buffer_get_end_iter(textbuf, &enditer);
10163 gtk_text_buffer_move_mark_by_name(textbuf,
10164 "selection_bound", &startiter);
10165 gtk_text_buffer_move_mark_by_name(textbuf,
10166 "insert", &enditer);
10170 static void compose_cut_cb(GtkAction *action, gpointer data)
10172 Compose *compose = (Compose *)data;
10173 if (compose->focused_editable
10174 #ifndef GENERIC_UMPC
10175 && gtk_widget_has_focus(compose->focused_editable)
10178 entry_cut_clipboard(compose->focused_editable);
10181 static void compose_copy_cb(GtkAction *action, gpointer data)
10183 Compose *compose = (Compose *)data;
10184 if (compose->focused_editable
10185 #ifndef GENERIC_UMPC
10186 && gtk_widget_has_focus(compose->focused_editable)
10189 entry_copy_clipboard(compose->focused_editable);
10192 static void compose_paste_cb(GtkAction *action, gpointer data)
10194 Compose *compose = (Compose *)data;
10195 gint prev_autowrap;
10196 GtkTextBuffer *buffer;
10198 if (compose->focused_editable &&
10199 #ifndef GENERIC_UMPC
10200 gtk_widget_has_focus(compose->focused_editable)
10203 entry_paste_clipboard(compose, compose->focused_editable,
10204 prefs_common.linewrap_pastes,
10205 GDK_SELECTION_CLIPBOARD, NULL);
10210 #ifndef GENERIC_UMPC
10211 gtk_widget_has_focus(compose->text) &&
10213 compose->gtkaspell &&
10214 compose->gtkaspell->check_while_typing)
10215 gtkaspell_highlight_all(compose->gtkaspell);
10219 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10221 Compose *compose = (Compose *)data;
10222 gint wrap_quote = prefs_common.linewrap_quote;
10223 if (compose->focused_editable
10224 #ifndef GENERIC_UMPC
10225 && gtk_widget_has_focus(compose->focused_editable)
10228 /* let text_insert() (called directly or at a later time
10229 * after the gtk_editable_paste_clipboard) know that
10230 * text is to be inserted as a quotation. implemented
10231 * by using a simple refcount... */
10232 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10233 G_OBJECT(compose->focused_editable),
10234 "paste_as_quotation"));
10235 g_object_set_data(G_OBJECT(compose->focused_editable),
10236 "paste_as_quotation",
10237 GINT_TO_POINTER(paste_as_quotation + 1));
10238 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10239 entry_paste_clipboard(compose, compose->focused_editable,
10240 prefs_common.linewrap_pastes,
10241 GDK_SELECTION_CLIPBOARD, NULL);
10242 prefs_common.linewrap_quote = wrap_quote;
10246 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10248 Compose *compose = (Compose *)data;
10249 gint prev_autowrap;
10250 GtkTextBuffer *buffer;
10252 if (compose->focused_editable
10253 #ifndef GENERIC_UMPC
10254 && gtk_widget_has_focus(compose->focused_editable)
10257 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10258 GDK_SELECTION_CLIPBOARD, NULL);
10263 #ifndef GENERIC_UMPC
10264 gtk_widget_has_focus(compose->text) &&
10266 compose->gtkaspell &&
10267 compose->gtkaspell->check_while_typing)
10268 gtkaspell_highlight_all(compose->gtkaspell);
10272 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10274 Compose *compose = (Compose *)data;
10275 gint prev_autowrap;
10276 GtkTextBuffer *buffer;
10278 if (compose->focused_editable
10279 #ifndef GENERIC_UMPC
10280 && gtk_widget_has_focus(compose->focused_editable)
10283 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10284 GDK_SELECTION_CLIPBOARD, NULL);
10289 #ifndef GENERIC_UMPC
10290 gtk_widget_has_focus(compose->text) &&
10292 compose->gtkaspell &&
10293 compose->gtkaspell->check_while_typing)
10294 gtkaspell_highlight_all(compose->gtkaspell);
10298 static void compose_allsel_cb(GtkAction *action, gpointer data)
10300 Compose *compose = (Compose *)data;
10301 if (compose->focused_editable
10302 #ifndef GENERIC_UMPC
10303 && gtk_widget_has_focus(compose->focused_editable)
10306 entry_allsel(compose->focused_editable);
10309 static void textview_move_beginning_of_line (GtkTextView *text)
10311 GtkTextBuffer *buffer;
10315 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10317 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10318 mark = gtk_text_buffer_get_insert(buffer);
10319 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10320 gtk_text_iter_set_line_offset(&ins, 0);
10321 gtk_text_buffer_place_cursor(buffer, &ins);
10324 static void textview_move_forward_character (GtkTextView *text)
10326 GtkTextBuffer *buffer;
10330 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10332 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10333 mark = gtk_text_buffer_get_insert(buffer);
10334 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10335 if (gtk_text_iter_forward_cursor_position(&ins))
10336 gtk_text_buffer_place_cursor(buffer, &ins);
10339 static void textview_move_backward_character (GtkTextView *text)
10341 GtkTextBuffer *buffer;
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);
10350 if (gtk_text_iter_backward_cursor_position(&ins))
10351 gtk_text_buffer_place_cursor(buffer, &ins);
10354 static void textview_move_forward_word (GtkTextView *text)
10356 GtkTextBuffer *buffer;
10361 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10363 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10364 mark = gtk_text_buffer_get_insert(buffer);
10365 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10366 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10367 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10368 gtk_text_iter_backward_word_start(&ins);
10369 gtk_text_buffer_place_cursor(buffer, &ins);
10373 static void textview_move_backward_word (GtkTextView *text)
10375 GtkTextBuffer *buffer;
10380 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10382 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10383 mark = gtk_text_buffer_get_insert(buffer);
10384 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10385 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10386 if (gtk_text_iter_backward_word_starts(&ins, 1))
10387 gtk_text_buffer_place_cursor(buffer, &ins);
10390 static void textview_move_end_of_line (GtkTextView *text)
10392 GtkTextBuffer *buffer;
10396 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10398 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10399 mark = gtk_text_buffer_get_insert(buffer);
10400 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10401 if (gtk_text_iter_forward_to_line_end(&ins))
10402 gtk_text_buffer_place_cursor(buffer, &ins);
10405 static void textview_move_next_line (GtkTextView *text)
10407 GtkTextBuffer *buffer;
10412 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10414 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10415 mark = gtk_text_buffer_get_insert(buffer);
10416 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10417 offset = gtk_text_iter_get_line_offset(&ins);
10418 if (gtk_text_iter_forward_line(&ins)) {
10419 gtk_text_iter_set_line_offset(&ins, offset);
10420 gtk_text_buffer_place_cursor(buffer, &ins);
10424 static void textview_move_previous_line (GtkTextView *text)
10426 GtkTextBuffer *buffer;
10431 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10433 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10434 mark = gtk_text_buffer_get_insert(buffer);
10435 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10436 offset = gtk_text_iter_get_line_offset(&ins);
10437 if (gtk_text_iter_backward_line(&ins)) {
10438 gtk_text_iter_set_line_offset(&ins, offset);
10439 gtk_text_buffer_place_cursor(buffer, &ins);
10443 static void textview_delete_forward_character (GtkTextView *text)
10445 GtkTextBuffer *buffer;
10447 GtkTextIter ins, end_iter;
10449 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10451 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10452 mark = gtk_text_buffer_get_insert(buffer);
10453 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10455 if (gtk_text_iter_forward_char(&end_iter)) {
10456 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10460 static void textview_delete_backward_character (GtkTextView *text)
10462 GtkTextBuffer *buffer;
10464 GtkTextIter ins, end_iter;
10466 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10468 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10469 mark = gtk_text_buffer_get_insert(buffer);
10470 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10472 if (gtk_text_iter_backward_char(&end_iter)) {
10473 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10477 static void textview_delete_forward_word (GtkTextView *text)
10479 GtkTextBuffer *buffer;
10481 GtkTextIter ins, end_iter;
10483 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10485 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10486 mark = gtk_text_buffer_get_insert(buffer);
10487 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10489 if (gtk_text_iter_forward_word_end(&end_iter)) {
10490 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10494 static void textview_delete_backward_word (GtkTextView *text)
10496 GtkTextBuffer *buffer;
10498 GtkTextIter ins, end_iter;
10500 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10502 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10503 mark = gtk_text_buffer_get_insert(buffer);
10504 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10506 if (gtk_text_iter_backward_word_start(&end_iter)) {
10507 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10511 static void textview_delete_line (GtkTextView *text)
10513 GtkTextBuffer *buffer;
10515 GtkTextIter ins, start_iter, end_iter;
10517 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10519 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10520 mark = gtk_text_buffer_get_insert(buffer);
10521 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10524 gtk_text_iter_set_line_offset(&start_iter, 0);
10527 if (gtk_text_iter_ends_line(&end_iter)){
10528 if (!gtk_text_iter_forward_char(&end_iter))
10529 gtk_text_iter_backward_char(&start_iter);
10532 gtk_text_iter_forward_to_line_end(&end_iter);
10533 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10536 static void textview_delete_to_line_end (GtkTextView *text)
10538 GtkTextBuffer *buffer;
10540 GtkTextIter ins, end_iter;
10542 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10544 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10545 mark = gtk_text_buffer_get_insert(buffer);
10546 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10548 if (gtk_text_iter_ends_line(&end_iter))
10549 gtk_text_iter_forward_char(&end_iter);
10551 gtk_text_iter_forward_to_line_end(&end_iter);
10552 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10555 #define DO_ACTION(name, act) { \
10556 if(!strcmp(name, a_name)) { \
10560 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10562 const gchar *a_name = gtk_action_get_name(action);
10563 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10564 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10565 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10566 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10567 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10568 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10569 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10570 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10571 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10572 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10573 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10574 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10575 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10576 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10580 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10582 Compose *compose = (Compose *)data;
10583 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10584 ComposeCallAdvancedAction action = -1;
10586 action = compose_call_advanced_action_from_path(gaction);
10589 void (*do_action) (GtkTextView *text);
10590 } action_table[] = {
10591 {textview_move_beginning_of_line},
10592 {textview_move_forward_character},
10593 {textview_move_backward_character},
10594 {textview_move_forward_word},
10595 {textview_move_backward_word},
10596 {textview_move_end_of_line},
10597 {textview_move_next_line},
10598 {textview_move_previous_line},
10599 {textview_delete_forward_character},
10600 {textview_delete_backward_character},
10601 {textview_delete_forward_word},
10602 {textview_delete_backward_word},
10603 {textview_delete_line},
10604 {textview_delete_to_line_end}
10607 if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
10609 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10610 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10611 if (action_table[action].do_action)
10612 action_table[action].do_action(text);
10614 g_warning("Not implemented yet.");
10618 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10620 GtkAllocation allocation;
10624 if (GTK_IS_EDITABLE(widget)) {
10625 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10626 gtk_editable_set_position(GTK_EDITABLE(widget),
10629 if ((parent = gtk_widget_get_parent(widget))
10630 && (parent = gtk_widget_get_parent(parent))
10631 && (parent = gtk_widget_get_parent(parent))) {
10632 if (GTK_IS_SCROLLED_WINDOW(parent)) {
10633 gtk_widget_get_allocation(widget, &allocation);
10634 gint y = allocation.y;
10635 gint height = allocation.height;
10636 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10637 (GTK_SCROLLED_WINDOW(parent));
10639 gfloat value = gtk_adjustment_get_value(shown);
10640 gfloat upper = gtk_adjustment_get_upper(shown);
10641 gfloat page_size = gtk_adjustment_get_page_size(shown);
10642 if (y < (int)value) {
10643 gtk_adjustment_set_value(shown, y - 1);
10645 if ((y + height) > ((int)value + (int)page_size)) {
10646 if ((y - height - 1) < ((int)upper - (int)page_size)) {
10647 gtk_adjustment_set_value(shown,
10648 y + height - (int)page_size - 1);
10650 gtk_adjustment_set_value(shown,
10651 (int)upper - (int)page_size - 1);
10658 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10659 compose->focused_editable = widget;
10661 #ifdef GENERIC_UMPC
10662 if (GTK_IS_TEXT_VIEW(widget)
10663 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10664 g_object_ref(compose->notebook);
10665 g_object_ref(compose->edit_vbox);
10666 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10667 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10668 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10669 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10670 g_object_unref(compose->notebook);
10671 g_object_unref(compose->edit_vbox);
10672 g_signal_handlers_block_by_func(G_OBJECT(widget),
10673 G_CALLBACK(compose_grab_focus_cb),
10675 gtk_widget_grab_focus(widget);
10676 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10677 G_CALLBACK(compose_grab_focus_cb),
10679 } else if (!GTK_IS_TEXT_VIEW(widget)
10680 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10681 g_object_ref(compose->notebook);
10682 g_object_ref(compose->edit_vbox);
10683 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10684 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10685 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10686 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10687 g_object_unref(compose->notebook);
10688 g_object_unref(compose->edit_vbox);
10689 g_signal_handlers_block_by_func(G_OBJECT(widget),
10690 G_CALLBACK(compose_grab_focus_cb),
10692 gtk_widget_grab_focus(widget);
10693 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10694 G_CALLBACK(compose_grab_focus_cb),
10700 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10702 compose->modified = TRUE;
10703 // compose_beautify_paragraph(compose, NULL, TRUE);
10704 #ifndef GENERIC_UMPC
10705 compose_set_title(compose);
10709 static void compose_wrap_cb(GtkAction *action, gpointer data)
10711 Compose *compose = (Compose *)data;
10712 compose_beautify_paragraph(compose, NULL, TRUE);
10715 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10717 Compose *compose = (Compose *)data;
10718 compose_wrap_all_full(compose, TRUE);
10721 static void compose_find_cb(GtkAction *action, gpointer data)
10723 Compose *compose = (Compose *)data;
10725 message_search_compose(compose);
10728 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10731 Compose *compose = (Compose *)data;
10732 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10733 if (compose->autowrap)
10734 compose_wrap_all_full(compose, TRUE);
10735 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10738 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10741 Compose *compose = (Compose *)data;
10742 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10745 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10747 Compose *compose = (Compose *)data;
10749 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10752 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10754 Compose *compose = (Compose *)data;
10756 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10759 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10761 g_free(compose->privacy_system);
10763 compose->privacy_system = g_strdup(account->default_privacy_system);
10764 compose_update_privacy_system_menu_item(compose, warn);
10767 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10769 Compose *compose = (Compose *)data;
10771 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10772 gtk_widget_show(compose->ruler_hbox);
10773 prefs_common.show_ruler = TRUE;
10775 gtk_widget_hide(compose->ruler_hbox);
10776 gtk_widget_queue_resize(compose->edit_vbox);
10777 prefs_common.show_ruler = FALSE;
10781 static void compose_attach_drag_received_cb (GtkWidget *widget,
10782 GdkDragContext *context,
10785 GtkSelectionData *data,
10788 gpointer user_data)
10790 Compose *compose = (Compose *)user_data;
10794 type = gtk_selection_data_get_data_type(data);
10795 if (((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
10797 || (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND"))
10799 ) && gtk_drag_get_source_widget(context) !=
10800 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10801 list = uri_list_extract_filenames(
10802 (const gchar *)gtk_selection_data_get_data(data));
10803 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10804 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10805 compose_attach_append
10806 (compose, (const gchar *)tmp->data,
10807 utf8_filename, NULL, NULL);
10808 g_free(utf8_filename);
10810 if (list) compose_changed_cb(NULL, compose);
10811 list_free_strings(list);
10813 } else if (gtk_drag_get_source_widget(context)
10814 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10815 /* comes from our summaryview */
10816 SummaryView * summaryview = NULL;
10817 GSList * list = NULL, *cur = NULL;
10819 if (mainwindow_get_mainwindow())
10820 summaryview = mainwindow_get_mainwindow()->summaryview;
10823 list = summary_get_selected_msg_list(summaryview);
10825 for (cur = list; cur; cur = cur->next) {
10826 MsgInfo *msginfo = (MsgInfo *)cur->data;
10827 gchar *file = NULL;
10829 file = procmsg_get_message_file_full(msginfo,
10832 compose_attach_append(compose, (const gchar *)file,
10833 (const gchar *)file, "message/rfc822", NULL);
10837 g_slist_free(list);
10841 static gboolean compose_drag_drop(GtkWidget *widget,
10842 GdkDragContext *drag_context,
10844 guint time, gpointer user_data)
10846 /* not handling this signal makes compose_insert_drag_received_cb
10851 static gboolean completion_set_focus_to_subject
10852 (GtkWidget *widget,
10853 GdkEventKey *event,
10856 cm_return_val_if_fail(compose != NULL, FALSE);
10858 /* make backtab move to subject field */
10859 if(event->keyval == GDK_KEY_ISO_Left_Tab) {
10860 gtk_widget_grab_focus(compose->subject_entry);
10866 static void compose_insert_drag_received_cb (GtkWidget *widget,
10867 GdkDragContext *drag_context,
10870 GtkSelectionData *data,
10873 gpointer user_data)
10875 Compose *compose = (Compose *)user_data;
10879 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10881 type = gtk_selection_data_get_data_type(data);
10883 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
10885 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND")) {
10887 AlertValue val = G_ALERTDEFAULT;
10888 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
10890 list = uri_list_extract_filenames(ddata);
10891 if (list == NULL && strstr(ddata, "://")) {
10892 /* Assume a list of no files, and data has ://, is a remote link */
10893 gchar *tmpdata = g_strstrip(g_strdup(ddata));
10894 gchar *tmpfile = get_tmp_file();
10895 str_write_to_file(tmpdata, tmpfile);
10897 compose_insert_file(compose, tmpfile);
10898 claws_unlink(tmpfile);
10900 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10901 compose_beautify_paragraph(compose, NULL, TRUE);
10904 switch (prefs_common.compose_dnd_mode) {
10905 case COMPOSE_DND_ASK:
10906 val = alertpanel_full(_("Insert or attach?"),
10907 _("Do you want to insert the contents of the file(s) "
10908 "into the message body, or attach it to the email?"),
10909 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10910 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10912 case COMPOSE_DND_INSERT:
10913 val = G_ALERTALTERNATE;
10915 case COMPOSE_DND_ATTACH:
10916 val = G_ALERTOTHER;
10919 /* unexpected case */
10920 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10923 if (val & G_ALERTDISABLE) {
10924 val &= ~G_ALERTDISABLE;
10925 /* remember what action to perform by default, only if we don't click Cancel */
10926 if (val == G_ALERTALTERNATE)
10927 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10928 else if (val == G_ALERTOTHER)
10929 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10932 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10933 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10934 list_free_strings(list);
10937 } else if (val == G_ALERTOTHER) {
10938 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10939 list_free_strings(list);
10944 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10945 compose_insert_file(compose, (const gchar *)tmp->data);
10947 list_free_strings(list);
10949 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10954 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10957 static void compose_header_drag_received_cb (GtkWidget *widget,
10958 GdkDragContext *drag_context,
10961 GtkSelectionData *data,
10964 gpointer user_data)
10966 GtkEditable *entry = (GtkEditable *)user_data;
10967 const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
10969 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10972 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10973 gchar *decoded=g_new(gchar, strlen(email));
10976 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
10977 gtk_editable_delete_text(entry, 0, -1);
10978 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10979 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10983 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10986 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10988 Compose *compose = (Compose *)data;
10990 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10991 compose->return_receipt = TRUE;
10993 compose->return_receipt = FALSE;
10996 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10998 Compose *compose = (Compose *)data;
11000 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11001 compose->remove_references = TRUE;
11003 compose->remove_references = FALSE;
11006 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
11007 ComposeHeaderEntry *headerentry)
11009 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
11013 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
11014 GdkEventKey *event,
11015 ComposeHeaderEntry *headerentry)
11017 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
11018 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
11019 !(event->state & GDK_MODIFIER_MASK) &&
11020 (event->keyval == GDK_KEY_BackSpace) &&
11021 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
11022 gtk_container_remove
11023 (GTK_CONTAINER(headerentry->compose->header_table),
11024 headerentry->combo);
11025 gtk_container_remove
11026 (GTK_CONTAINER(headerentry->compose->header_table),
11027 headerentry->entry);
11028 headerentry->compose->header_list =
11029 g_slist_remove(headerentry->compose->header_list,
11031 g_free(headerentry);
11032 } else if (event->keyval == GDK_KEY_Tab) {
11033 if (headerentry->compose->header_last == headerentry) {
11034 /* Override default next focus, and give it to subject_entry
11035 * instead of notebook tabs
11037 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
11038 gtk_widget_grab_focus(headerentry->compose->subject_entry);
11045 static gboolean scroll_postpone(gpointer data)
11047 Compose *compose = (Compose *)data;
11049 cm_return_val_if_fail(!compose->batch, FALSE);
11051 GTK_EVENTS_FLUSH();
11052 compose_show_first_last_header(compose, FALSE);
11056 static void compose_headerentry_changed_cb(GtkWidget *entry,
11057 ComposeHeaderEntry *headerentry)
11059 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
11060 compose_create_header_entry(headerentry->compose);
11061 g_signal_handlers_disconnect_matched
11062 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
11063 0, 0, NULL, NULL, headerentry);
11065 if (!headerentry->compose->batch)
11066 g_timeout_add(0, scroll_postpone, headerentry->compose);
11070 static gboolean compose_defer_auto_save_draft(Compose *compose)
11072 compose->draft_timeout_tag = -1;
11073 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
11077 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
11079 GtkAdjustment *vadj;
11081 cm_return_if_fail(compose);
11082 cm_return_if_fail(!compose->batch);
11083 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
11084 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
11085 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11086 gtk_widget_get_parent(compose->header_table)));
11087 gtk_adjustment_set_value(vadj, (show_first ?
11088 gtk_adjustment_get_lower(vadj) :
11089 (gtk_adjustment_get_upper(vadj) -
11090 gtk_adjustment_get_page_size(vadj))));
11091 gtk_adjustment_changed(vadj);
11094 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
11095 const gchar *text, gint len, Compose *compose)
11097 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
11098 (G_OBJECT(compose->text), "paste_as_quotation"));
11101 cm_return_if_fail(text != NULL);
11103 g_signal_handlers_block_by_func(G_OBJECT(buffer),
11104 G_CALLBACK(text_inserted),
11106 if (paste_as_quotation) {
11108 const gchar *qmark;
11110 GtkTextIter start_iter;
11113 len = strlen(text);
11115 new_text = g_strndup(text, len);
11117 qmark = compose_quote_char_from_context(compose);
11119 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11120 gtk_text_buffer_place_cursor(buffer, iter);
11122 pos = gtk_text_iter_get_offset(iter);
11124 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
11125 _("Quote format error at line %d."));
11126 quote_fmt_reset_vartable();
11128 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
11129 GINT_TO_POINTER(paste_as_quotation - 1));
11131 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11132 gtk_text_buffer_place_cursor(buffer, iter);
11133 gtk_text_buffer_delete_mark(buffer, mark);
11135 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
11136 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
11137 compose_beautify_paragraph(compose, &start_iter, FALSE);
11138 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
11139 gtk_text_buffer_delete_mark(buffer, mark);
11141 if (strcmp(text, "\n") || compose->automatic_break
11142 || gtk_text_iter_starts_line(iter)) {
11143 GtkTextIter before_ins;
11144 gtk_text_buffer_insert(buffer, iter, text, len);
11145 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
11146 before_ins = *iter;
11147 gtk_text_iter_backward_chars(&before_ins, len);
11148 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
11151 /* check if the preceding is just whitespace or quote */
11152 GtkTextIter start_line;
11153 gchar *tmp = NULL, *quote = NULL;
11154 gint quote_len = 0, is_normal = 0;
11155 start_line = *iter;
11156 gtk_text_iter_set_line_offset(&start_line, 0);
11157 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
11160 if (*tmp == '\0') {
11163 quote = compose_get_quote_str(buffer, &start_line, "e_len);
11171 gtk_text_buffer_insert(buffer, iter, text, len);
11173 gtk_text_buffer_insert_with_tags_by_name(buffer,
11174 iter, text, len, "no_join", NULL);
11179 if (!paste_as_quotation) {
11180 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11181 compose_beautify_paragraph(compose, iter, FALSE);
11182 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11183 gtk_text_buffer_delete_mark(buffer, mark);
11186 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
11187 G_CALLBACK(text_inserted),
11189 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
11191 if (prefs_common.autosave &&
11192 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
11193 compose->draft_timeout_tag != -2 /* disabled while loading */)
11194 compose->draft_timeout_tag = g_timeout_add
11195 (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
11199 static void compose_check_all(GtkAction *action, gpointer data)
11201 Compose *compose = (Compose *)data;
11202 if (!compose->gtkaspell)
11205 if (gtk_widget_has_focus(compose->subject_entry))
11206 claws_spell_entry_check_all(
11207 CLAWS_SPELL_ENTRY(compose->subject_entry));
11209 gtkaspell_check_all(compose->gtkaspell);
11212 static void compose_highlight_all(GtkAction *action, gpointer data)
11214 Compose *compose = (Compose *)data;
11215 if (compose->gtkaspell) {
11216 claws_spell_entry_recheck_all(
11217 CLAWS_SPELL_ENTRY(compose->subject_entry));
11218 gtkaspell_highlight_all(compose->gtkaspell);
11222 static void compose_check_backwards(GtkAction *action, gpointer data)
11224 Compose *compose = (Compose *)data;
11225 if (!compose->gtkaspell) {
11226 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11230 if (gtk_widget_has_focus(compose->subject_entry))
11231 claws_spell_entry_check_backwards(
11232 CLAWS_SPELL_ENTRY(compose->subject_entry));
11234 gtkaspell_check_backwards(compose->gtkaspell);
11237 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11239 Compose *compose = (Compose *)data;
11240 if (!compose->gtkaspell) {
11241 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11245 if (gtk_widget_has_focus(compose->subject_entry))
11246 claws_spell_entry_check_forwards_go(
11247 CLAWS_SPELL_ENTRY(compose->subject_entry));
11249 gtkaspell_check_forwards_go(compose->gtkaspell);
11254 *\brief Guess originating forward account from MsgInfo and several
11255 * "common preference" settings. Return NULL if no guess.
11257 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11259 PrefsAccount *account = NULL;
11261 cm_return_val_if_fail(msginfo, NULL);
11262 cm_return_val_if_fail(msginfo->folder, NULL);
11263 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11265 if (msginfo->folder->prefs->enable_default_account)
11266 account = account_find_from_id(msginfo->folder->prefs->default_account);
11269 account = msginfo->folder->folder->account;
11271 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11273 Xstrdup_a(to, msginfo->to, return NULL);
11274 extract_address(to);
11275 account = account_find_from_address(to, FALSE);
11278 if (!account && prefs_common.forward_account_autosel) {
11279 gchar cc[BUFFSIZE];
11280 if (!procheader_get_header_from_msginfo
11281 (msginfo, cc,sizeof cc , "Cc:")) {
11282 gchar *buf = cc + strlen("Cc:");
11283 extract_address(buf);
11284 account = account_find_from_address(buf, FALSE);
11288 if (!account && prefs_common.forward_account_autosel) {
11289 gchar deliveredto[BUFFSIZE];
11290 if (!procheader_get_header_from_msginfo
11291 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
11292 gchar *buf = deliveredto + strlen("Delivered-To:");
11293 extract_address(buf);
11294 account = account_find_from_address(buf, FALSE);
11301 gboolean compose_close(Compose *compose)
11305 if (!g_mutex_trylock(compose->mutex)) {
11306 /* we have to wait for the (possibly deferred by auto-save)
11307 * drafting to be done, before destroying the compose under
11309 debug_print("waiting for drafting to finish...\n");
11310 compose_allow_user_actions(compose, FALSE);
11311 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11314 cm_return_val_if_fail(compose, FALSE);
11315 gtkut_widget_get_uposition(compose->window, &x, &y);
11316 if (!compose->batch) {
11317 prefs_common.compose_x = x;
11318 prefs_common.compose_y = y;
11320 g_mutex_unlock(compose->mutex);
11321 compose_destroy(compose);
11326 * Add entry field for each address in list.
11327 * \param compose E-Mail composition object.
11328 * \param listAddress List of (formatted) E-Mail addresses.
11330 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11333 node = listAddress;
11335 addr = ( gchar * ) node->data;
11336 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11337 node = g_list_next( node );
11341 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11342 guint action, gboolean opening_multiple)
11344 gchar *body = NULL;
11345 GSList *new_msglist = NULL;
11346 MsgInfo *tmp_msginfo = NULL;
11347 gboolean originally_enc = FALSE;
11348 gboolean originally_sig = FALSE;
11349 Compose *compose = NULL;
11350 gchar *s_system = NULL;
11352 cm_return_if_fail(msgview != NULL);
11354 cm_return_if_fail(msginfo_list != NULL);
11356 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11357 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11358 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11360 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11361 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11362 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11363 orig_msginfo, mimeinfo);
11364 if (tmp_msginfo != NULL) {
11365 new_msglist = g_slist_append(NULL, tmp_msginfo);
11367 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11368 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11369 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11371 tmp_msginfo->folder = orig_msginfo->folder;
11372 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11373 if (orig_msginfo->tags) {
11374 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11375 tmp_msginfo->folder->tags_dirty = TRUE;
11381 if (!opening_multiple)
11382 body = messageview_get_selection(msgview);
11385 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11386 procmsg_msginfo_free(tmp_msginfo);
11387 g_slist_free(new_msglist);
11389 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11391 if (compose && originally_enc) {
11392 compose_force_encryption(compose, compose->account, FALSE, s_system);
11395 if (compose && originally_sig && compose->account->default_sign_reply) {
11396 compose_force_signing(compose, compose->account, s_system);
11400 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11403 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11406 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11407 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11408 GSList *cur = msginfo_list;
11409 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11410 "messages. Opening the windows "
11411 "could take some time. Do you "
11412 "want to continue?"),
11413 g_slist_length(msginfo_list));
11414 if (g_slist_length(msginfo_list) > 9
11415 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11416 != G_ALERTALTERNATE) {
11421 /* We'll open multiple compose windows */
11422 /* let the WM place the next windows */
11423 compose_force_window_origin = FALSE;
11424 for (; cur; cur = cur->next) {
11426 tmplist.data = cur->data;
11427 tmplist.next = NULL;
11428 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11430 compose_force_window_origin = TRUE;
11432 /* forwarding multiple mails as attachments is done via a
11433 * single compose window */
11434 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11438 void compose_check_for_email_account(Compose *compose)
11440 PrefsAccount *ac = NULL, *curr = NULL;
11446 if (compose->account && compose->account->protocol == A_NNTP) {
11447 ac = account_get_cur_account();
11448 if (ac->protocol == A_NNTP) {
11449 list = account_get_list();
11451 for( ; list != NULL ; list = g_list_next(list)) {
11452 curr = (PrefsAccount *) list->data;
11453 if (curr->protocol != A_NNTP) {
11459 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11464 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo,
11465 const gchar *address)
11467 GSList *msginfo_list = NULL;
11468 gchar *body = messageview_get_selection(msgview);
11471 msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11473 compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11474 compose_check_for_email_account(compose);
11475 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11476 compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11477 compose_reply_set_subject(compose, msginfo);
11480 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11483 void compose_set_position(Compose *compose, gint pos)
11485 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11487 gtkut_text_view_set_position(text, pos);
11490 gboolean compose_search_string(Compose *compose,
11491 const gchar *str, gboolean case_sens)
11493 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11495 return gtkut_text_view_search_string(text, str, case_sens);
11498 gboolean compose_search_string_backward(Compose *compose,
11499 const gchar *str, gboolean case_sens)
11501 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11503 return gtkut_text_view_search_string_backward(text, str, case_sens);
11506 /* allocate a msginfo structure and populate its data from a compose data structure */
11507 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11509 MsgInfo *newmsginfo;
11511 gchar buf[BUFFSIZE];
11513 cm_return_val_if_fail( compose != NULL, NULL );
11515 newmsginfo = procmsg_msginfo_new();
11518 get_rfc822_date(buf, sizeof(buf));
11519 newmsginfo->date = g_strdup(buf);
11522 if (compose->from_name) {
11523 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11524 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11528 if (compose->subject_entry)
11529 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11531 /* to, cc, reply-to, newsgroups */
11532 for (list = compose->header_list; list; list = list->next) {
11533 gchar *header = gtk_editable_get_chars(
11535 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11536 gchar *entry = gtk_editable_get_chars(
11537 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11539 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11540 if ( newmsginfo->to == NULL ) {
11541 newmsginfo->to = g_strdup(entry);
11542 } else if (entry && *entry) {
11543 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11544 g_free(newmsginfo->to);
11545 newmsginfo->to = tmp;
11548 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11549 if ( newmsginfo->cc == NULL ) {
11550 newmsginfo->cc = g_strdup(entry);
11551 } else if (entry && *entry) {
11552 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11553 g_free(newmsginfo->cc);
11554 newmsginfo->cc = tmp;
11557 if ( strcasecmp(header,
11558 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11559 if ( newmsginfo->newsgroups == NULL ) {
11560 newmsginfo->newsgroups = g_strdup(entry);
11561 } else if (entry && *entry) {
11562 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11563 g_free(newmsginfo->newsgroups);
11564 newmsginfo->newsgroups = tmp;
11572 /* other data is unset */
11578 /* update compose's dictionaries from folder dict settings */
11579 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11580 FolderItem *folder_item)
11582 cm_return_if_fail(compose != NULL);
11584 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11585 FolderItemPrefs *prefs = folder_item->prefs;
11587 if (prefs->enable_default_dictionary)
11588 gtkaspell_change_dict(compose->gtkaspell,
11589 prefs->default_dictionary, FALSE);
11590 if (folder_item->prefs->enable_default_alt_dictionary)
11591 gtkaspell_change_alt_dict(compose->gtkaspell,
11592 prefs->default_alt_dictionary);
11593 if (prefs->enable_default_dictionary
11594 || prefs->enable_default_alt_dictionary)
11595 compose_spell_menu_changed(compose);