2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #include "addressbook.h"
64 #include "folderview.h"
67 #include "stock_pixmap.h"
68 #include "send_message.h"
71 #include "customheader.h"
72 #include "prefs_common.h"
73 #include "prefs_account.h"
77 #include "procheader.h"
79 #include "statusbar.h"
82 #include "quoted-printable.h"
87 #include "alertpanel.h"
88 #include "manage_window.h"
89 #include "gtkshruler.h"
91 #include "addr_compl.h"
92 #include "quote_fmt.h"
94 #include "foldersel.h"
97 #include "message_search.h"
102 #include "autofaces.h"
114 #define N_ATTACH_COLS (N_COL_COLUMNS)
118 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
119 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
127 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
132 } ComposeCallAdvancedAction;
136 PRIORITY_HIGHEST = 1,
145 COMPOSE_INSERT_SUCCESS,
146 COMPOSE_INSERT_READ_ERROR,
147 COMPOSE_INSERT_INVALID_CHARACTER,
148 COMPOSE_INSERT_NO_FILE
149 } ComposeInsertResult;
153 COMPOSE_WRITE_FOR_SEND,
154 COMPOSE_WRITE_FOR_STORE
159 COMPOSE_QUOTE_FORCED,
164 #define B64_LINE_SIZE 57
165 #define B64_BUFFSIZE 77
167 #define MAX_REFERENCES_LEN 999
169 static GList *compose_list = NULL;
171 static Compose *compose_generic_new (PrefsAccount *account,
174 GPtrArray *attach_files,
175 GList *listAddress );
177 static Compose *compose_create (PrefsAccount *account,
182 static void compose_entry_mark_default_to (Compose *compose,
183 const gchar *address);
184 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
185 ComposeQuoteMode quote_mode,
189 static Compose *compose_forward_multiple (PrefsAccount *account,
190 GSList *msginfo_list);
191 static Compose *compose_reply (MsgInfo *msginfo,
192 ComposeQuoteMode quote_mode,
197 static Compose *compose_reply_mode (ComposeMode mode,
198 GSList *msginfo_list,
200 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
201 static void compose_update_privacy_systems_menu(Compose *compose);
203 static GtkWidget *compose_account_option_menu_create
205 static void compose_set_out_encoding (Compose *compose);
206 static void compose_set_template_menu (Compose *compose);
207 static void compose_template_apply (Compose *compose,
210 static void compose_destroy (Compose *compose);
212 static void compose_entries_set (Compose *compose,
214 ComposeEntryType to_type);
215 static gint compose_parse_header (Compose *compose,
217 static gchar *compose_parse_references (const gchar *ref,
220 static gchar *compose_quote_fmt (Compose *compose,
226 gboolean need_unescape,
227 const gchar *err_msg);
229 static void compose_reply_set_entry (Compose *compose,
235 followup_and_reply_to);
236 static void compose_reedit_set_entry (Compose *compose,
239 static void compose_insert_sig (Compose *compose,
241 static gchar *compose_get_signature_str (Compose *compose);
242 static ComposeInsertResult compose_insert_file (Compose *compose,
245 static gboolean compose_attach_append (Compose *compose,
248 const gchar *content_type);
249 static void compose_attach_parts (Compose *compose,
252 static gboolean compose_beautify_paragraph (Compose *compose,
253 GtkTextIter *par_iter,
255 static void compose_wrap_all (Compose *compose);
256 static void compose_wrap_all_full (Compose *compose,
259 static void compose_set_title (Compose *compose);
260 static void compose_select_account (Compose *compose,
261 PrefsAccount *account,
264 static PrefsAccount *compose_current_mail_account(void);
265 /* static gint compose_send (Compose *compose); */
266 static gboolean compose_check_for_valid_recipient
268 static gboolean compose_check_entries (Compose *compose,
269 gboolean check_everything);
270 static gint compose_write_to_file (Compose *compose,
273 gboolean attach_parts);
274 static gint compose_write_body_to_file (Compose *compose,
276 static gint compose_remove_reedit_target (Compose *compose,
278 static void compose_remove_draft (Compose *compose);
279 static gint compose_queue_sub (Compose *compose,
283 gboolean check_subject,
284 gboolean remove_reedit_target);
285 static void compose_add_attachments (Compose *compose,
287 static gchar *compose_get_header (Compose *compose);
289 static void compose_convert_header (Compose *compose,
294 gboolean addr_field);
296 static void compose_attach_info_free (AttachInfo *ainfo);
297 static void compose_attach_remove_selected (GtkAction *action,
300 static void compose_attach_property (GtkAction *action,
302 static void compose_attach_property_create (gboolean *cancelled);
303 static void attach_property_ok (GtkWidget *widget,
304 gboolean *cancelled);
305 static void attach_property_cancel (GtkWidget *widget,
306 gboolean *cancelled);
307 static gint attach_property_delete_event (GtkWidget *widget,
309 gboolean *cancelled);
310 static gboolean attach_property_key_pressed (GtkWidget *widget,
312 gboolean *cancelled);
314 static void compose_exec_ext_editor (Compose *compose);
316 static gint compose_exec_ext_editor_real (const gchar *file);
317 static gboolean compose_ext_editor_kill (Compose *compose);
318 static gboolean compose_input_cb (GIOChannel *source,
319 GIOCondition condition,
321 static void compose_set_ext_editor_sensitive (Compose *compose,
323 #endif /* G_OS_UNIX */
325 static void compose_undo_state_changed (UndoMain *undostruct,
330 static void compose_create_header_entry (Compose *compose);
331 static void compose_add_header_entry (Compose *compose, const gchar *header, gchar *text);
332 static void compose_remove_header_entries(Compose *compose);
334 static void compose_update_priority_menu_item(Compose * compose);
336 static void compose_spell_menu_changed (void *data);
338 static void compose_add_field_list ( Compose *compose,
339 GList *listAddress );
341 /* callback functions */
343 static gboolean compose_edit_size_alloc (GtkEditable *widget,
344 GtkAllocation *allocation,
345 GtkSHRuler *shruler);
346 static void account_activated (GtkComboBox *optmenu,
348 static void attach_selected (GtkTreeView *tree_view,
349 GtkTreePath *tree_path,
350 GtkTreeViewColumn *column,
352 static gboolean attach_button_pressed (GtkWidget *widget,
353 GdkEventButton *event,
355 static gboolean attach_key_pressed (GtkWidget *widget,
358 static void compose_send_cb (GtkAction *action, gpointer data);
359 static void compose_send_later_cb (GtkAction *action, gpointer data);
361 static void compose_save_cb (GtkAction *action,
364 static void compose_attach_cb (GtkAction *action,
366 static void compose_insert_file_cb (GtkAction *action,
368 static void compose_insert_sig_cb (GtkAction *action,
371 static void compose_close_cb (GtkAction *action,
374 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
376 static void compose_address_cb (GtkAction *action,
378 static void about_show_cb (GtkAction *action,
380 static void compose_template_activate_cb(GtkWidget *widget,
383 static void compose_ext_editor_cb (GtkAction *action,
386 static gint compose_delete_cb (GtkWidget *widget,
390 static void compose_undo_cb (GtkAction *action,
392 static void compose_redo_cb (GtkAction *action,
394 static void compose_cut_cb (GtkAction *action,
396 static void compose_copy_cb (GtkAction *action,
398 static void compose_paste_cb (GtkAction *action,
400 static void compose_paste_as_quote_cb (GtkAction *action,
402 static void compose_paste_no_wrap_cb (GtkAction *action,
404 static void compose_paste_wrap_cb (GtkAction *action,
406 static void compose_allsel_cb (GtkAction *action,
409 static void compose_advanced_action_cb (GtkAction *action,
412 static void compose_grab_focus_cb (GtkWidget *widget,
415 static void compose_changed_cb (GtkTextBuffer *textbuf,
418 static void compose_wrap_cb (GtkAction *action,
420 static void compose_wrap_all_cb (GtkAction *action,
422 static void compose_find_cb (GtkAction *action,
424 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
427 static void compose_toggle_ruler_cb (GtkToggleAction *action,
429 static void compose_toggle_sign_cb (GtkToggleAction *action,
431 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
433 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
434 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
435 static void activate_privacy_system (Compose *compose,
436 PrefsAccount *account,
438 static void compose_use_signing(Compose *compose, gboolean use_signing);
439 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
440 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
442 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
444 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
445 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
446 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
448 static void compose_attach_drag_received_cb (GtkWidget *widget,
449 GdkDragContext *drag_context,
452 GtkSelectionData *data,
456 static void compose_insert_drag_received_cb (GtkWidget *widget,
457 GdkDragContext *drag_context,
460 GtkSelectionData *data,
464 static void compose_header_drag_received_cb (GtkWidget *widget,
465 GdkDragContext *drag_context,
468 GtkSelectionData *data,
473 static gboolean compose_drag_drop (GtkWidget *widget,
474 GdkDragContext *drag_context,
476 guint time, gpointer user_data);
478 static void text_inserted (GtkTextBuffer *buffer,
483 static Compose *compose_generic_reply(MsgInfo *msginfo,
484 ComposeQuoteMode quote_mode,
488 gboolean followup_and_reply_to,
491 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
492 ComposeHeaderEntry *headerentry);
493 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
495 ComposeHeaderEntry *headerentry);
497 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
499 static void compose_allow_user_actions (Compose *compose, gboolean allow);
501 static void compose_nothing_cb (GtkAction *action, gpointer data)
507 static void compose_check_all (GtkAction *action, gpointer data);
508 static void compose_highlight_all (GtkAction *action, gpointer data);
509 static void compose_check_backwards (GtkAction *action, gpointer data);
510 static void compose_check_forwards_go (GtkAction *action, gpointer data);
513 static gint compose_defer_auto_save_draft (Compose *compose);
514 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
516 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
519 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
520 FolderItem *folder_item);
522 static void compose_attach_update_label(Compose *compose);
524 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data);
526 static GtkActionEntry compose_popup_entries[] =
528 {"Compose", NULL, "Compose" },
529 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
530 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
531 {"Compose/---", NULL, "---", NULL, NULL, NULL },
532 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
535 static GtkActionEntry compose_entries[] =
537 {"Menu", NULL, "Menu" },
539 {"Message", NULL, N_("_Message") },
540 {"Edit", NULL, N_("_Edit") },
542 {"Spelling", NULL, N_("_Spelling") },
544 {"Options", NULL, N_("_Options") },
545 {"Tools", NULL, N_("_Tools") },
546 {"Help", NULL, N_("_Help") },
548 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
549 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
550 {"Message/---", NULL, "---" },
552 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
553 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
554 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
555 /* {"Message/---", NULL, "---" }, */
556 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
557 /* {"Message/---", NULL, "---" }, */
558 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
561 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
562 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
563 {"Edit/---", NULL, "---" },
565 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
566 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
567 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
569 {"Edit/SpecialPaste", NULL, N_("Special paste") },
570 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
571 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
572 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
574 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
576 {"Edit/Advanced", NULL, N_("A_dvanced") },
577 {"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*/
578 {"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*/
579 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
580 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
581 {"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*/
582 {"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*/
583 {"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*/
584 {"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*/
585 {"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*/
586 {"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*/
587 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
588 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
589 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
590 {"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*/
592 /* {"Edit/---", NULL, "---" }, */
593 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
595 /* {"Edit/---", NULL, "---" }, */
596 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
597 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
598 /* {"Edit/---", NULL, "---" }, */
599 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
602 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
603 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
604 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
605 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
607 {"Spelling/---", NULL, "---" },
608 {"Spelling/Options", NULL, N_("_Options") },
613 {"Options/ReplyMode", NULL, N_("Reply _mode") },
614 {"Options/---", NULL, "---" },
615 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
616 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
618 /* {"Options/---", NULL, "---" }, */
620 {"Options/Priority", NULL, N_("_Priority") },
622 {"Options/Encoding", NULL, N_("Character _encoding") },
623 {"Options/Encoding/---", NULL, "---" },
624 #define ENC_ACTION(cs_char,c_char,string) \
625 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
627 {"Options/Encoding/Western", NULL, N_("Western European") },
628 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
629 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
630 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
631 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
632 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
633 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
634 {"Options/Encoding/Korean", NULL, N_("Korean") },
635 {"Options/Encoding/Thai", NULL, N_("Thai") },
638 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
640 {"Tools/Template", NULL, N_("_Template") },
641 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
642 {"Tools/Actions", NULL, N_("Actio_ns") },
643 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
646 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
649 static GtkToggleActionEntry compose_toggle_entries[] =
651 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
652 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
653 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
654 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
655 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
656 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
659 static GtkRadioActionEntry compose_radio_rm_entries[] =
661 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
662 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
663 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
664 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
667 static GtkRadioActionEntry compose_radio_prio_entries[] =
669 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
670 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
671 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
672 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
673 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
676 static GtkRadioActionEntry compose_radio_enc_entries[] =
678 ENC_ACTION(CS_AUTO, C_AUTO, "_Automatic"), /* RADIO compose_set_encoding_cb */
679 ENC_ACTION(CS_US_ASCII, C_US_ASCII, "7bit ASCII (US-ASC_II)"), /* RADIO compose_set_encoding_cb */
680 ENC_ACTION(CS_UTF_8, C_UTF_8, "Unicode (_UTF-8)"), /* RADIO compose_set_encoding_cb */
681 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
682 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
683 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
684 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, "Central European (ISO-8859-_2)"), /* RADIO compose_set_encoding_cb */
685 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
686 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
687 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, "Greek (ISO-8859-_7)"), /* RADIO compose_set_encoding_cb */
688 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
689 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
690 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
691 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
692 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, "Turkish (ISO-8859-_9)"), /* RADIO compose_set_encoding_cb */
693 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
711 static GtkTargetEntry compose_mime_types[] =
713 {"text/uri-list", 0, 0},
714 {"UTF8_STRING", 0, 0},
718 static gboolean compose_put_existing_to_front(MsgInfo *info)
720 GList *compose_list = compose_get_compose_list();
724 for (elem = compose_list; elem != NULL && elem->data != NULL;
726 Compose *c = (Compose*)elem->data;
728 if (!c->targetinfo || !c->targetinfo->msgid ||
732 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
733 gtkut_window_popup(c->window);
741 static GdkColor quote_color1 =
742 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
743 static GdkColor quote_color2 =
744 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
745 static GdkColor quote_color3 =
746 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
748 static GdkColor quote_bgcolor1 =
749 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
750 static GdkColor quote_bgcolor2 =
751 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
752 static GdkColor quote_bgcolor3 =
753 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
755 static GdkColor signature_color = {
762 static GdkColor uri_color = {
769 static void compose_create_tags(GtkTextView *text, Compose *compose)
771 GtkTextBuffer *buffer;
772 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
778 buffer = gtk_text_view_get_buffer(text);
780 if (prefs_common.enable_color) {
781 /* grab the quote colors, converting from an int to a GdkColor */
782 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
784 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
786 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
788 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
790 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
792 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
794 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
796 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
799 signature_color = quote_color1 = quote_color2 = quote_color3 =
800 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
803 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
804 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
805 "foreground-gdk", "e_color1,
806 "paragraph-background-gdk", "e_bgcolor1,
808 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
809 "foreground-gdk", "e_color2,
810 "paragraph-background-gdk", "e_bgcolor2,
812 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
813 "foreground-gdk", "e_color3,
814 "paragraph-background-gdk", "e_bgcolor3,
817 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
818 "foreground-gdk", "e_color1,
820 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
821 "foreground-gdk", "e_color2,
823 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
824 "foreground-gdk", "e_color3,
828 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
829 "foreground-gdk", &signature_color,
832 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
833 "foreground-gdk", &uri_color,
835 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
836 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
838 color[0] = quote_color1;
839 color[1] = quote_color2;
840 color[2] = quote_color3;
841 color[3] = quote_bgcolor1;
842 color[4] = quote_bgcolor2;
843 color[5] = quote_bgcolor3;
844 color[6] = signature_color;
845 color[7] = uri_color;
846 cmap = gdk_drawable_get_colormap(compose->window->window);
847 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
849 for (i = 0; i < 8; i++) {
850 if (success[i] == FALSE) {
853 g_warning("Compose: color allocation failed.\n");
854 style = gtk_widget_get_style(GTK_WIDGET(text));
855 quote_color1 = quote_color2 = quote_color3 =
856 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
857 signature_color = uri_color = black;
862 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
863 GPtrArray *attach_files)
865 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
868 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
870 return compose_generic_new(account, mailto, item, NULL, NULL);
873 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
875 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
878 #define SCROLL_TO_CURSOR(compose) { \
879 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
880 gtk_text_view_get_buffer( \
881 GTK_TEXT_VIEW(compose->text))); \
882 gtk_text_view_scroll_mark_onscreen( \
883 GTK_TEXT_VIEW(compose->text), \
887 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
888 GPtrArray *attach_files, GList *listAddress )
891 GtkTextView *textview;
892 GtkTextBuffer *textbuf;
894 const gchar *subject_format = NULL;
895 const gchar *body_format = NULL;
896 gchar *mailto_from = NULL;
897 PrefsAccount *mailto_account = NULL;
898 MsgInfo* dummyinfo = NULL;
900 /* check if mailto defines a from */
901 if (mailto && *mailto != '\0') {
902 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL);
903 /* mailto defines a from, check if we can get account prefs from it,
904 if not, the account prefs will be guessed using other ways, but we'll keep
907 mailto_account = account_find_from_address(mailto_from, TRUE);
909 account = mailto_account;
912 /* if no account prefs set from mailto, set if from folder prefs (if any) */
913 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
914 account = account_find_from_id(item->prefs->default_account);
916 /* if no account prefs set, fallback to the current one */
917 if (!account) account = cur_account;
918 g_return_val_if_fail(account != NULL, NULL);
920 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
922 /* override from name if mailto asked for it */
924 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
927 /* override from name according to folder properties */
928 if (item && item->prefs &&
929 item->prefs->compose_with_format &&
930 item->prefs->compose_override_from_format &&
931 *item->prefs->compose_override_from_format != '\0') {
936 dummyinfo = compose_msginfo_new_from_compose(compose);
938 /* decode \-escape sequences in the internal representation of the quote format */
939 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
940 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
943 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
946 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
948 quote_fmt_scan_string(tmp);
951 buf = quote_fmt_get_buffer();
953 alertpanel_error(_("New message From format error."));
955 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
956 quote_fmt_reset_vartable();
961 compose->replyinfo = NULL;
962 compose->fwdinfo = NULL;
964 textview = GTK_TEXT_VIEW(compose->text);
965 textbuf = gtk_text_view_get_buffer(textview);
966 compose_create_tags(textview, compose);
968 undo_block(compose->undostruct);
970 compose_set_dictionaries_from_folder_prefs(compose, item);
973 if (account->auto_sig)
974 compose_insert_sig(compose, FALSE);
975 gtk_text_buffer_get_start_iter(textbuf, &iter);
976 gtk_text_buffer_place_cursor(textbuf, &iter);
978 if (account->protocol != A_NNTP) {
979 if (mailto && *mailto != '\0') {
980 compose_entries_set(compose, mailto, COMPOSE_TO);
982 } else if (item && item->prefs->enable_default_to) {
983 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
984 compose_entry_mark_default_to(compose, item->prefs->default_to);
986 if (item && item->ret_rcpt) {
987 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
990 if (mailto && *mailto != '\0') {
991 if (!strchr(mailto, '@'))
992 compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
994 compose_entries_set(compose, mailto, COMPOSE_TO);
995 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
996 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
999 * CLAWS: just don't allow return receipt request, even if the user
1000 * may want to send an email. simple but foolproof.
1002 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1004 compose_add_field_list( compose, listAddress );
1006 if (item && item->prefs && item->prefs->compose_with_format) {
1007 subject_format = item->prefs->compose_subject_format;
1008 body_format = item->prefs->compose_body_format;
1009 } else if (account->compose_with_format) {
1010 subject_format = account->compose_subject_format;
1011 body_format = account->compose_body_format;
1012 } else if (prefs_common.compose_with_format) {
1013 subject_format = prefs_common.compose_subject_format;
1014 body_format = prefs_common.compose_body_format;
1017 if (subject_format || body_format) {
1020 && *subject_format != '\0' )
1022 gchar *subject = NULL;
1027 dummyinfo = compose_msginfo_new_from_compose(compose);
1029 /* decode \-escape sequences in the internal representation of the quote format */
1030 tmp = malloc(strlen(subject_format)+1);
1031 pref_get_unescaped_pref(tmp, subject_format);
1033 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1035 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1036 compose->gtkaspell);
1038 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1040 quote_fmt_scan_string(tmp);
1043 buf = quote_fmt_get_buffer();
1045 alertpanel_error(_("New message subject format error."));
1047 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1048 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1049 quote_fmt_reset_vartable();
1056 && *body_format != '\0' )
1059 GtkTextBuffer *buffer;
1060 GtkTextIter start, end;
1064 dummyinfo = compose_msginfo_new_from_compose(compose);
1066 text = GTK_TEXT_VIEW(compose->text);
1067 buffer = gtk_text_view_get_buffer(text);
1068 gtk_text_buffer_get_start_iter(buffer, &start);
1069 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1070 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1072 compose_quote_fmt(compose, dummyinfo,
1074 NULL, tmp, FALSE, TRUE,
1075 _("New message body format error at line %d."));
1076 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1077 quote_fmt_reset_vartable();
1083 procmsg_msginfo_free( dummyinfo );
1089 for (i = 0; i < attach_files->len; i++) {
1090 file = g_ptr_array_index(attach_files, i);
1091 compose_attach_append(compose, file, file, NULL);
1095 compose_show_first_last_header(compose, TRUE);
1097 /* Set save folder */
1098 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1099 gchar *folderidentifier;
1101 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1102 folderidentifier = folder_item_get_identifier(item);
1103 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1104 g_free(folderidentifier);
1107 gtk_widget_grab_focus(compose->header_last->entry);
1109 undo_unblock(compose->undostruct);
1111 if (prefs_common.auto_exteditor)
1112 compose_exec_ext_editor(compose);
1114 compose->draft_timeout_tag = -1;
1115 SCROLL_TO_CURSOR(compose);
1117 compose->modified = FALSE;
1118 compose_set_title(compose);
1122 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1123 gboolean override_pref)
1125 gchar *privacy = NULL;
1127 g_return_if_fail(compose != NULL);
1128 g_return_if_fail(account != NULL);
1130 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1133 if (account->default_privacy_system
1134 && strlen(account->default_privacy_system)) {
1135 privacy = account->default_privacy_system;
1137 GSList *privacy_avail = privacy_get_system_ids();
1138 if (privacy_avail && g_slist_length(privacy_avail)) {
1139 privacy = (gchar *)(privacy_avail->data);
1142 if (privacy != NULL) {
1143 if (compose->privacy_system == NULL)
1144 compose->privacy_system = g_strdup(privacy);
1145 else if (*(compose->privacy_system) == '\0') {
1146 g_free(compose->privacy_system);
1147 compose->privacy_system = g_strdup(privacy);
1149 compose_update_privacy_system_menu_item(compose, FALSE);
1150 compose_use_encryption(compose, TRUE);
1154 static void compose_force_signing(Compose *compose, PrefsAccount *account)
1156 gchar *privacy = NULL;
1158 if (account->default_privacy_system
1159 && strlen(account->default_privacy_system)) {
1160 privacy = account->default_privacy_system;
1162 GSList *privacy_avail = privacy_get_system_ids();
1163 if (privacy_avail && g_slist_length(privacy_avail)) {
1164 privacy = (gchar *)(privacy_avail->data);
1167 if (privacy != NULL) {
1168 if (compose->privacy_system == NULL)
1169 compose->privacy_system = g_strdup(privacy);
1170 compose_update_privacy_system_menu_item(compose, FALSE);
1171 compose_use_signing(compose, TRUE);
1175 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1179 Compose *compose = NULL;
1181 g_return_val_if_fail(msginfo_list != NULL, NULL);
1183 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1184 g_return_val_if_fail(msginfo != NULL, NULL);
1186 list_len = g_slist_length(msginfo_list);
1190 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1191 FALSE, prefs_common.default_reply_list, FALSE, body);
1193 case COMPOSE_REPLY_WITH_QUOTE:
1194 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1195 FALSE, prefs_common.default_reply_list, FALSE, body);
1197 case COMPOSE_REPLY_WITHOUT_QUOTE:
1198 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1199 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1201 case COMPOSE_REPLY_TO_SENDER:
1202 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1203 FALSE, FALSE, TRUE, body);
1205 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1206 compose = compose_followup_and_reply_to(msginfo,
1207 COMPOSE_QUOTE_CHECK,
1208 FALSE, FALSE, body);
1210 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1211 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1212 FALSE, FALSE, TRUE, body);
1214 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1215 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1216 FALSE, FALSE, TRUE, NULL);
1218 case COMPOSE_REPLY_TO_ALL:
1219 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1220 TRUE, FALSE, FALSE, body);
1222 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1223 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1224 TRUE, FALSE, FALSE, body);
1226 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1227 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1228 TRUE, FALSE, FALSE, NULL);
1230 case COMPOSE_REPLY_TO_LIST:
1231 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1232 FALSE, TRUE, FALSE, body);
1234 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1235 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1236 FALSE, TRUE, FALSE, body);
1238 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1239 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1240 FALSE, TRUE, FALSE, NULL);
1242 case COMPOSE_FORWARD:
1243 if (prefs_common.forward_as_attachment) {
1244 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1247 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1251 case COMPOSE_FORWARD_INLINE:
1252 /* check if we reply to more than one Message */
1253 if (list_len == 1) {
1254 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1257 /* more messages FALL THROUGH */
1258 case COMPOSE_FORWARD_AS_ATTACH:
1259 compose = compose_forward_multiple(NULL, msginfo_list);
1261 case COMPOSE_REDIRECT:
1262 compose = compose_redirect(NULL, msginfo, FALSE);
1265 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1268 if (compose == NULL) {
1269 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1273 compose->rmode = mode;
1274 switch (compose->rmode) {
1276 case COMPOSE_REPLY_WITH_QUOTE:
1277 case COMPOSE_REPLY_WITHOUT_QUOTE:
1278 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1279 debug_print("reply mode Normal\n");
1280 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1281 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1283 case COMPOSE_REPLY_TO_SENDER:
1284 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1285 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1286 debug_print("reply mode Sender\n");
1287 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1289 case COMPOSE_REPLY_TO_ALL:
1290 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1291 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1292 debug_print("reply mode All\n");
1293 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1295 case COMPOSE_REPLY_TO_LIST:
1296 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1297 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1298 debug_print("reply mode List\n");
1299 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1307 static Compose *compose_reply(MsgInfo *msginfo,
1308 ComposeQuoteMode quote_mode,
1314 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1315 to_sender, FALSE, body);
1318 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1319 ComposeQuoteMode quote_mode,
1324 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1325 to_sender, TRUE, body);
1328 static void compose_extract_original_charset(Compose *compose)
1330 MsgInfo *info = NULL;
1331 if (compose->replyinfo) {
1332 info = compose->replyinfo;
1333 } else if (compose->fwdinfo) {
1334 info = compose->fwdinfo;
1335 } else if (compose->targetinfo) {
1336 info = compose->targetinfo;
1339 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1340 MimeInfo *partinfo = mimeinfo;
1341 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1342 partinfo = procmime_mimeinfo_next(partinfo);
1344 compose->orig_charset =
1345 g_strdup(procmime_mimeinfo_get_parameter(
1346 partinfo, "charset"));
1348 procmime_mimeinfo_free_all(mimeinfo);
1352 #define SIGNAL_BLOCK(buffer) { \
1353 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1354 G_CALLBACK(compose_changed_cb), \
1356 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1357 G_CALLBACK(text_inserted), \
1361 #define SIGNAL_UNBLOCK(buffer) { \
1362 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1363 G_CALLBACK(compose_changed_cb), \
1365 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1366 G_CALLBACK(text_inserted), \
1370 static Compose *compose_generic_reply(MsgInfo *msginfo,
1371 ComposeQuoteMode quote_mode,
1372 gboolean to_all, gboolean to_ml,
1374 gboolean followup_and_reply_to,
1378 PrefsAccount *account = NULL;
1379 GtkTextView *textview;
1380 GtkTextBuffer *textbuf;
1381 gboolean quote = FALSE;
1382 const gchar *qmark = NULL;
1383 const gchar *body_fmt = NULL;
1385 g_return_val_if_fail(msginfo != NULL, NULL);
1386 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1388 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1390 g_return_val_if_fail(account != NULL, NULL);
1392 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1394 compose->updating = TRUE;
1396 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1397 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1399 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1400 if (!compose->replyinfo)
1401 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1403 compose_extract_original_charset(compose);
1405 if (msginfo->folder && msginfo->folder->ret_rcpt)
1406 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1408 /* Set save folder */
1409 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1410 gchar *folderidentifier;
1412 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1413 folderidentifier = folder_item_get_identifier(msginfo->folder);
1414 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1415 g_free(folderidentifier);
1418 if (compose_parse_header(compose, msginfo) < 0) {
1419 compose->updating = FALSE;
1420 compose_destroy(compose);
1424 /* override from name according to folder properties */
1425 if (msginfo->folder && msginfo->folder->prefs &&
1426 msginfo->folder->prefs->reply_with_format &&
1427 msginfo->folder->prefs->reply_override_from_format &&
1428 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1433 /* decode \-escape sequences in the internal representation of the quote format */
1434 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1435 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1438 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1439 compose->gtkaspell);
1441 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1443 quote_fmt_scan_string(tmp);
1446 buf = quote_fmt_get_buffer();
1448 alertpanel_error(_("Message reply From format error."));
1450 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1451 quote_fmt_reset_vartable();
1456 textview = (GTK_TEXT_VIEW(compose->text));
1457 textbuf = gtk_text_view_get_buffer(textview);
1458 compose_create_tags(textview, compose);
1460 undo_block(compose->undostruct);
1462 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1465 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1466 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1467 /* use the reply format of folder (if enabled), or the account's one
1468 (if enabled) or fallback to the global reply format, which is always
1469 enabled (even if empty), and use the relevant quotemark */
1471 if (msginfo->folder && msginfo->folder->prefs &&
1472 msginfo->folder->prefs->reply_with_format) {
1473 qmark = msginfo->folder->prefs->reply_quotemark;
1474 body_fmt = msginfo->folder->prefs->reply_body_format;
1476 } else if (account->reply_with_format) {
1477 qmark = account->reply_quotemark;
1478 body_fmt = account->reply_body_format;
1481 qmark = prefs_common.quotemark;
1482 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1483 body_fmt = gettext(prefs_common.quotefmt);
1490 /* empty quotemark is not allowed */
1491 if (qmark == NULL || *qmark == '\0')
1493 compose_quote_fmt(compose, compose->replyinfo,
1494 body_fmt, qmark, body, FALSE, TRUE,
1495 _("Message reply format error at line %d."));
1496 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1497 quote_fmt_reset_vartable();
1500 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1501 compose_force_encryption(compose, account, FALSE);
1504 SIGNAL_BLOCK(textbuf);
1506 if (account->auto_sig)
1507 compose_insert_sig(compose, FALSE);
1509 compose_wrap_all(compose);
1511 SIGNAL_UNBLOCK(textbuf);
1513 gtk_widget_grab_focus(compose->text);
1515 undo_unblock(compose->undostruct);
1517 if (prefs_common.auto_exteditor)
1518 compose_exec_ext_editor(compose);
1520 compose->modified = FALSE;
1521 compose_set_title(compose);
1523 compose->updating = FALSE;
1524 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1525 SCROLL_TO_CURSOR(compose);
1527 if (compose->deferred_destroy) {
1528 compose_destroy(compose);
1535 #define INSERT_FW_HEADER(var, hdr) \
1536 if (msginfo->var && *msginfo->var) { \
1537 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1538 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1539 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1542 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1543 gboolean as_attach, const gchar *body,
1544 gboolean no_extedit,
1548 GtkTextView *textview;
1549 GtkTextBuffer *textbuf;
1552 g_return_val_if_fail(msginfo != NULL, NULL);
1553 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1556 !(account = compose_guess_forward_account_from_msginfo
1558 account = cur_account;
1560 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1562 compose->updating = TRUE;
1563 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1564 if (!compose->fwdinfo)
1565 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1567 compose_extract_original_charset(compose);
1569 if (msginfo->subject && *msginfo->subject) {
1570 gchar *buf, *buf2, *p;
1572 buf = p = g_strdup(msginfo->subject);
1573 p += subject_get_prefix_length(p);
1574 memmove(buf, p, strlen(p) + 1);
1576 buf2 = g_strdup_printf("Fw: %s", buf);
1577 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1583 /* override from name according to folder properties */
1584 if (msginfo->folder && msginfo->folder->prefs &&
1585 msginfo->folder->prefs->forward_with_format &&
1586 msginfo->folder->prefs->forward_override_from_format &&
1587 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1591 MsgInfo *full_msginfo = NULL;
1594 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1596 full_msginfo = procmsg_msginfo_copy(msginfo);
1598 /* decode \-escape sequences in the internal representation of the quote format */
1599 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1600 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1603 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1604 compose->gtkaspell);
1606 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1608 quote_fmt_scan_string(tmp);
1611 buf = quote_fmt_get_buffer();
1613 alertpanel_error(_("Message forward From format error."));
1615 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1616 quote_fmt_reset_vartable();
1619 procmsg_msginfo_free(full_msginfo);
1622 textview = GTK_TEXT_VIEW(compose->text);
1623 textbuf = gtk_text_view_get_buffer(textview);
1624 compose_create_tags(textview, compose);
1626 undo_block(compose->undostruct);
1630 msgfile = procmsg_get_message_file(msginfo);
1631 if (!is_file_exist(msgfile))
1632 g_warning("%s: file not exist\n", msgfile);
1634 compose_attach_append(compose, msgfile, msgfile,
1639 const gchar *qmark = NULL;
1640 const gchar *body_fmt = NULL;
1641 MsgInfo *full_msginfo;
1643 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1644 body_fmt = gettext(prefs_common.fw_quotefmt);
1648 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1650 full_msginfo = procmsg_msginfo_copy(msginfo);
1652 /* use the forward format of folder (if enabled), or the account's one
1653 (if enabled) or fallback to the global forward format, which is always
1654 enabled (even if empty), and use the relevant quotemark */
1655 if (msginfo->folder && msginfo->folder->prefs &&
1656 msginfo->folder->prefs->forward_with_format) {
1657 qmark = msginfo->folder->prefs->forward_quotemark;
1658 body_fmt = msginfo->folder->prefs->forward_body_format;
1660 } else if (account->forward_with_format) {
1661 qmark = account->forward_quotemark;
1662 body_fmt = account->forward_body_format;
1665 qmark = prefs_common.fw_quotemark;
1666 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1667 body_fmt = gettext(prefs_common.fw_quotefmt);
1672 /* empty quotemark is not allowed */
1673 if (qmark == NULL || *qmark == '\0')
1676 compose_quote_fmt(compose, full_msginfo,
1677 body_fmt, qmark, body, FALSE, TRUE,
1678 _("Message forward format error at line %d."));
1679 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1680 quote_fmt_reset_vartable();
1681 compose_attach_parts(compose, msginfo);
1683 procmsg_msginfo_free(full_msginfo);
1686 SIGNAL_BLOCK(textbuf);
1688 if (account->auto_sig)
1689 compose_insert_sig(compose, FALSE);
1691 compose_wrap_all(compose);
1693 SIGNAL_UNBLOCK(textbuf);
1695 gtk_text_buffer_get_start_iter(textbuf, &iter);
1696 gtk_text_buffer_place_cursor(textbuf, &iter);
1698 gtk_widget_grab_focus(compose->header_last->entry);
1700 if (!no_extedit && prefs_common.auto_exteditor)
1701 compose_exec_ext_editor(compose);
1704 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1705 gchar *folderidentifier;
1707 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1708 folderidentifier = folder_item_get_identifier(msginfo->folder);
1709 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1710 g_free(folderidentifier);
1713 undo_unblock(compose->undostruct);
1715 compose->modified = FALSE;
1716 compose_set_title(compose);
1718 compose->updating = FALSE;
1719 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1720 SCROLL_TO_CURSOR(compose);
1722 if (compose->deferred_destroy) {
1723 compose_destroy(compose);
1730 #undef INSERT_FW_HEADER
1732 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1735 GtkTextView *textview;
1736 GtkTextBuffer *textbuf;
1740 gboolean single_mail = TRUE;
1742 g_return_val_if_fail(msginfo_list != NULL, NULL);
1744 if (g_slist_length(msginfo_list) > 1)
1745 single_mail = FALSE;
1747 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1748 if (((MsgInfo *)msginfo->data)->folder == NULL)
1751 /* guess account from first selected message */
1753 !(account = compose_guess_forward_account_from_msginfo
1754 (msginfo_list->data)))
1755 account = cur_account;
1757 g_return_val_if_fail(account != NULL, NULL);
1759 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1760 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1761 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1764 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1766 compose->updating = TRUE;
1768 /* override from name according to folder properties */
1769 if (msginfo_list->data) {
1770 MsgInfo *msginfo = msginfo_list->data;
1772 if (msginfo->folder && msginfo->folder->prefs &&
1773 msginfo->folder->prefs->forward_with_format &&
1774 msginfo->folder->prefs->forward_override_from_format &&
1775 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1780 /* decode \-escape sequences in the internal representation of the quote format */
1781 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1782 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1785 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1786 compose->gtkaspell);
1788 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1790 quote_fmt_scan_string(tmp);
1793 buf = quote_fmt_get_buffer();
1795 alertpanel_error(_("Message forward From format error."));
1797 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1798 quote_fmt_reset_vartable();
1804 textview = GTK_TEXT_VIEW(compose->text);
1805 textbuf = gtk_text_view_get_buffer(textview);
1806 compose_create_tags(textview, compose);
1808 undo_block(compose->undostruct);
1809 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1810 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1812 if (!is_file_exist(msgfile))
1813 g_warning("%s: file not exist\n", msgfile);
1815 compose_attach_append(compose, msgfile, msgfile,
1821 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1822 if (info->subject && *info->subject) {
1823 gchar *buf, *buf2, *p;
1825 buf = p = g_strdup(info->subject);
1826 p += subject_get_prefix_length(p);
1827 memmove(buf, p, strlen(p) + 1);
1829 buf2 = g_strdup_printf("Fw: %s", buf);
1830 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1836 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1837 _("Fw: multiple emails"));
1840 SIGNAL_BLOCK(textbuf);
1842 if (account->auto_sig)
1843 compose_insert_sig(compose, FALSE);
1845 compose_wrap_all(compose);
1847 SIGNAL_UNBLOCK(textbuf);
1849 gtk_text_buffer_get_start_iter(textbuf, &iter);
1850 gtk_text_buffer_place_cursor(textbuf, &iter);
1852 gtk_widget_grab_focus(compose->header_last->entry);
1853 undo_unblock(compose->undostruct);
1854 compose->modified = FALSE;
1855 compose_set_title(compose);
1857 compose->updating = FALSE;
1858 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1859 SCROLL_TO_CURSOR(compose);
1861 if (compose->deferred_destroy) {
1862 compose_destroy(compose);
1869 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1871 GtkTextIter start = *iter;
1872 GtkTextIter end_iter;
1873 int start_pos = gtk_text_iter_get_offset(&start);
1875 if (!compose->account->sig_sep)
1878 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1879 start_pos+strlen(compose->account->sig_sep));
1881 /* check sig separator */
1882 str = gtk_text_iter_get_text(&start, &end_iter);
1883 if (!strcmp(str, compose->account->sig_sep)) {
1885 /* check end of line (\n) */
1886 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1887 start_pos+strlen(compose->account->sig_sep));
1888 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1889 start_pos+strlen(compose->account->sig_sep)+1);
1890 tmp = gtk_text_iter_get_text(&start, &end_iter);
1891 if (!strcmp(tmp,"\n")) {
1903 static void compose_colorize_signature(Compose *compose)
1905 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1907 GtkTextIter end_iter;
1908 gtk_text_buffer_get_start_iter(buffer, &iter);
1909 while (gtk_text_iter_forward_line(&iter))
1910 if (compose_is_sig_separator(compose, buffer, &iter)) {
1911 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1912 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1916 #define BLOCK_WRAP() { \
1917 prev_autowrap = compose->autowrap; \
1918 buffer = gtk_text_view_get_buffer( \
1919 GTK_TEXT_VIEW(compose->text)); \
1920 compose->autowrap = FALSE; \
1922 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1923 G_CALLBACK(compose_changed_cb), \
1925 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1926 G_CALLBACK(text_inserted), \
1929 #define UNBLOCK_WRAP() { \
1930 compose->autowrap = prev_autowrap; \
1931 if (compose->autowrap) { \
1932 gint old = compose->draft_timeout_tag; \
1933 compose->draft_timeout_tag = -2; \
1934 compose_wrap_all(compose); \
1935 compose->draft_timeout_tag = old; \
1938 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1939 G_CALLBACK(compose_changed_cb), \
1941 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1942 G_CALLBACK(text_inserted), \
1946 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
1948 Compose *compose = NULL;
1949 PrefsAccount *account = NULL;
1950 GtkTextView *textview;
1951 GtkTextBuffer *textbuf;
1955 gchar buf[BUFFSIZE];
1956 gboolean use_signing = FALSE;
1957 gboolean use_encryption = FALSE;
1958 gchar *privacy_system = NULL;
1959 int priority = PRIORITY_NORMAL;
1960 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
1961 gboolean autowrap = prefs_common.autowrap;
1963 g_return_val_if_fail(msginfo != NULL, NULL);
1964 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1966 if (compose_put_existing_to_front(msginfo)) {
1970 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
1971 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
1972 gchar queueheader_buf[BUFFSIZE];
1975 /* Select Account from queue headers */
1976 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1977 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
1978 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
1979 account = account_find_from_id(id);
1981 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1982 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
1983 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
1984 account = account_find_from_id(id);
1986 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1987 sizeof(queueheader_buf), "NAID:")) {
1988 id = atoi(&queueheader_buf[strlen("NAID:")]);
1989 account = account_find_from_id(id);
1991 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1992 sizeof(queueheader_buf), "MAID:")) {
1993 id = atoi(&queueheader_buf[strlen("MAID:")]);
1994 account = account_find_from_id(id);
1996 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1997 sizeof(queueheader_buf), "S:")) {
1998 account = account_find_from_address(queueheader_buf, FALSE);
2000 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2001 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2002 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2003 use_signing = param;
2006 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2007 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2008 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2009 use_signing = param;
2012 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2013 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2014 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2015 use_encryption = param;
2017 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2018 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2019 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2020 use_encryption = param;
2022 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2023 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2024 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2027 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2028 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2029 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2031 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2032 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2033 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2035 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2036 sizeof(queueheader_buf), "X-Priority: ")) {
2037 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2040 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2041 sizeof(queueheader_buf), "RMID:")) {
2042 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2043 if (tokens[0] && tokens[1] && tokens[2]) {
2044 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2045 if (orig_item != NULL) {
2046 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2051 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2052 sizeof(queueheader_buf), "FMID:")) {
2053 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2054 if (tokens[0] && tokens[1] && tokens[2]) {
2055 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2056 if (orig_item != NULL) {
2057 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2063 account = msginfo->folder->folder->account;
2066 if (!account && prefs_common.reedit_account_autosel) {
2067 gchar from[BUFFSIZE];
2068 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2069 extract_address(from);
2070 account = account_find_from_address(from, FALSE);
2074 account = cur_account;
2076 g_return_val_if_fail(account != NULL, NULL);
2078 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2080 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2081 compose->autowrap = autowrap;
2082 compose->replyinfo = replyinfo;
2083 compose->fwdinfo = fwdinfo;
2085 compose->updating = TRUE;
2086 compose->priority = priority;
2088 if (privacy_system != NULL) {
2089 compose->privacy_system = privacy_system;
2090 compose_use_signing(compose, use_signing);
2091 compose_use_encryption(compose, use_encryption);
2092 compose_update_privacy_system_menu_item(compose, FALSE);
2094 activate_privacy_system(compose, account, FALSE);
2097 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2099 compose_extract_original_charset(compose);
2101 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2102 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2103 gchar queueheader_buf[BUFFSIZE];
2105 /* Set message save folder */
2106 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2109 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2110 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
2111 gtk_editable_insert_text(GTK_EDITABLE(compose->savemsg_entry), &queueheader_buf[4], strlen(&queueheader_buf[4]), &startpos);
2113 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2114 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2116 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2121 if (compose_parse_header(compose, msginfo) < 0) {
2122 compose->updating = FALSE;
2123 compose_destroy(compose);
2126 compose_reedit_set_entry(compose, msginfo);
2128 textview = GTK_TEXT_VIEW(compose->text);
2129 textbuf = gtk_text_view_get_buffer(textview);
2130 compose_create_tags(textview, compose);
2132 mark = gtk_text_buffer_get_insert(textbuf);
2133 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2135 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2136 G_CALLBACK(compose_changed_cb),
2139 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2140 fp = procmime_get_first_encrypted_text_content(msginfo);
2142 compose_force_encryption(compose, account, TRUE);
2145 fp = procmime_get_first_text_content(msginfo);
2148 g_warning("Can't get text part\n");
2152 gboolean prev_autowrap = compose->autowrap;
2153 GtkTextBuffer *buffer = textbuf;
2155 while (fgets(buf, sizeof(buf), fp) != NULL) {
2157 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2163 compose_attach_parts(compose, msginfo);
2165 compose_colorize_signature(compose);
2167 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2168 G_CALLBACK(compose_changed_cb),
2171 gtk_widget_grab_focus(compose->text);
2173 if (prefs_common.auto_exteditor) {
2174 compose_exec_ext_editor(compose);
2176 compose->modified = FALSE;
2177 compose_set_title(compose);
2179 compose->updating = FALSE;
2180 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2181 SCROLL_TO_CURSOR(compose);
2183 if (compose->deferred_destroy) {
2184 compose_destroy(compose);
2188 compose->sig_str = compose_get_signature_str(compose);
2193 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2200 g_return_val_if_fail(msginfo != NULL, NULL);
2203 account = account_get_reply_account(msginfo,
2204 prefs_common.reply_account_autosel);
2205 g_return_val_if_fail(account != NULL, NULL);
2207 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2209 compose->updating = TRUE;
2211 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2212 compose->replyinfo = NULL;
2213 compose->fwdinfo = NULL;
2215 compose_show_first_last_header(compose, TRUE);
2217 gtk_widget_grab_focus(compose->header_last->entry);
2219 filename = procmsg_get_message_file(msginfo);
2221 if (filename == NULL) {
2222 compose->updating = FALSE;
2223 compose_destroy(compose);
2228 compose->redirect_filename = filename;
2230 /* Set save folder */
2231 item = msginfo->folder;
2232 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2233 gchar *folderidentifier;
2235 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2236 folderidentifier = folder_item_get_identifier(item);
2237 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
2238 g_free(folderidentifier);
2241 compose_attach_parts(compose, msginfo);
2243 if (msginfo->subject)
2244 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2246 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2248 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2249 _("Message redirect format error at line %d."));
2250 quote_fmt_reset_vartable();
2251 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2253 compose_colorize_signature(compose);
2256 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2257 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2258 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2260 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2261 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2262 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2263 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2264 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2265 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2266 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2267 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2269 if (compose->toolbar->draft_btn)
2270 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2271 if (compose->toolbar->insert_btn)
2272 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2273 if (compose->toolbar->attach_btn)
2274 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2275 if (compose->toolbar->sig_btn)
2276 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2277 if (compose->toolbar->exteditor_btn)
2278 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2279 if (compose->toolbar->linewrap_current_btn)
2280 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2281 if (compose->toolbar->linewrap_all_btn)
2282 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2284 compose->modified = FALSE;
2285 compose_set_title(compose);
2286 compose->updating = FALSE;
2287 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2288 SCROLL_TO_CURSOR(compose);
2290 if (compose->deferred_destroy) {
2291 compose_destroy(compose);
2298 GList *compose_get_compose_list(void)
2300 return compose_list;
2303 void compose_entry_append(Compose *compose, const gchar *address,
2304 ComposeEntryType type)
2306 const gchar *header;
2308 gboolean in_quote = FALSE;
2309 if (!address || *address == '\0') return;
2316 header = N_("Bcc:");
2318 case COMPOSE_REPLYTO:
2319 header = N_("Reply-To:");
2321 case COMPOSE_NEWSGROUPS:
2322 header = N_("Newsgroups:");
2324 case COMPOSE_FOLLOWUPTO:
2325 header = N_( "Followup-To:");
2332 header = prefs_common_translated_header_name(header);
2334 cur = begin = (gchar *)address;
2336 /* we separate the line by commas, but not if we're inside a quoted
2338 while (*cur != '\0') {
2340 in_quote = !in_quote;
2341 if (*cur == ',' && !in_quote) {
2342 gchar *tmp = g_strdup(begin);
2344 tmp[cur-begin]='\0';
2347 while (*tmp == ' ' || *tmp == '\t')
2349 compose_add_header_entry(compose, header, tmp);
2356 gchar *tmp = g_strdup(begin);
2358 tmp[cur-begin]='\0';
2361 while (*tmp == ' ' || *tmp == '\t')
2363 compose_add_header_entry(compose, header, tmp);
2368 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2370 static GdkColor yellow;
2371 static GdkColor black;
2372 static gboolean yellow_initialised = FALSE;
2376 if (!yellow_initialised) {
2377 gdk_color_parse("#f5f6be", &yellow);
2378 gdk_color_parse("#000000", &black);
2379 yellow_initialised = gdk_colormap_alloc_color(
2380 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2381 yellow_initialised &= gdk_colormap_alloc_color(
2382 gdk_colormap_get_system(), &black, FALSE, TRUE);
2385 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2386 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2387 if (gtk_entry_get_text(entry) &&
2388 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2389 if (yellow_initialised) {
2390 gtk_widget_modify_base(
2391 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2392 GTK_STATE_NORMAL, &yellow);
2393 gtk_widget_modify_text(
2394 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2395 GTK_STATE_NORMAL, &black);
2401 void compose_toolbar_cb(gint action, gpointer data)
2403 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2404 Compose *compose = (Compose*)toolbar_item->parent;
2406 g_return_if_fail(compose != NULL);
2410 compose_send_cb(NULL, compose);
2413 compose_send_later_cb(NULL, compose);
2416 compose_draft(compose, COMPOSE_QUIT_EDITING);
2419 compose_insert_file_cb(NULL, compose);
2422 compose_attach_cb(NULL, compose);
2425 compose_insert_sig(compose, FALSE);
2428 compose_ext_editor_cb(NULL, compose);
2430 case A_LINEWRAP_CURRENT:
2431 compose_beautify_paragraph(compose, NULL, TRUE);
2433 case A_LINEWRAP_ALL:
2434 compose_wrap_all_full(compose, TRUE);
2437 compose_address_cb(NULL, compose);
2440 case A_CHECK_SPELLING:
2441 compose_check_all(NULL, compose);
2449 static void compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2454 gchar *subject = NULL;
2458 gchar **attach = NULL;
2460 /* get mailto parts but skip from */
2461 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2464 compose_entry_append(compose, to, to_type);
2466 compose_entry_append(compose, cc, COMPOSE_CC);
2468 compose_entry_append(compose, bcc, COMPOSE_BCC);
2470 if (!g_utf8_validate (subject, -1, NULL)) {
2471 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2472 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2475 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2479 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2480 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2483 gboolean prev_autowrap = compose->autowrap;
2485 compose->autowrap = FALSE;
2487 mark = gtk_text_buffer_get_insert(buffer);
2488 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2490 if (!g_utf8_validate (body, -1, NULL)) {
2491 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2492 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2495 gtk_text_buffer_insert(buffer, &iter, body, -1);
2497 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2499 compose->autowrap = prev_autowrap;
2500 if (compose->autowrap)
2501 compose_wrap_all(compose);
2505 gint i = 0, att = 0;
2506 gchar *warn_files = NULL;
2507 while (attach[i] != NULL) {
2508 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2509 if (utf8_filename) {
2510 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2511 gchar *tmp = g_strdup_printf("%s%s\n",
2512 warn_files?warn_files:"",
2518 g_free(utf8_filename);
2520 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2525 alertpanel_notice(ngettext(
2526 "The following file has been attached: \n%s",
2527 "The following files have been attached: \n%s", att), warn_files);
2539 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2541 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2542 {"Cc:", NULL, TRUE},
2543 {"References:", NULL, FALSE},
2544 {"Bcc:", NULL, TRUE},
2545 {"Newsgroups:", NULL, TRUE},
2546 {"Followup-To:", NULL, TRUE},
2547 {"List-Post:", NULL, FALSE},
2548 {"X-Priority:", NULL, FALSE},
2549 {NULL, NULL, FALSE}};
2565 g_return_val_if_fail(msginfo != NULL, -1);
2567 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2568 procheader_get_header_fields(fp, hentry);
2571 if (hentry[H_REPLY_TO].body != NULL) {
2572 if (hentry[H_REPLY_TO].body[0] != '\0') {
2574 conv_unmime_header(hentry[H_REPLY_TO].body,
2577 g_free(hentry[H_REPLY_TO].body);
2578 hentry[H_REPLY_TO].body = NULL;
2580 if (hentry[H_CC].body != NULL) {
2581 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2582 g_free(hentry[H_CC].body);
2583 hentry[H_CC].body = NULL;
2585 if (hentry[H_REFERENCES].body != NULL) {
2586 if (compose->mode == COMPOSE_REEDIT)
2587 compose->references = hentry[H_REFERENCES].body;
2589 compose->references = compose_parse_references
2590 (hentry[H_REFERENCES].body, msginfo->msgid);
2591 g_free(hentry[H_REFERENCES].body);
2593 hentry[H_REFERENCES].body = NULL;
2595 if (hentry[H_BCC].body != NULL) {
2596 if (compose->mode == COMPOSE_REEDIT)
2598 conv_unmime_header(hentry[H_BCC].body, NULL);
2599 g_free(hentry[H_BCC].body);
2600 hentry[H_BCC].body = NULL;
2602 if (hentry[H_NEWSGROUPS].body != NULL) {
2603 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2604 hentry[H_NEWSGROUPS].body = NULL;
2606 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2607 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2608 compose->followup_to =
2609 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2612 g_free(hentry[H_FOLLOWUP_TO].body);
2613 hentry[H_FOLLOWUP_TO].body = NULL;
2615 if (hentry[H_LIST_POST].body != NULL) {
2618 extract_address(hentry[H_LIST_POST].body);
2619 if (hentry[H_LIST_POST].body[0] != '\0') {
2620 scan_mailto_url(hentry[H_LIST_POST].body,
2621 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2623 g_free(compose->ml_post);
2624 compose->ml_post = to;
2627 g_free(hentry[H_LIST_POST].body);
2628 hentry[H_LIST_POST].body = NULL;
2631 /* CLAWS - X-Priority */
2632 if (compose->mode == COMPOSE_REEDIT)
2633 if (hentry[H_X_PRIORITY].body != NULL) {
2636 priority = atoi(hentry[H_X_PRIORITY].body);
2637 g_free(hentry[H_X_PRIORITY].body);
2639 hentry[H_X_PRIORITY].body = NULL;
2641 if (priority < PRIORITY_HIGHEST ||
2642 priority > PRIORITY_LOWEST)
2643 priority = PRIORITY_NORMAL;
2645 compose->priority = priority;
2648 if (compose->mode == COMPOSE_REEDIT) {
2649 if (msginfo->inreplyto && *msginfo->inreplyto)
2650 compose->inreplyto = g_strdup(msginfo->inreplyto);
2654 if (msginfo->msgid && *msginfo->msgid)
2655 compose->inreplyto = g_strdup(msginfo->msgid);
2657 if (!compose->references) {
2658 if (msginfo->msgid && *msginfo->msgid) {
2659 if (msginfo->inreplyto && *msginfo->inreplyto)
2660 compose->references =
2661 g_strdup_printf("<%s>\n\t<%s>",
2665 compose->references =
2666 g_strconcat("<", msginfo->msgid, ">",
2668 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2669 compose->references =
2670 g_strconcat("<", msginfo->inreplyto, ">",
2678 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2680 GSList *ref_id_list, *cur;
2684 ref_id_list = references_list_append(NULL, ref);
2685 if (!ref_id_list) return NULL;
2686 if (msgid && *msgid)
2687 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2692 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2693 /* "<" + Message-ID + ">" + CR+LF+TAB */
2694 len += strlen((gchar *)cur->data) + 5;
2696 if (len > MAX_REFERENCES_LEN) {
2697 /* remove second message-ID */
2698 if (ref_id_list && ref_id_list->next &&
2699 ref_id_list->next->next) {
2700 g_free(ref_id_list->next->data);
2701 ref_id_list = g_slist_remove
2702 (ref_id_list, ref_id_list->next->data);
2704 slist_free_strings(ref_id_list);
2705 g_slist_free(ref_id_list);
2712 new_ref = g_string_new("");
2713 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2714 if (new_ref->len > 0)
2715 g_string_append(new_ref, "\n\t");
2716 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2719 slist_free_strings(ref_id_list);
2720 g_slist_free(ref_id_list);
2722 new_ref_str = new_ref->str;
2723 g_string_free(new_ref, FALSE);
2728 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2729 const gchar *fmt, const gchar *qmark,
2730 const gchar *body, gboolean rewrap,
2731 gboolean need_unescape,
2732 const gchar *err_msg)
2734 MsgInfo* dummyinfo = NULL;
2735 gchar *quote_str = NULL;
2737 gboolean prev_autowrap;
2738 const gchar *trimmed_body = body;
2739 gint cursor_pos = -1;
2740 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2741 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2746 SIGNAL_BLOCK(buffer);
2749 dummyinfo = compose_msginfo_new_from_compose(compose);
2750 msginfo = dummyinfo;
2753 if (qmark != NULL) {
2755 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2756 compose->gtkaspell);
2758 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2760 quote_fmt_scan_string(qmark);
2763 buf = quote_fmt_get_buffer();
2765 alertpanel_error(_("Quote mark format error."));
2767 Xstrdup_a(quote_str, buf, goto error)
2770 if (fmt && *fmt != '\0') {
2773 while (*trimmed_body == '\n')
2777 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2778 compose->gtkaspell);
2780 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2782 if (need_unescape) {
2785 /* decode \-escape sequences in the internal representation of the quote format */
2786 tmp = malloc(strlen(fmt)+1);
2787 pref_get_unescaped_pref(tmp, fmt);
2788 quote_fmt_scan_string(tmp);
2792 quote_fmt_scan_string(fmt);
2796 buf = quote_fmt_get_buffer();
2798 gint line = quote_fmt_get_line();
2799 alertpanel_error(err_msg, line);
2805 prev_autowrap = compose->autowrap;
2806 compose->autowrap = FALSE;
2808 mark = gtk_text_buffer_get_insert(buffer);
2809 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2810 if (g_utf8_validate(buf, -1, NULL)) {
2811 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2813 gchar *tmpout = NULL;
2814 tmpout = conv_codeset_strdup
2815 (buf, conv_get_locale_charset_str_no_utf8(),
2817 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2819 tmpout = g_malloc(strlen(buf)*2+1);
2820 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2822 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2826 cursor_pos = quote_fmt_get_cursor_pos();
2827 if (cursor_pos == -1)
2828 cursor_pos = gtk_text_iter_get_offset(&iter);
2829 compose->set_cursor_pos = cursor_pos;
2831 gtk_text_buffer_get_start_iter(buffer, &iter);
2832 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2833 gtk_text_buffer_place_cursor(buffer, &iter);
2835 compose->autowrap = prev_autowrap;
2836 if (compose->autowrap && rewrap)
2837 compose_wrap_all(compose);
2844 SIGNAL_UNBLOCK(buffer);
2846 procmsg_msginfo_free( dummyinfo );
2851 /* if ml_post is of type addr@host and from is of type
2852 * addr-anything@host, return TRUE
2854 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2856 gchar *left_ml = NULL;
2857 gchar *right_ml = NULL;
2858 gchar *left_from = NULL;
2859 gchar *right_from = NULL;
2860 gboolean result = FALSE;
2862 if (!ml_post || !from)
2865 left_ml = g_strdup(ml_post);
2866 if (strstr(left_ml, "@")) {
2867 right_ml = strstr(left_ml, "@")+1;
2868 *(strstr(left_ml, "@")) = '\0';
2871 left_from = g_strdup(from);
2872 if (strstr(left_from, "@")) {
2873 right_from = strstr(left_from, "@")+1;
2874 *(strstr(left_from, "@")) = '\0';
2877 if (left_ml && left_from && right_ml && right_from
2878 && !strncmp(left_from, left_ml, strlen(left_ml))
2879 && !strcmp(right_from, right_ml)) {
2888 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2890 gchar *my_addr1, *my_addr2;
2892 if (!addr1 || !addr2)
2895 Xstrdup_a(my_addr1, addr1, return FALSE);
2896 Xstrdup_a(my_addr2, addr2, return FALSE);
2898 extract_address(my_addr1);
2899 extract_address(my_addr2);
2901 return !strcasecmp(my_addr1, my_addr2);
2904 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2905 gboolean to_all, gboolean to_ml,
2907 gboolean followup_and_reply_to)
2909 GSList *cc_list = NULL;
2912 gchar *replyto = NULL;
2913 GHashTable *to_table;
2915 gboolean reply_to_ml = FALSE;
2916 gboolean default_reply_to = FALSE;
2918 g_return_if_fail(compose->account != NULL);
2919 g_return_if_fail(msginfo != NULL);
2921 reply_to_ml = to_ml && compose->ml_post;
2923 default_reply_to = msginfo->folder &&
2924 msginfo->folder->prefs->enable_default_reply_to;
2926 if (compose->account->protocol != A_NNTP) {
2927 if (reply_to_ml && !default_reply_to) {
2929 gboolean is_subscr = is_subscription(compose->ml_post,
2932 /* normal answer to ml post with a reply-to */
2933 compose_entry_append(compose,
2936 if (compose->replyto
2937 && !same_address(compose->ml_post, compose->replyto))
2938 compose_entry_append(compose,
2942 /* answer to subscription confirmation */
2943 if (compose->replyto)
2944 compose_entry_append(compose,
2947 else if (msginfo->from)
2948 compose_entry_append(compose,
2953 else if (!(to_all || to_sender) && default_reply_to) {
2954 compose_entry_append(compose,
2955 msginfo->folder->prefs->default_reply_to,
2957 compose_entry_mark_default_to(compose,
2958 msginfo->folder->prefs->default_reply_to);
2963 Xstrdup_a(tmp1, msginfo->from, return);
2964 extract_address(tmp1);
2965 if (to_all || to_sender ||
2966 !account_find_from_address(tmp1, FALSE))
2967 compose_entry_append(compose,
2968 (compose->replyto && !to_sender)
2969 ? compose->replyto :
2970 msginfo->from ? msginfo->from : "",
2972 else if (!to_all && !to_sender) {
2973 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2974 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
2975 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2976 if (compose->replyto) {
2977 compose_entry_append(compose,
2981 compose_entry_append(compose,
2982 msginfo->from ? msginfo->from : "",
2986 /* replying to own mail, use original recp */
2987 compose_entry_append(compose,
2988 msginfo->to ? msginfo->to : "",
2990 compose_entry_append(compose,
2991 msginfo->cc ? msginfo->cc : "",
2997 if (to_sender || (compose->followup_to &&
2998 !strncmp(compose->followup_to, "poster", 6)))
2999 compose_entry_append
3001 (compose->replyto ? compose->replyto :
3002 msginfo->from ? msginfo->from : ""),
3005 else if (followup_and_reply_to || to_all) {
3006 compose_entry_append
3008 (compose->replyto ? compose->replyto :
3009 msginfo->from ? msginfo->from : ""),
3012 compose_entry_append
3014 compose->followup_to ? compose->followup_to :
3015 compose->newsgroups ? compose->newsgroups : "",
3016 COMPOSE_NEWSGROUPS);
3019 compose_entry_append
3021 compose->followup_to ? compose->followup_to :
3022 compose->newsgroups ? compose->newsgroups : "",
3023 COMPOSE_NEWSGROUPS);
3026 if (msginfo->subject && *msginfo->subject) {
3030 buf = p = g_strdup(msginfo->subject);
3031 p += subject_get_prefix_length(p);
3032 memmove(buf, p, strlen(p) + 1);
3034 buf2 = g_strdup_printf("Re: %s", buf);
3035 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3040 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3042 if (to_ml && compose->ml_post) return;
3043 if (!to_all || compose->account->protocol == A_NNTP) return;
3045 if (compose->replyto) {
3046 Xstrdup_a(replyto, compose->replyto, return);
3047 extract_address(replyto);
3049 if (msginfo->from) {
3050 Xstrdup_a(from, msginfo->from, return);
3051 extract_address(from);
3054 if (replyto && from)
3055 cc_list = address_list_append_with_comments(cc_list, from);
3056 if (to_all && msginfo->folder &&
3057 msginfo->folder->prefs->enable_default_reply_to)
3058 cc_list = address_list_append_with_comments(cc_list,
3059 msginfo->folder->prefs->default_reply_to);
3060 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3061 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3063 to_table = g_hash_table_new(g_str_hash, g_str_equal);
3065 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
3066 if (compose->account) {
3067 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
3068 GINT_TO_POINTER(1));
3070 /* remove address on To: and that of current account */
3071 for (cur = cc_list; cur != NULL; ) {
3072 GSList *next = cur->next;
3075 addr = g_utf8_strdown(cur->data, -1);
3076 extract_address(addr);
3078 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3079 cc_list = g_slist_remove(cc_list, cur->data);
3081 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3085 hash_free_strings(to_table);
3086 g_hash_table_destroy(to_table);
3089 for (cur = cc_list; cur != NULL; cur = cur->next)
3090 compose_entry_append(compose, (gchar *)cur->data,
3092 slist_free_strings(cc_list);
3093 g_slist_free(cc_list);
3098 #define SET_ENTRY(entry, str) \
3101 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3104 #define SET_ADDRESS(type, str) \
3107 compose_entry_append(compose, str, type); \
3110 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3112 g_return_if_fail(msginfo != NULL);
3114 SET_ENTRY(subject_entry, msginfo->subject);
3115 SET_ENTRY(from_name, msginfo->from);
3116 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3117 SET_ADDRESS(COMPOSE_CC, compose->cc);
3118 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3119 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3120 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3121 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3123 compose_update_priority_menu_item(compose);
3124 compose_update_privacy_system_menu_item(compose, FALSE);
3125 compose_show_first_last_header(compose, TRUE);
3131 static void compose_insert_sig(Compose *compose, gboolean replace)
3133 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3134 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3136 GtkTextIter iter, iter_end;
3137 gint cur_pos, ins_pos;
3138 gboolean prev_autowrap;
3139 gboolean found = FALSE;
3140 gboolean exists = FALSE;
3142 g_return_if_fail(compose->account != NULL);
3146 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3147 G_CALLBACK(compose_changed_cb),
3150 mark = gtk_text_buffer_get_insert(buffer);
3151 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3152 cur_pos = gtk_text_iter_get_offset (&iter);
3155 gtk_text_buffer_get_end_iter(buffer, &iter);
3157 exists = (compose->sig_str != NULL);
3160 GtkTextIter first_iter, start_iter, end_iter;
3162 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3164 if (!exists || compose->sig_str[0] == '\0')
3167 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3168 compose->signature_tag);
3171 /* include previous \n\n */
3172 gtk_text_iter_backward_chars(&first_iter, 1);
3173 start_iter = first_iter;
3174 end_iter = first_iter;
3176 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3177 compose->signature_tag);
3178 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3179 compose->signature_tag);
3181 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3187 g_free(compose->sig_str);
3188 compose->sig_str = compose_get_signature_str(compose);
3190 cur_pos = gtk_text_iter_get_offset(&iter);
3192 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3193 g_free(compose->sig_str);
3194 compose->sig_str = NULL;
3196 if (compose->sig_inserted == FALSE)
3197 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3198 compose->sig_inserted = TRUE;
3200 cur_pos = gtk_text_iter_get_offset(&iter);
3201 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3203 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3204 gtk_text_iter_forward_chars(&iter, 1);
3205 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3206 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3208 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3209 cur_pos = gtk_text_buffer_get_char_count (buffer);
3212 /* put the cursor where it should be
3213 * either where the quote_fmt says, either where it was */
3214 if (compose->set_cursor_pos < 0)
3215 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3217 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3218 compose->set_cursor_pos);
3220 gtk_text_buffer_place_cursor(buffer, &iter);
3221 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3222 G_CALLBACK(compose_changed_cb),
3228 static gchar *compose_get_signature_str(Compose *compose)
3230 gchar *sig_body = NULL;
3231 gchar *sig_str = NULL;
3232 gchar *utf8_sig_str = NULL;
3234 g_return_val_if_fail(compose->account != NULL, NULL);
3236 if (!compose->account->sig_path)
3239 if (compose->account->sig_type == SIG_FILE) {
3240 if (!is_file_or_fifo_exist(compose->account->sig_path)) {
3241 g_warning("can't open signature file: %s\n",
3242 compose->account->sig_path);
3247 if (compose->account->sig_type == SIG_COMMAND)
3248 sig_body = get_command_output(compose->account->sig_path);
3252 tmp = file_read_to_str(compose->account->sig_path);
3255 sig_body = normalize_newlines(tmp);
3259 if (compose->account->sig_sep) {
3260 sig_str = g_strconcat("\n", compose->account->sig_sep, "\n", sig_body,
3264 sig_str = g_strconcat("\n", sig_body, NULL);
3267 if (g_utf8_validate(sig_str, -1, NULL) == TRUE)
3268 utf8_sig_str = sig_str;
3270 utf8_sig_str = conv_codeset_strdup
3271 (sig_str, conv_get_locale_charset_str_no_utf8(),
3277 return utf8_sig_str;
3280 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3283 GtkTextBuffer *buffer;
3286 const gchar *cur_encoding;
3287 gchar buf[BUFFSIZE];
3290 gboolean prev_autowrap;
3291 gboolean badtxt = FALSE;
3293 g_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3295 if ((fp = g_fopen(file, "rb")) == NULL) {
3296 FILE_OP_ERROR(file, "fopen");
3297 return COMPOSE_INSERT_READ_ERROR;
3300 prev_autowrap = compose->autowrap;
3301 compose->autowrap = FALSE;
3303 text = GTK_TEXT_VIEW(compose->text);
3304 buffer = gtk_text_view_get_buffer(text);
3305 mark = gtk_text_buffer_get_insert(buffer);
3306 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3308 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3309 G_CALLBACK(text_inserted),
3312 cur_encoding = conv_get_locale_charset_str_no_utf8();
3314 while (fgets(buf, sizeof(buf), fp) != NULL) {
3317 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3318 str = g_strdup(buf);
3320 str = conv_codeset_strdup
3321 (buf, cur_encoding, CS_INTERNAL);
3324 /* strip <CR> if DOS/Windows file,
3325 replace <CR> with <LF> if Macintosh file. */
3328 if (len > 0 && str[len - 1] != '\n') {
3330 if (str[len] == '\r') str[len] = '\n';
3333 gtk_text_buffer_insert(buffer, &iter, str, -1);
3337 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3338 G_CALLBACK(text_inserted),
3340 compose->autowrap = prev_autowrap;
3341 if (compose->autowrap)
3342 compose_wrap_all(compose);
3347 return COMPOSE_INSERT_INVALID_CHARACTER;
3349 return COMPOSE_INSERT_SUCCESS;
3352 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3353 const gchar *filename,
3354 const gchar *content_type)
3362 GtkListStore *store;
3364 gboolean has_binary = FALSE;
3366 if (!is_file_exist(file)) {
3367 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3368 gboolean result = FALSE;
3369 if (file_from_uri && is_file_exist(file_from_uri)) {
3370 result = compose_attach_append(
3371 compose, file_from_uri,
3375 g_free(file_from_uri);
3378 alertpanel_error("File %s doesn't exist\n", filename);
3381 if ((size = get_file_size(file)) < 0) {
3382 alertpanel_error("Can't get file size of %s\n", filename);
3386 alertpanel_error(_("File %s is empty."), filename);
3389 if ((fp = g_fopen(file, "rb")) == NULL) {
3390 alertpanel_error(_("Can't read %s."), filename);
3395 ainfo = g_new0(AttachInfo, 1);
3396 auto_ainfo = g_auto_pointer_new_with_free
3397 (ainfo, (GFreeFunc) compose_attach_info_free);
3398 ainfo->file = g_strdup(file);
3401 ainfo->content_type = g_strdup(content_type);
3402 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3404 MsgFlags flags = {0, 0};
3406 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3407 ainfo->encoding = ENC_7BIT;
3409 ainfo->encoding = ENC_8BIT;
3411 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3412 if (msginfo && msginfo->subject)
3413 name = g_strdup(msginfo->subject);
3415 name = g_path_get_basename(filename ? filename : file);
3417 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3419 procmsg_msginfo_free(msginfo);
3421 if (!g_ascii_strncasecmp(content_type, "text", 4))
3422 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3424 ainfo->encoding = ENC_BASE64;
3425 name = g_path_get_basename(filename ? filename : file);
3426 ainfo->name = g_strdup(name);
3430 ainfo->content_type = procmime_get_mime_type(file);
3431 if (!ainfo->content_type) {
3432 ainfo->content_type =
3433 g_strdup("application/octet-stream");
3434 ainfo->encoding = ENC_BASE64;
3435 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3437 procmime_get_encoding_for_text_file(file, &has_binary);
3439 ainfo->encoding = ENC_BASE64;
3440 name = g_path_get_basename(filename ? filename : file);
3441 ainfo->name = g_strdup(name);
3445 if (ainfo->name != NULL
3446 && !strcmp(ainfo->name, ".")) {
3447 g_free(ainfo->name);
3451 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3452 g_free(ainfo->content_type);
3453 ainfo->content_type = g_strdup("application/octet-stream");
3456 ainfo->size = (goffset)size;
3457 size_text = to_human_readable((goffset)size);
3459 store = GTK_LIST_STORE(gtk_tree_view_get_model
3460 (GTK_TREE_VIEW(compose->attach_clist)));
3462 gtk_list_store_append(store, &iter);
3463 gtk_list_store_set(store, &iter,
3464 COL_MIMETYPE, ainfo->content_type,
3465 COL_SIZE, size_text,
3466 COL_NAME, ainfo->name,
3468 COL_AUTODATA, auto_ainfo,
3471 g_auto_pointer_free(auto_ainfo);
3472 compose_attach_update_label(compose);
3476 static void compose_use_signing(Compose *compose, gboolean use_signing)
3478 compose->use_signing = use_signing;
3479 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3482 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3484 compose->use_encryption = use_encryption;
3485 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3488 #define NEXT_PART_NOT_CHILD(info) \
3490 node = info->node; \
3491 while (node->children) \
3492 node = g_node_last_child(node); \
3493 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3496 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3500 MimeInfo *firsttext = NULL;
3501 MimeInfo *encrypted = NULL;
3504 const gchar *partname = NULL;
3506 mimeinfo = procmime_scan_message(msginfo);
3507 if (!mimeinfo) return;
3509 if (mimeinfo->node->children == NULL) {
3510 procmime_mimeinfo_free_all(mimeinfo);
3514 /* find first content part */
3515 child = (MimeInfo *) mimeinfo->node->children->data;
3516 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3517 child = (MimeInfo *)child->node->children->data;
3519 if (child->type == MIMETYPE_TEXT) {
3521 debug_print("First text part found\n");
3522 } else if (compose->mode == COMPOSE_REEDIT &&
3523 child->type == MIMETYPE_APPLICATION &&
3524 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3525 encrypted = (MimeInfo *)child->node->parent->data;
3528 child = (MimeInfo *) mimeinfo->node->children->data;
3529 while (child != NULL) {
3532 if (child == encrypted) {
3533 /* skip this part of tree */
3534 NEXT_PART_NOT_CHILD(child);
3538 if (child->type == MIMETYPE_MULTIPART) {
3539 /* get the actual content */
3540 child = procmime_mimeinfo_next(child);
3544 if (child == firsttext) {
3545 child = procmime_mimeinfo_next(child);
3549 outfile = procmime_get_tmp_file_name(child);
3550 if ((err = procmime_get_part(outfile, child)) < 0)
3551 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3553 gchar *content_type;
3555 content_type = procmime_get_content_type_str(child->type, child->subtype);
3557 /* if we meet a pgp signature, we don't attach it, but
3558 * we force signing. */
3559 if ((strcmp(content_type, "application/pgp-signature") &&
3560 strcmp(content_type, "application/pkcs7-signature") &&
3561 strcmp(content_type, "application/x-pkcs7-signature"))
3562 || compose->mode == COMPOSE_REDIRECT) {
3563 partname = procmime_mimeinfo_get_parameter(child, "filename");
3564 if (partname == NULL)
3565 partname = procmime_mimeinfo_get_parameter(child, "name");
3566 if (partname == NULL)
3568 compose_attach_append(compose, outfile,
3569 partname, content_type);
3571 compose_force_signing(compose, compose->account);
3573 g_free(content_type);
3576 NEXT_PART_NOT_CHILD(child);
3578 procmime_mimeinfo_free_all(mimeinfo);
3581 #undef NEXT_PART_NOT_CHILD
3586 WAIT_FOR_INDENT_CHAR,
3587 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3590 /* return indent length, we allow:
3591 indent characters followed by indent characters or spaces/tabs,
3592 alphabets and numbers immediately followed by indent characters,
3593 and the repeating sequences of the above
3594 If quote ends with multiple spaces, only the first one is included. */
3595 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3596 const GtkTextIter *start, gint *len)
3598 GtkTextIter iter = *start;
3602 IndentState state = WAIT_FOR_INDENT_CHAR;
3605 gint alnum_count = 0;
3606 gint space_count = 0;
3609 if (prefs_common.quote_chars == NULL) {
3613 while (!gtk_text_iter_ends_line(&iter)) {
3614 wc = gtk_text_iter_get_char(&iter);
3615 if (g_unichar_iswide(wc))
3617 clen = g_unichar_to_utf8(wc, ch);
3621 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3622 is_space = g_unichar_isspace(wc);
3624 if (state == WAIT_FOR_INDENT_CHAR) {
3625 if (!is_indent && !g_unichar_isalnum(wc))
3628 quote_len += alnum_count + space_count + 1;
3629 alnum_count = space_count = 0;
3630 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3633 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3634 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3638 else if (is_indent) {
3639 quote_len += alnum_count + space_count + 1;
3640 alnum_count = space_count = 0;
3643 state = WAIT_FOR_INDENT_CHAR;
3647 gtk_text_iter_forward_char(&iter);
3650 if (quote_len > 0 && space_count > 0)
3656 if (quote_len > 0) {
3658 gtk_text_iter_forward_chars(&iter, quote_len);
3659 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3665 /* return >0 if the line is itemized */
3666 static int compose_itemized_length(GtkTextBuffer *buffer,
3667 const GtkTextIter *start)
3669 GtkTextIter iter = *start;
3674 if (gtk_text_iter_ends_line(&iter))
3679 wc = gtk_text_iter_get_char(&iter);
3680 if (!g_unichar_isspace(wc))
3682 gtk_text_iter_forward_char(&iter);
3683 if (gtk_text_iter_ends_line(&iter))
3687 clen = g_unichar_to_utf8(wc, ch);
3691 if (!strchr("*-+", ch[0]))
3694 gtk_text_iter_forward_char(&iter);
3695 if (gtk_text_iter_ends_line(&iter))
3697 wc = gtk_text_iter_get_char(&iter);
3698 if (g_unichar_isspace(wc)) {
3704 /* return the string at the start of the itemization */
3705 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3706 const GtkTextIter *start)
3708 GtkTextIter iter = *start;
3711 GString *item_chars = g_string_new("");
3714 if (gtk_text_iter_ends_line(&iter))
3719 wc = gtk_text_iter_get_char(&iter);
3720 if (!g_unichar_isspace(wc))
3722 gtk_text_iter_forward_char(&iter);
3723 if (gtk_text_iter_ends_line(&iter))
3725 g_string_append_unichar(item_chars, wc);
3728 str = item_chars->str;
3729 g_string_free(item_chars, FALSE);
3733 /* return the number of spaces at a line's start */
3734 static int compose_left_offset_length(GtkTextBuffer *buffer,
3735 const GtkTextIter *start)
3737 GtkTextIter iter = *start;
3740 if (gtk_text_iter_ends_line(&iter))
3744 wc = gtk_text_iter_get_char(&iter);
3745 if (!g_unichar_isspace(wc))
3748 gtk_text_iter_forward_char(&iter);
3749 if (gtk_text_iter_ends_line(&iter))
3753 gtk_text_iter_forward_char(&iter);
3754 if (gtk_text_iter_ends_line(&iter))
3759 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3760 const GtkTextIter *start,
3761 GtkTextIter *break_pos,
3765 GtkTextIter iter = *start, line_end = *start;
3766 PangoLogAttr *attrs;
3773 gboolean can_break = FALSE;
3774 gboolean do_break = FALSE;
3775 gboolean was_white = FALSE;
3776 gboolean prev_dont_break = FALSE;
3778 gtk_text_iter_forward_to_line_end(&line_end);
3779 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3780 len = g_utf8_strlen(str, -1);
3784 g_warning("compose_get_line_break_pos: len = 0!\n");
3788 /* g_print("breaking line: %d: %s (len = %d)\n",
3789 gtk_text_iter_get_line(&iter), str, len); */
3791 attrs = g_new(PangoLogAttr, len + 1);
3793 pango_default_break(str, -1, NULL, attrs, len + 1);
3797 /* skip quote and leading spaces */
3798 for (i = 0; *p != '\0' && i < len; i++) {
3801 wc = g_utf8_get_char(p);
3802 if (i >= quote_len && !g_unichar_isspace(wc))
3804 if (g_unichar_iswide(wc))
3806 else if (*p == '\t')
3810 p = g_utf8_next_char(p);
3813 for (; *p != '\0' && i < len; i++) {
3814 PangoLogAttr *attr = attrs + i;
3818 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3821 was_white = attr->is_white;
3823 /* don't wrap URI */
3824 if ((uri_len = get_uri_len(p)) > 0) {
3826 if (pos > 0 && col > max_col) {
3836 wc = g_utf8_get_char(p);
3837 if (g_unichar_iswide(wc)) {
3839 if (prev_dont_break && can_break && attr->is_line_break)
3841 } else if (*p == '\t')
3845 if (pos > 0 && col > max_col) {
3850 if (*p == '-' || *p == '/')
3851 prev_dont_break = TRUE;
3853 prev_dont_break = FALSE;
3855 p = g_utf8_next_char(p);
3859 debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3864 *break_pos = *start;
3865 gtk_text_iter_set_line_offset(break_pos, pos);
3870 static gboolean compose_join_next_line(Compose *compose,
3871 GtkTextBuffer *buffer,
3873 const gchar *quote_str)
3875 GtkTextIter iter_ = *iter, cur, prev, next, end;
3876 PangoLogAttr attrs[3];
3878 gchar *next_quote_str;
3881 gboolean keep_cursor = FALSE;
3883 if (!gtk_text_iter_forward_line(&iter_) ||
3884 gtk_text_iter_ends_line(&iter_)) {
3887 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
3889 if ((quote_str || next_quote_str) &&
3890 strcmp2(quote_str, next_quote_str) != 0) {
3891 g_free(next_quote_str);
3894 g_free(next_quote_str);
3897 if (quote_len > 0) {
3898 gtk_text_iter_forward_chars(&end, quote_len);
3899 if (gtk_text_iter_ends_line(&end)) {
3904 /* don't join itemized lines */
3905 if (compose_itemized_length(buffer, &end) > 0) {
3909 /* don't join signature separator */
3910 if (compose_is_sig_separator(compose, buffer, &iter_)) {
3913 /* delete quote str */
3915 gtk_text_buffer_delete(buffer, &iter_, &end);
3917 /* don't join line breaks put by the user */
3919 gtk_text_iter_backward_char(&cur);
3920 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
3921 gtk_text_iter_forward_char(&cur);
3925 gtk_text_iter_forward_char(&cur);
3926 /* delete linebreak and extra spaces */
3927 while (gtk_text_iter_backward_char(&cur)) {
3928 wc1 = gtk_text_iter_get_char(&cur);
3929 if (!g_unichar_isspace(wc1))
3934 while (!gtk_text_iter_ends_line(&cur)) {
3935 wc1 = gtk_text_iter_get_char(&cur);
3936 if (!g_unichar_isspace(wc1))
3938 gtk_text_iter_forward_char(&cur);
3941 if (!gtk_text_iter_equal(&prev, &next)) {
3944 mark = gtk_text_buffer_get_insert(buffer);
3945 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
3946 if (gtk_text_iter_equal(&prev, &cur))
3948 gtk_text_buffer_delete(buffer, &prev, &next);
3952 /* insert space if required */
3953 gtk_text_iter_backward_char(&prev);
3954 wc1 = gtk_text_iter_get_char(&prev);
3955 wc2 = gtk_text_iter_get_char(&next);
3956 gtk_text_iter_forward_char(&next);
3957 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
3958 pango_default_break(str, -1, NULL, attrs, 3);
3959 if (!attrs[1].is_line_break ||
3960 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
3961 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
3963 gtk_text_iter_backward_char(&iter_);
3964 gtk_text_buffer_place_cursor(buffer, &iter_);
3973 #define ADD_TXT_POS(bp_, ep_, pti_) \
3974 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
3975 last = last->next; \
3976 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
3977 last->next = NULL; \
3979 g_warning("alloc error scanning URIs\n"); \
3982 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
3984 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3985 GtkTextBuffer *buffer;
3986 GtkTextIter iter, break_pos, end_of_line;
3987 gchar *quote_str = NULL;
3989 gboolean wrap_quote = prefs_common.linewrap_quote;
3990 gboolean prev_autowrap = compose->autowrap;
3991 gint startq_offset = -1, noq_offset = -1;
3992 gint uri_start = -1, uri_stop = -1;
3993 gint nouri_start = -1, nouri_stop = -1;
3994 gint num_blocks = 0;
3995 gint quotelevel = -1;
3996 gboolean modified = force;
3997 gboolean removed = FALSE;
3998 gboolean modified_before_remove = FALSE;
4000 gboolean start = TRUE;
4001 gint itemized_len = 0, rem_item_len = 0;
4002 gchar *itemized_chars = NULL;
4003 gboolean item_continuation = FALSE;
4008 if (compose->draft_timeout_tag == -2) {
4012 compose->autowrap = FALSE;
4014 buffer = gtk_text_view_get_buffer(text);
4015 undo_wrapping(compose->undostruct, TRUE);
4020 mark = gtk_text_buffer_get_insert(buffer);
4021 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4025 if (compose->draft_timeout_tag == -2) {
4026 if (gtk_text_iter_ends_line(&iter)) {
4027 while (gtk_text_iter_ends_line(&iter) &&
4028 gtk_text_iter_forward_line(&iter))
4031 while (gtk_text_iter_backward_line(&iter)) {
4032 if (gtk_text_iter_ends_line(&iter)) {
4033 gtk_text_iter_forward_line(&iter);
4039 /* move to line start */
4040 gtk_text_iter_set_line_offset(&iter, 0);
4043 itemized_len = compose_itemized_length(buffer, &iter);
4045 if (!itemized_len) {
4046 itemized_len = compose_left_offset_length(buffer, &iter);
4047 item_continuation = TRUE;
4051 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4053 /* go until paragraph end (empty line) */
4054 while (start || !gtk_text_iter_ends_line(&iter)) {
4055 gchar *scanpos = NULL;
4056 /* parse table - in order of priority */
4058 const gchar *needle; /* token */
4060 /* token search function */
4061 gchar *(*search) (const gchar *haystack,
4062 const gchar *needle);
4063 /* part parsing function */
4064 gboolean (*parse) (const gchar *start,
4065 const gchar *scanpos,
4069 /* part to URI function */
4070 gchar *(*build_uri) (const gchar *bp,
4074 static struct table parser[] = {
4075 {"http://", strcasestr, get_uri_part, make_uri_string},
4076 {"https://", strcasestr, get_uri_part, make_uri_string},
4077 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4078 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4079 {"www.", strcasestr, get_uri_part, make_http_string},
4080 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4081 {"@", strcasestr, get_email_part, make_email_string}
4083 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4084 gint last_index = PARSE_ELEMS;
4086 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4090 if (!prev_autowrap && num_blocks == 0) {
4092 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4093 G_CALLBACK(text_inserted),
4096 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4099 uri_start = uri_stop = -1;
4101 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4104 debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4105 if (startq_offset == -1)
4106 startq_offset = gtk_text_iter_get_offset(&iter);
4107 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4108 if (quotelevel > 2) {
4109 /* recycle colors */
4110 if (prefs_common.recycle_quote_colors)
4119 if (startq_offset == -1)
4120 noq_offset = gtk_text_iter_get_offset(&iter);
4124 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4127 if (gtk_text_iter_ends_line(&iter)) {
4129 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4130 prefs_common.linewrap_len,
4132 GtkTextIter prev, next, cur;
4133 if (prev_autowrap != FALSE || force) {
4134 compose->automatic_break = TRUE;
4136 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4137 compose->automatic_break = FALSE;
4138 if (itemized_len && prefs_common.auto_indent) {
4139 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4140 if (!item_continuation)
4141 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4143 } else if (quote_str && wrap_quote) {
4144 compose->automatic_break = TRUE;
4146 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4147 compose->automatic_break = FALSE;
4148 if (itemized_len && prefs_common.auto_indent) {
4149 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4150 if (!item_continuation)
4151 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4155 /* remove trailing spaces */
4157 rem_item_len = itemized_len;
4158 while (prefs_common.auto_indent && rem_item_len-- > 0)
4159 gtk_text_iter_backward_char(&cur);
4160 gtk_text_iter_backward_char(&cur);
4163 while (!gtk_text_iter_starts_line(&cur)) {
4166 gtk_text_iter_backward_char(&cur);
4167 wc = gtk_text_iter_get_char(&cur);
4168 if (!g_unichar_isspace(wc))
4172 if (!gtk_text_iter_equal(&prev, &next)) {
4173 gtk_text_buffer_delete(buffer, &prev, &next);
4175 gtk_text_iter_forward_char(&break_pos);
4179 gtk_text_buffer_insert(buffer, &break_pos,
4183 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4185 /* move iter to current line start */
4186 gtk_text_iter_set_line_offset(&iter, 0);
4193 /* move iter to next line start */
4199 if (!prev_autowrap && num_blocks > 0) {
4201 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4202 G_CALLBACK(text_inserted),
4206 while (!gtk_text_iter_ends_line(&end_of_line)) {
4207 gtk_text_iter_forward_char(&end_of_line);
4209 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4211 nouri_start = gtk_text_iter_get_offset(&iter);
4212 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4214 walk_pos = gtk_text_iter_get_offset(&iter);
4215 /* FIXME: this looks phony. scanning for anything in the parse table */
4216 for (n = 0; n < PARSE_ELEMS; n++) {
4219 tmp = parser[n].search(walk, parser[n].needle);
4221 if (scanpos == NULL || tmp < scanpos) {
4230 /* check if URI can be parsed */
4231 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4232 (const gchar **)&ep, FALSE)
4233 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4237 strlen(parser[last_index].needle);
4240 uri_start = walk_pos + (bp - o_walk);
4241 uri_stop = walk_pos + (ep - o_walk);
4245 gtk_text_iter_forward_line(&iter);
4248 if (startq_offset != -1) {
4249 GtkTextIter startquote, endquote;
4250 gtk_text_buffer_get_iter_at_offset(
4251 buffer, &startquote, startq_offset);
4254 switch (quotelevel) {
4256 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4257 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4258 gtk_text_buffer_apply_tag_by_name(
4259 buffer, "quote0", &startquote, &endquote);
4260 gtk_text_buffer_remove_tag_by_name(
4261 buffer, "quote1", &startquote, &endquote);
4262 gtk_text_buffer_remove_tag_by_name(
4263 buffer, "quote2", &startquote, &endquote);
4268 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4269 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4270 gtk_text_buffer_apply_tag_by_name(
4271 buffer, "quote1", &startquote, &endquote);
4272 gtk_text_buffer_remove_tag_by_name(
4273 buffer, "quote0", &startquote, &endquote);
4274 gtk_text_buffer_remove_tag_by_name(
4275 buffer, "quote2", &startquote, &endquote);
4280 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4281 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4282 gtk_text_buffer_apply_tag_by_name(
4283 buffer, "quote2", &startquote, &endquote);
4284 gtk_text_buffer_remove_tag_by_name(
4285 buffer, "quote0", &startquote, &endquote);
4286 gtk_text_buffer_remove_tag_by_name(
4287 buffer, "quote1", &startquote, &endquote);
4293 } else if (noq_offset != -1) {
4294 GtkTextIter startnoquote, endnoquote;
4295 gtk_text_buffer_get_iter_at_offset(
4296 buffer, &startnoquote, noq_offset);
4299 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4300 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4301 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4302 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4303 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4304 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4305 gtk_text_buffer_remove_tag_by_name(
4306 buffer, "quote0", &startnoquote, &endnoquote);
4307 gtk_text_buffer_remove_tag_by_name(
4308 buffer, "quote1", &startnoquote, &endnoquote);
4309 gtk_text_buffer_remove_tag_by_name(
4310 buffer, "quote2", &startnoquote, &endnoquote);
4316 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4317 GtkTextIter nouri_start_iter, nouri_end_iter;
4318 gtk_text_buffer_get_iter_at_offset(
4319 buffer, &nouri_start_iter, nouri_start);
4320 gtk_text_buffer_get_iter_at_offset(
4321 buffer, &nouri_end_iter, nouri_stop);
4322 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4323 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4324 gtk_text_buffer_remove_tag_by_name(
4325 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4326 modified_before_remove = modified;
4331 if (uri_start >= 0 && uri_stop > 0) {
4332 GtkTextIter uri_start_iter, uri_end_iter, back;
4333 gtk_text_buffer_get_iter_at_offset(
4334 buffer, &uri_start_iter, uri_start);
4335 gtk_text_buffer_get_iter_at_offset(
4336 buffer, &uri_end_iter, uri_stop);
4337 back = uri_end_iter;
4338 gtk_text_iter_backward_char(&back);
4339 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4340 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4341 gtk_text_buffer_apply_tag_by_name(
4342 buffer, "link", &uri_start_iter, &uri_end_iter);
4344 if (removed && !modified_before_remove) {
4350 debug_print("not modified, out after %d lines\n", lines);
4354 debug_print("modified, out after %d lines\n", lines);
4356 g_free(itemized_chars);
4359 undo_wrapping(compose->undostruct, FALSE);
4360 compose->autowrap = prev_autowrap;
4365 void compose_action_cb(void *data)
4367 Compose *compose = (Compose *)data;
4368 compose_wrap_all(compose);
4371 static void compose_wrap_all(Compose *compose)
4373 compose_wrap_all_full(compose, FALSE);
4376 static void compose_wrap_all_full(Compose *compose, gboolean force)
4378 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4379 GtkTextBuffer *buffer;
4381 gboolean modified = TRUE;
4383 buffer = gtk_text_view_get_buffer(text);
4385 gtk_text_buffer_get_start_iter(buffer, &iter);
4386 while (!gtk_text_iter_is_end(&iter) && modified)
4387 modified = compose_beautify_paragraph(compose, &iter, force);
4391 static void compose_set_title(Compose *compose)
4397 edited = compose->modified ? _(" [Edited]") : "";
4399 subject = gtk_editable_get_chars(
4400 GTK_EDITABLE(compose->subject_entry), 0, -1);
4402 #ifndef GENERIC_UMPC
4403 if (subject && strlen(subject))
4404 str = g_strdup_printf(_("%s - Compose message%s"),
4407 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4409 str = g_strdup(_("Compose message"));
4412 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4418 * compose_current_mail_account:
4420 * Find a current mail account (the currently selected account, or the
4421 * default account, if a news account is currently selected). If a
4422 * mail account cannot be found, display an error message.
4424 * Return value: Mail account, or NULL if not found.
4426 static PrefsAccount *
4427 compose_current_mail_account(void)
4431 if (cur_account && cur_account->protocol != A_NNTP)
4434 ac = account_get_default();
4435 if (!ac || ac->protocol == A_NNTP) {
4436 alertpanel_error(_("Account for sending mail is not specified.\n"
4437 "Please select a mail account before sending."));
4444 #define QUOTE_IF_REQUIRED(out, str) \
4446 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4450 len = strlen(str) + 3; \
4451 if ((__tmp = alloca(len)) == NULL) { \
4452 g_warning("can't allocate memory\n"); \
4453 g_string_free(header, TRUE); \
4456 g_snprintf(__tmp, len, "\"%s\"", str); \
4461 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4462 g_warning("can't allocate memory\n"); \
4463 g_string_free(header, TRUE); \
4466 strcpy(__tmp, str); \
4472 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4474 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4478 len = strlen(str) + 3; \
4479 if ((__tmp = alloca(len)) == NULL) { \
4480 g_warning("can't allocate memory\n"); \
4483 g_snprintf(__tmp, len, "\"%s\"", str); \
4488 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4489 g_warning("can't allocate memory\n"); \
4492 strcpy(__tmp, str); \
4498 static void compose_select_account(Compose *compose, PrefsAccount *account,
4503 g_return_if_fail(account != NULL);
4505 compose->account = account;
4507 if (account->name && *account->name) {
4509 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4510 from = g_strdup_printf("%s <%s>",
4511 buf, account->address);
4512 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4514 from = g_strdup_printf("<%s>",
4516 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4521 compose_set_title(compose);
4523 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4524 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4526 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4527 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4528 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4530 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4532 activate_privacy_system(compose, account, FALSE);
4534 if (!init && compose->mode != COMPOSE_REDIRECT) {
4535 undo_block(compose->undostruct);
4536 compose_insert_sig(compose, TRUE);
4537 undo_unblock(compose->undostruct);
4541 /* use account's dict info if set */
4542 if (compose->gtkaspell) {
4543 if (account->enable_default_dictionary)
4544 gtkaspell_change_dict(compose->gtkaspell,
4545 account->default_dictionary, FALSE);
4546 if (account->enable_default_alt_dictionary)
4547 gtkaspell_change_alt_dict(compose->gtkaspell,
4548 account->default_alt_dictionary);
4549 if (account->enable_default_dictionary
4550 || account->enable_default_alt_dictionary)
4551 compose_spell_menu_changed(compose);
4556 gboolean compose_check_for_valid_recipient(Compose *compose) {
4557 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4558 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4559 gboolean recipient_found = FALSE;
4563 /* free to and newsgroup list */
4564 slist_free_strings(compose->to_list);
4565 g_slist_free(compose->to_list);
4566 compose->to_list = NULL;
4568 slist_free_strings(compose->newsgroup_list);
4569 g_slist_free(compose->newsgroup_list);
4570 compose->newsgroup_list = NULL;
4572 /* search header entries for to and newsgroup entries */
4573 for (list = compose->header_list; list; list = list->next) {
4576 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4577 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4580 if (entry[0] != '\0') {
4581 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4582 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4583 compose->to_list = address_list_append(compose->to_list, entry);
4584 recipient_found = TRUE;
4587 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4588 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4589 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4590 recipient_found = TRUE;
4597 return recipient_found;
4600 static gboolean compose_check_for_set_recipients(Compose *compose)
4602 if (compose->account->set_autocc && compose->account->auto_cc) {
4603 gboolean found_other = FALSE;
4605 /* search header entries for to and newsgroup entries */
4606 for (list = compose->header_list; list; list = list->next) {
4609 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4610 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4613 if (strcmp(entry, compose->account->auto_cc)
4614 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4624 if (compose->batch) {
4625 gtk_widget_show_all(compose->window);
4627 aval = alertpanel(_("Send"),
4628 _("The only recipient is the default CC address. Send anyway?"),
4629 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4630 if (aval != G_ALERTALTERNATE)
4634 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4635 gboolean found_other = FALSE;
4637 /* search header entries for to and newsgroup entries */
4638 for (list = compose->header_list; list; list = list->next) {
4641 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4642 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4645 if (strcmp(entry, compose->account->auto_bcc)
4646 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4656 if (compose->batch) {
4657 gtk_widget_show_all(compose->window);
4659 aval = alertpanel(_("Send"),
4660 _("The only recipient is the default BCC address. Send anyway?"),
4661 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4662 if (aval != G_ALERTALTERNATE)
4669 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4673 if (compose_check_for_valid_recipient(compose) == FALSE) {
4674 if (compose->batch) {
4675 gtk_widget_show_all(compose->window);
4677 alertpanel_error(_("Recipient is not specified."));
4681 if (compose_check_for_set_recipients(compose) == FALSE) {
4685 if (!compose->batch) {
4686 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4687 if (*str == '\0' && check_everything == TRUE &&
4688 compose->mode != COMPOSE_REDIRECT) {
4690 gchar *button_label;
4693 if (compose->sending)
4694 button_label = _("+_Send");
4696 button_label = _("+_Queue");
4697 message = g_strdup_printf(_("Subject is empty. %s"),
4698 compose->sending?_("Send it anyway?"):
4699 _("Queue it anyway?"));
4701 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4702 GTK_STOCK_CANCEL, button_label, NULL);
4704 if (aval != G_ALERTALTERNATE)
4709 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4715 gint compose_send(Compose *compose)
4718 FolderItem *folder = NULL;
4720 gchar *msgpath = NULL;
4721 gboolean discard_window = FALSE;
4722 gchar *errstr = NULL;
4723 gchar *tmsgid = NULL;
4724 MainWindow *mainwin = mainwindow_get_mainwindow();
4725 gboolean queued_removed = FALSE;
4727 if (prefs_common.send_dialog_invisible
4728 || compose->batch == TRUE)
4729 discard_window = TRUE;
4731 compose_allow_user_actions (compose, FALSE);
4732 compose->sending = TRUE;
4734 if (compose_check_entries(compose, TRUE) == FALSE) {
4735 if (compose->batch) {
4736 gtk_widget_show_all(compose->window);
4742 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4745 if (compose->batch) {
4746 gtk_widget_show_all(compose->window);
4749 alertpanel_error(_("Could not queue message for sending:\n\n"
4750 "Charset conversion failed."));
4751 } else if (val == -5) {
4752 alertpanel_error(_("Could not queue message for sending:\n\n"
4753 "Couldn't get recipient encryption key."));
4754 } else if (val == -6) {
4756 } else if (val == -3) {
4757 if (privacy_peek_error())
4758 alertpanel_error(_("Could not queue message for sending:\n\n"
4759 "Signature failed: %s"), privacy_get_error());
4760 } else if (val == -2 && errno != 0) {
4761 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4763 alertpanel_error(_("Could not queue message for sending."));
4768 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4769 if (discard_window) {
4770 compose->sending = FALSE;
4771 compose_close(compose);
4772 /* No more compose access in the normal codepath
4773 * after this point! */
4778 alertpanel_error(_("The message was queued but could not be "
4779 "sent.\nUse \"Send queued messages\" from "
4780 "the main window to retry."));
4781 if (!discard_window) {
4788 if (msgpath == NULL) {
4789 msgpath = folder_item_fetch_msg(folder, msgnum);
4790 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4793 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4794 claws_unlink(msgpath);
4797 if (!discard_window) {
4799 if (!queued_removed)
4800 folder_item_remove_msg(folder, msgnum);
4801 folder_item_scan(folder);
4803 /* make sure we delete that */
4804 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4806 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4807 folder_item_remove_msg(folder, tmp->msgnum);
4808 procmsg_msginfo_free(tmp);
4815 if (!queued_removed)
4816 folder_item_remove_msg(folder, msgnum);
4817 folder_item_scan(folder);
4819 /* make sure we delete that */
4820 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4822 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4823 folder_item_remove_msg(folder, tmp->msgnum);
4824 procmsg_msginfo_free(tmp);
4827 if (!discard_window) {
4828 compose->sending = FALSE;
4829 compose_allow_user_actions (compose, TRUE);
4830 compose_close(compose);
4834 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4835 "the main window to retry."), errstr);
4838 alertpanel_error_log(_("The message was queued but could not be "
4839 "sent.\nUse \"Send queued messages\" from "
4840 "the main window to retry."));
4842 if (!discard_window) {
4851 toolbar_main_set_sensitive(mainwin);
4852 main_window_set_menu_sensitive(mainwin);
4858 compose_allow_user_actions (compose, TRUE);
4859 compose->sending = FALSE;
4860 compose->modified = TRUE;
4861 toolbar_main_set_sensitive(mainwin);
4862 main_window_set_menu_sensitive(mainwin);
4867 static gboolean compose_use_attach(Compose *compose)
4869 GtkTreeModel *model = gtk_tree_view_get_model
4870 (GTK_TREE_VIEW(compose->attach_clist));
4871 return gtk_tree_model_iter_n_children(model, NULL) > 0;
4874 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
4877 gchar buf[BUFFSIZE];
4879 gboolean first_to_address;
4880 gboolean first_cc_address;
4882 ComposeHeaderEntry *headerentry;
4883 const gchar *headerentryname;
4884 const gchar *cc_hdr;
4885 const gchar *to_hdr;
4886 gboolean err = FALSE;
4888 debug_print("Writing redirect header\n");
4890 cc_hdr = prefs_common_translated_header_name("Cc:");
4891 to_hdr = prefs_common_translated_header_name("To:");
4893 first_to_address = TRUE;
4894 for (list = compose->header_list; list; list = list->next) {
4895 headerentry = ((ComposeHeaderEntry *)list->data);
4896 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4898 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
4899 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4900 Xstrdup_a(str, entstr, return -1);
4902 if (str[0] != '\0') {
4903 compose_convert_header
4904 (compose, buf, sizeof(buf), str,
4905 strlen("Resent-To") + 2, TRUE);
4907 if (first_to_address) {
4908 err |= (fprintf(fp, "Resent-To: ") < 0);
4909 first_to_address = FALSE;
4911 err |= (fprintf(fp, ",") < 0);
4913 err |= (fprintf(fp, "%s", buf) < 0);
4917 if (!first_to_address) {
4918 err |= (fprintf(fp, "\n") < 0);
4921 first_cc_address = TRUE;
4922 for (list = compose->header_list; list; list = list->next) {
4923 headerentry = ((ComposeHeaderEntry *)list->data);
4924 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4926 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
4927 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4928 Xstrdup_a(str, strg, return -1);
4930 if (str[0] != '\0') {
4931 compose_convert_header
4932 (compose, buf, sizeof(buf), str,
4933 strlen("Resent-Cc") + 2, TRUE);
4935 if (first_cc_address) {
4936 err |= (fprintf(fp, "Resent-Cc: ") < 0);
4937 first_cc_address = FALSE;
4939 err |= (fprintf(fp, ",") < 0);
4941 err |= (fprintf(fp, "%s", buf) < 0);
4945 if (!first_cc_address) {
4946 err |= (fprintf(fp, "\n") < 0);
4949 return (err ? -1:0);
4952 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
4954 gchar buf[BUFFSIZE];
4956 const gchar *entstr;
4957 /* struct utsname utsbuf; */
4958 gboolean err = FALSE;
4960 g_return_val_if_fail(fp != NULL, -1);
4961 g_return_val_if_fail(compose->account != NULL, -1);
4962 g_return_val_if_fail(compose->account->address != NULL, -1);
4965 get_rfc822_date(buf, sizeof(buf));
4966 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
4969 if (compose->account->name && *compose->account->name) {
4970 compose_convert_header
4971 (compose, buf, sizeof(buf), compose->account->name,
4972 strlen("From: "), TRUE);
4973 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
4974 buf, compose->account->address) < 0);
4976 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
4979 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4980 if (*entstr != '\0') {
4981 Xstrdup_a(str, entstr, return -1);
4984 compose_convert_header(compose, buf, sizeof(buf), str,
4985 strlen("Subject: "), FALSE);
4986 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
4990 /* Resent-Message-ID */
4991 if (compose->account->set_domain && compose->account->domain) {
4992 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
4993 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
4994 g_snprintf(buf, sizeof(buf), "%s",
4995 strchr(compose->account->address, '@') ?
4996 strchr(compose->account->address, '@')+1 :
4997 compose->account->address);
4999 g_snprintf(buf, sizeof(buf), "%s", "");
5002 if (compose->account->gen_msgid) {
5003 generate_msgid(buf, sizeof(buf));
5004 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5005 compose->msgid = g_strdup(buf);
5007 compose->msgid = NULL;
5010 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5013 /* separator between header and body */
5014 err |= (fputs("\n", fp) == EOF);
5016 return (err ? -1:0);
5019 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5023 gchar buf[BUFFSIZE];
5025 gboolean skip = FALSE;
5026 gboolean err = FALSE;
5027 gchar *not_included[]={
5028 "Return-Path:", "Delivered-To:", "Received:",
5029 "Subject:", "X-UIDL:", "AF:",
5030 "NF:", "PS:", "SRH:",
5031 "SFN:", "DSR:", "MID:",
5032 "CFG:", "PT:", "S:",
5033 "RQ:", "SSV:", "NSV:",
5034 "SSH:", "R:", "MAID:",
5035 "NAID:", "RMID:", "FMID:",
5036 "SCF:", "RRCPT:", "NG:",
5037 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5038 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5039 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5040 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5041 "X-Claws-Auto-Wrapping:",
5044 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5045 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5049 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5051 for (i = 0; not_included[i] != NULL; i++) {
5052 if (g_ascii_strncasecmp(buf, not_included[i],
5053 strlen(not_included[i])) == 0) {
5060 if (fputs(buf, fdest) == -1)
5063 if (!prefs_common.redirect_keep_from) {
5064 if (g_ascii_strncasecmp(buf, "From:",
5065 strlen("From:")) == 0) {
5066 err |= (fputs(" (by way of ", fdest) == EOF);
5067 if (compose->account->name
5068 && *compose->account->name) {
5069 compose_convert_header
5070 (compose, buf, sizeof(buf),
5071 compose->account->name,
5074 err |= (fprintf(fdest, "%s <%s>",
5076 compose->account->address) < 0);
5078 err |= (fprintf(fdest, "%s",
5079 compose->account->address) < 0);
5080 err |= (fputs(")", fdest) == EOF);
5084 if (fputs("\n", fdest) == -1)
5091 if (compose_redirect_write_headers(compose, fdest))
5094 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5095 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5108 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5110 GtkTextBuffer *buffer;
5111 GtkTextIter start, end;
5114 const gchar *out_codeset;
5115 EncodingType encoding = ENC_UNKNOWN;
5116 MimeInfo *mimemsg, *mimetext;
5118 const gchar *src_codeset = CS_INTERNAL;
5120 if (action == COMPOSE_WRITE_FOR_SEND)
5121 attach_parts = TRUE;
5123 /* create message MimeInfo */
5124 mimemsg = procmime_mimeinfo_new();
5125 mimemsg->type = MIMETYPE_MESSAGE;
5126 mimemsg->subtype = g_strdup("rfc822");
5127 mimemsg->content = MIMECONTENT_MEM;
5128 mimemsg->tmp = TRUE; /* must free content later */
5129 mimemsg->data.mem = compose_get_header(compose);
5131 /* Create text part MimeInfo */
5132 /* get all composed text */
5133 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5134 gtk_text_buffer_get_start_iter(buffer, &start);
5135 gtk_text_buffer_get_end_iter(buffer, &end);
5136 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5138 out_codeset = conv_get_charset_str(compose->out_encoding);
5140 if (!out_codeset && is_ascii_str(chars)) {
5141 out_codeset = CS_US_ASCII;
5142 } else if (prefs_common.outgoing_fallback_to_ascii &&
5143 is_ascii_str(chars)) {
5144 out_codeset = CS_US_ASCII;
5145 encoding = ENC_7BIT;
5149 gchar *test_conv_global_out = NULL;
5150 gchar *test_conv_reply = NULL;
5152 /* automatic mode. be automatic. */
5153 codeconv_set_strict(TRUE);
5155 out_codeset = conv_get_outgoing_charset_str();
5157 debug_print("trying to convert to %s\n", out_codeset);
5158 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5161 if (!test_conv_global_out && compose->orig_charset
5162 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5163 out_codeset = compose->orig_charset;
5164 debug_print("failure; trying to convert to %s\n", out_codeset);
5165 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5168 if (!test_conv_global_out && !test_conv_reply) {
5170 out_codeset = CS_INTERNAL;
5171 debug_print("failure; finally using %s\n", out_codeset);
5173 g_free(test_conv_global_out);
5174 g_free(test_conv_reply);
5175 codeconv_set_strict(FALSE);
5178 if (encoding == ENC_UNKNOWN) {
5179 if (prefs_common.encoding_method == CTE_BASE64)
5180 encoding = ENC_BASE64;
5181 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5182 encoding = ENC_QUOTED_PRINTABLE;
5183 else if (prefs_common.encoding_method == CTE_8BIT)
5184 encoding = ENC_8BIT;
5186 encoding = procmime_get_encoding_for_charset(out_codeset);
5189 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5190 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5192 if (action == COMPOSE_WRITE_FOR_SEND) {
5193 codeconv_set_strict(TRUE);
5194 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5195 codeconv_set_strict(FALSE);
5201 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5202 "to the specified %s charset.\n"
5203 "Send it as %s?"), out_codeset, src_codeset);
5204 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5205 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5208 if (aval != G_ALERTALTERNATE) {
5213 out_codeset = src_codeset;
5219 out_codeset = src_codeset;
5224 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5225 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5226 strstr(buf, "\nFrom ") != NULL) {
5227 encoding = ENC_QUOTED_PRINTABLE;
5231 mimetext = procmime_mimeinfo_new();
5232 mimetext->content = MIMECONTENT_MEM;
5233 mimetext->tmp = TRUE; /* must free content later */
5234 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5235 * and free the data, which we need later. */
5236 mimetext->data.mem = g_strdup(buf);
5237 mimetext->type = MIMETYPE_TEXT;
5238 mimetext->subtype = g_strdup("plain");
5239 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5240 g_strdup(out_codeset));
5242 /* protect trailing spaces when signing message */
5243 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5244 privacy_system_can_sign(compose->privacy_system)) {
5245 encoding = ENC_QUOTED_PRINTABLE;
5248 debug_print("main text: %zd bytes encoded as %s in %d\n",
5249 strlen(buf), out_codeset, encoding);
5251 /* check for line length limit */
5252 if (action == COMPOSE_WRITE_FOR_SEND &&
5253 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5254 check_line_length(buf, 1000, &line) < 0) {
5258 msg = g_strdup_printf
5259 (_("Line %d exceeds the line length limit (998 bytes).\n"
5260 "The contents of the message might be broken on the way to the delivery.\n"
5262 "Send it anyway?"), line + 1);
5263 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5265 if (aval != G_ALERTALTERNATE) {
5271 if (encoding != ENC_UNKNOWN)
5272 procmime_encode_content(mimetext, encoding);
5274 /* append attachment parts */
5275 if (compose_use_attach(compose) && attach_parts) {
5276 MimeInfo *mimempart;
5277 gchar *boundary = NULL;
5278 mimempart = procmime_mimeinfo_new();
5279 mimempart->content = MIMECONTENT_EMPTY;
5280 mimempart->type = MIMETYPE_MULTIPART;
5281 mimempart->subtype = g_strdup("mixed");
5285 boundary = generate_mime_boundary(NULL);
5286 } while (strstr(buf, boundary) != NULL);
5288 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5291 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5293 g_node_append(mimempart->node, mimetext->node);
5294 g_node_append(mimemsg->node, mimempart->node);
5296 compose_add_attachments(compose, mimempart);
5298 g_node_append(mimemsg->node, mimetext->node);
5302 /* sign message if sending */
5303 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5304 privacy_system_can_sign(compose->privacy_system))
5305 if (!privacy_sign(compose->privacy_system, mimemsg, compose->account))
5308 procmime_write_mimeinfo(mimemsg, fp);
5310 procmime_mimeinfo_free_all(mimemsg);
5315 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5317 GtkTextBuffer *buffer;
5318 GtkTextIter start, end;
5323 if ((fp = g_fopen(file, "wb")) == NULL) {
5324 FILE_OP_ERROR(file, "fopen");
5328 /* chmod for security */
5329 if (change_file_mode_rw(fp, file) < 0) {
5330 FILE_OP_ERROR(file, "chmod");
5331 g_warning("can't change file mode\n");
5334 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5335 gtk_text_buffer_get_start_iter(buffer, &start);
5336 gtk_text_buffer_get_end_iter(buffer, &end);
5337 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5339 chars = conv_codeset_strdup
5340 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5343 if (!chars) return -1;
5346 len = strlen(chars);
5347 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5348 FILE_OP_ERROR(file, "fwrite");
5357 if (fclose(fp) == EOF) {
5358 FILE_OP_ERROR(file, "fclose");
5365 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5368 MsgInfo *msginfo = compose->targetinfo;
5370 g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5371 if (!msginfo) return -1;
5373 if (!force && MSG_IS_LOCKED(msginfo->flags))
5376 item = msginfo->folder;
5377 g_return_val_if_fail(item != NULL, -1);
5379 if (procmsg_msg_exist(msginfo) &&
5380 (folder_has_parent_of_type(item, F_QUEUE) ||
5381 folder_has_parent_of_type(item, F_DRAFT)
5382 || msginfo == compose->autosaved_draft)) {
5383 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5384 g_warning("can't remove the old message\n");
5387 debug_print("removed reedit target %d\n", msginfo->msgnum);
5394 static void compose_remove_draft(Compose *compose)
5397 MsgInfo *msginfo = compose->targetinfo;
5398 drafts = account_get_special_folder(compose->account, F_DRAFT);
5400 if (procmsg_msg_exist(msginfo)) {
5401 folder_item_remove_msg(drafts, msginfo->msgnum);
5406 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5407 gboolean remove_reedit_target)
5409 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5412 static gboolean compose_warn_encryption(Compose *compose)
5414 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5415 AlertValue val = G_ALERTALTERNATE;
5417 if (warning == NULL)
5420 val = alertpanel_full(_("Encryption warning"), warning,
5421 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5422 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5423 if (val & G_ALERTDISABLE) {
5424 val &= ~G_ALERTDISABLE;
5425 if (val == G_ALERTALTERNATE)
5426 privacy_inhibit_encrypt_warning(compose->privacy_system,
5430 if (val == G_ALERTALTERNATE) {
5437 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5438 gchar **msgpath, gboolean check_subject,
5439 gboolean remove_reedit_target)
5446 static gboolean lock = FALSE;
5447 PrefsAccount *mailac = NULL, *newsac = NULL;
5448 gboolean err = FALSE;
5450 debug_print("queueing message...\n");
5451 g_return_val_if_fail(compose->account != NULL, -1);
5455 if (compose_check_entries(compose, check_subject) == FALSE) {
5457 if (compose->batch) {
5458 gtk_widget_show_all(compose->window);
5463 if (!compose->to_list && !compose->newsgroup_list) {
5464 g_warning("can't get recipient list.");
5469 if (compose->to_list) {
5470 if (compose->account->protocol != A_NNTP)
5471 mailac = compose->account;
5472 else if (cur_account && cur_account->protocol != A_NNTP)
5473 mailac = cur_account;
5474 else if (!(mailac = compose_current_mail_account())) {
5476 alertpanel_error(_("No account for sending mails available!"));
5481 if (compose->newsgroup_list) {
5482 if (compose->account->protocol == A_NNTP)
5483 newsac = compose->account;
5484 else if (!newsac->protocol != A_NNTP) {
5486 alertpanel_error(_("No account for posting news available!"));
5491 /* write queue header */
5492 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5493 G_DIR_SEPARATOR, compose, (guint) rand());
5494 debug_print("queuing to %s\n", tmp);
5495 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5496 FILE_OP_ERROR(tmp, "fopen");
5502 if (change_file_mode_rw(fp, tmp) < 0) {
5503 FILE_OP_ERROR(tmp, "chmod");
5504 g_warning("can't change file mode\n");
5507 /* queueing variables */
5508 err |= (fprintf(fp, "AF:\n") < 0);
5509 err |= (fprintf(fp, "NF:0\n") < 0);
5510 err |= (fprintf(fp, "PS:10\n") < 0);
5511 err |= (fprintf(fp, "SRH:1\n") < 0);
5512 err |= (fprintf(fp, "SFN:\n") < 0);
5513 err |= (fprintf(fp, "DSR:\n") < 0);
5515 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5517 err |= (fprintf(fp, "MID:\n") < 0);
5518 err |= (fprintf(fp, "CFG:\n") < 0);
5519 err |= (fprintf(fp, "PT:0\n") < 0);
5520 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5521 err |= (fprintf(fp, "RQ:\n") < 0);
5523 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5525 err |= (fprintf(fp, "SSV:\n") < 0);
5527 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5529 err |= (fprintf(fp, "NSV:\n") < 0);
5530 err |= (fprintf(fp, "SSH:\n") < 0);
5531 /* write recepient list */
5532 if (compose->to_list) {
5533 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5534 for (cur = compose->to_list->next; cur != NULL;
5536 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5537 err |= (fprintf(fp, "\n") < 0);
5539 /* write newsgroup list */
5540 if (compose->newsgroup_list) {
5541 err |= (fprintf(fp, "NG:") < 0);
5542 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5543 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5544 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5545 err |= (fprintf(fp, "\n") < 0);
5547 /* Sylpheed account IDs */
5549 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5551 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5554 if (compose->privacy_system != NULL) {
5555 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5556 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5557 if (compose->use_encryption) {
5559 if (!compose_warn_encryption(compose)) {
5566 if (mailac && mailac->encrypt_to_self) {
5567 GSList *tmp_list = g_slist_copy(compose->to_list);
5568 tmp_list = g_slist_append(tmp_list, compose->account->address);
5569 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5570 g_slist_free(tmp_list);
5572 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5574 if (encdata != NULL) {
5575 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5576 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5577 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5579 } /* else we finally dont want to encrypt */
5581 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5582 /* and if encdata was null, it means there's been a problem in
5594 /* Save copy folder */
5595 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5596 gchar *savefolderid;
5598 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
5599 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5600 g_free(savefolderid);
5602 /* Save copy folder */
5603 if (compose->return_receipt) {
5604 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5606 /* Message-ID of message replying to */
5607 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5610 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5611 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5614 /* Message-ID of message forwarding to */
5615 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5618 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5619 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5623 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5625 /* end of headers */
5626 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5628 if (compose->redirect_filename != NULL) {
5629 if (compose_redirect_write_to_file(compose, fp) < 0) {
5638 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5643 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5647 g_warning("failed to write queue message\n");
5654 if (fclose(fp) == EOF) {
5655 FILE_OP_ERROR(tmp, "fclose");
5662 if (item && *item) {
5665 queue = account_get_special_folder(compose->account, F_QUEUE);
5668 g_warning("can't find queue folder\n");
5674 folder_item_scan(queue);
5675 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5676 g_warning("can't queue the message\n");
5683 if (msgpath == NULL) {
5689 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5690 compose_remove_reedit_target(compose, FALSE);
5693 if ((msgnum != NULL) && (item != NULL)) {
5701 static void compose_add_attachments(Compose *compose, MimeInfo *parent)
5704 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5706 struct stat statbuf;
5707 gchar *type, *subtype;
5708 GtkTreeModel *model;
5711 model = gtk_tree_view_get_model(tree_view);
5713 if (!gtk_tree_model_get_iter_first(model, &iter))
5716 gtk_tree_model_get(model, &iter,
5720 mimepart = procmime_mimeinfo_new();
5721 mimepart->content = MIMECONTENT_FILE;
5722 mimepart->data.filename = g_strdup(ainfo->file);
5723 mimepart->tmp = FALSE; /* or we destroy our attachment */
5724 mimepart->offset = 0;
5726 stat(ainfo->file, &statbuf);
5727 mimepart->length = statbuf.st_size;
5729 type = g_strdup(ainfo->content_type);
5731 if (!strchr(type, '/')) {
5733 type = g_strdup("application/octet-stream");
5736 subtype = strchr(type, '/') + 1;
5737 *(subtype - 1) = '\0';
5738 mimepart->type = procmime_get_media_type(type);
5739 mimepart->subtype = g_strdup(subtype);
5742 if (mimepart->type == MIMETYPE_MESSAGE &&
5743 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5744 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5747 g_hash_table_insert(mimepart->typeparameters,
5748 g_strdup("name"), g_strdup(ainfo->name));
5749 g_hash_table_insert(mimepart->dispositionparameters,
5750 g_strdup("filename"), g_strdup(ainfo->name));
5751 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5755 if (compose->use_signing) {
5756 if (ainfo->encoding == ENC_7BIT)
5757 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5758 else if (ainfo->encoding == ENC_8BIT)
5759 ainfo->encoding = ENC_BASE64;
5762 procmime_encode_content(mimepart, ainfo->encoding);
5764 g_node_append(parent->node, mimepart->node);
5765 } while (gtk_tree_model_iter_next(model, &iter));
5768 #define IS_IN_CUSTOM_HEADER(header) \
5769 (compose->account->add_customhdr && \
5770 custom_header_find(compose->account->customhdr_list, header) != NULL)
5772 static void compose_add_headerfield_from_headerlist(Compose *compose,
5774 const gchar *fieldname,
5775 const gchar *seperator)
5777 gchar *str, *fieldname_w_colon;
5778 gboolean add_field = FALSE;
5780 ComposeHeaderEntry *headerentry;
5781 const gchar *headerentryname;
5782 const gchar *trans_fieldname;
5785 if (IS_IN_CUSTOM_HEADER(fieldname))
5788 debug_print("Adding %s-fields\n", fieldname);
5790 fieldstr = g_string_sized_new(64);
5792 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5793 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5795 for (list = compose->header_list; list; list = list->next) {
5796 headerentry = ((ComposeHeaderEntry *)list->data);
5797 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5799 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5800 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5802 if (str[0] != '\0') {
5804 g_string_append(fieldstr, seperator);
5805 g_string_append(fieldstr, str);
5814 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5815 compose_convert_header
5816 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
5817 strlen(fieldname) + 2, TRUE);
5818 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5822 g_free(fieldname_w_colon);
5823 g_string_free(fieldstr, TRUE);
5828 static gchar *compose_get_header(Compose *compose)
5830 gchar buf[BUFFSIZE];
5831 const gchar *entry_str;
5835 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5837 gchar *from_name = NULL, *from_address = NULL;
5840 g_return_val_if_fail(compose->account != NULL, NULL);
5841 g_return_val_if_fail(compose->account->address != NULL, NULL);
5843 header = g_string_sized_new(64);
5846 get_rfc822_date(buf, sizeof(buf));
5847 g_string_append_printf(header, "Date: %s\n", buf);
5851 if (compose->account->name && *compose->account->name) {
5853 QUOTE_IF_REQUIRED(buf, compose->account->name);
5854 tmp = g_strdup_printf("%s <%s>",
5855 buf, compose->account->address);
5857 tmp = g_strdup_printf("%s",
5858 compose->account->address);
5860 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5861 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5863 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5864 from_address = g_strdup(compose->account->address);
5866 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5867 /* extract name and address */
5868 if (strstr(spec, " <") && strstr(spec, ">")) {
5869 from_address = g_strdup(strrchr(spec, '<')+1);
5870 *(strrchr(from_address, '>')) = '\0';
5871 from_name = g_strdup(spec);
5872 *(strrchr(from_name, '<')) = '\0';
5875 from_address = g_strdup(spec);
5882 if (from_name && *from_name) {
5883 compose_convert_header
5884 (compose, buf, sizeof(buf), from_name,
5885 strlen("From: "), TRUE);
5886 QUOTE_IF_REQUIRED(name, buf);
5888 g_string_append_printf(header, "From: %s <%s>\n",
5889 name, from_address);
5891 g_string_append_printf(header, "From: %s\n", from_address);
5894 g_free(from_address);
5897 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
5900 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
5903 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
5906 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
5909 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
5911 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
5914 compose_convert_header(compose, buf, sizeof(buf), str,
5915 strlen("Subject: "), FALSE);
5916 g_string_append_printf(header, "Subject: %s\n", buf);
5922 if (compose->account->set_domain && compose->account->domain) {
5923 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5924 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5925 g_snprintf(buf, sizeof(buf), "%s",
5926 strchr(compose->account->address, '@') ?
5927 strchr(compose->account->address, '@')+1 :
5928 compose->account->address);
5930 g_snprintf(buf, sizeof(buf), "%s", "");
5933 if (compose->account->gen_msgid) {
5934 generate_msgid(buf, sizeof(buf));
5935 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
5936 compose->msgid = g_strdup(buf);
5938 compose->msgid = NULL;
5941 if (compose->remove_references == FALSE) {
5943 if (compose->inreplyto && compose->to_list)
5944 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
5947 if (compose->references)
5948 g_string_append_printf(header, "References: %s\n", compose->references);
5952 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
5955 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
5958 if (compose->account->organization &&
5959 strlen(compose->account->organization) &&
5960 !IS_IN_CUSTOM_HEADER("Organization")) {
5961 compose_convert_header(compose, buf, sizeof(buf),
5962 compose->account->organization,
5963 strlen("Organization: "), FALSE);
5964 g_string_append_printf(header, "Organization: %s\n", buf);
5967 /* Program version and system info */
5968 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
5969 !compose->newsgroup_list) {
5970 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
5972 gtk_major_version, gtk_minor_version, gtk_micro_version,
5975 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
5976 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
5978 gtk_major_version, gtk_minor_version, gtk_micro_version,
5982 /* custom headers */
5983 if (compose->account->add_customhdr) {
5986 for (cur = compose->account->customhdr_list; cur != NULL;
5988 CustomHeader *chdr = (CustomHeader *)cur->data;
5990 if (custom_header_is_allowed(chdr->name)) {
5991 compose_convert_header
5992 (compose, buf, sizeof(buf),
5993 chdr->value ? chdr->value : "",
5994 strlen(chdr->name) + 2, FALSE);
5995 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6000 /* Automatic Faces and X-Faces */
6001 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6002 g_string_append_printf(header, "X-Face: %s\n", buf);
6004 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6005 g_string_append_printf(header, "X-Face: %s\n", buf);
6007 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6008 g_string_append_printf(header, "Face: %s\n", buf);
6010 else if (get_default_face (buf, sizeof(buf)) == 0) {
6011 g_string_append_printf(header, "Face: %s\n", buf);
6015 switch (compose->priority) {
6016 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6017 "X-Priority: 1 (Highest)\n");
6019 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6020 "X-Priority: 2 (High)\n");
6022 case PRIORITY_NORMAL: break;
6023 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6024 "X-Priority: 4 (Low)\n");
6026 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6027 "X-Priority: 5 (Lowest)\n");
6029 default: debug_print("compose: priority unknown : %d\n",
6033 /* Request Return Receipt */
6034 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6035 if (compose->return_receipt) {
6036 if (compose->account->name
6037 && *compose->account->name) {
6038 compose_convert_header(compose, buf, sizeof(buf),
6039 compose->account->name,
6040 strlen("Disposition-Notification-To: "),
6042 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6044 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6048 /* get special headers */
6049 for (list = compose->header_list; list; list = list->next) {
6050 ComposeHeaderEntry *headerentry;
6053 gchar *headername_wcolon;
6054 const gchar *headername_trans;
6057 gboolean standard_header = FALSE;
6059 headerentry = ((ComposeHeaderEntry *)list->data);
6061 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6063 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6068 if (!strstr(tmp, ":")) {
6069 headername_wcolon = g_strconcat(tmp, ":", NULL);
6070 headername = g_strdup(tmp);
6072 headername_wcolon = g_strdup(tmp);
6073 headername = g_strdup(strtok(tmp, ":"));
6077 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6078 Xstrdup_a(headervalue, entry_str, return NULL);
6079 subst_char(headervalue, '\r', ' ');
6080 subst_char(headervalue, '\n', ' ');
6081 string = std_headers;
6082 while (*string != NULL) {
6083 headername_trans = prefs_common_translated_header_name(*string);
6084 if (!strcmp(headername_trans, headername_wcolon))
6085 standard_header = TRUE;
6088 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6089 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6092 g_free(headername_wcolon);
6096 g_string_free(header, FALSE);
6101 #undef IS_IN_CUSTOM_HEADER
6103 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6104 gint header_len, gboolean addr_field)
6106 gchar *tmpstr = NULL;
6107 const gchar *out_codeset = NULL;
6109 g_return_if_fail(src != NULL);
6110 g_return_if_fail(dest != NULL);
6112 if (len < 1) return;
6114 tmpstr = g_strdup(src);
6116 subst_char(tmpstr, '\n', ' ');
6117 subst_char(tmpstr, '\r', ' ');
6120 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6121 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6122 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6127 codeconv_set_strict(TRUE);
6128 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6129 conv_get_charset_str(compose->out_encoding));
6130 codeconv_set_strict(FALSE);
6132 if (!dest || *dest == '\0') {
6133 gchar *test_conv_global_out = NULL;
6134 gchar *test_conv_reply = NULL;
6136 /* automatic mode. be automatic. */
6137 codeconv_set_strict(TRUE);
6139 out_codeset = conv_get_outgoing_charset_str();
6141 debug_print("trying to convert to %s\n", out_codeset);
6142 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6145 if (!test_conv_global_out && compose->orig_charset
6146 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6147 out_codeset = compose->orig_charset;
6148 debug_print("failure; trying to convert to %s\n", out_codeset);
6149 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6152 if (!test_conv_global_out && !test_conv_reply) {
6154 out_codeset = CS_INTERNAL;
6155 debug_print("finally using %s\n", out_codeset);
6157 g_free(test_conv_global_out);
6158 g_free(test_conv_reply);
6159 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6161 codeconv_set_strict(FALSE);
6166 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6170 g_return_if_fail(user_data != NULL);
6172 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6173 g_strstrip(address);
6174 if (*address != '\0') {
6175 gchar *name = procheader_get_fromname(address);
6176 extract_address(address);
6177 addressbook_add_contact(name, address, NULL, NULL);
6182 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6184 GtkWidget *menuitem;
6187 g_return_if_fail(menu != NULL);
6188 g_return_if_fail(GTK_IS_MENU_SHELL(menu));
6190 menuitem = gtk_separator_menu_item_new();
6191 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6192 gtk_widget_show(menuitem);
6194 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6195 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6197 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6198 g_strstrip(address);
6199 if (*address == '\0') {
6200 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6203 g_signal_connect(G_OBJECT(menuitem), "activate",
6204 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6205 gtk_widget_show(menuitem);
6208 static void compose_create_header_entry(Compose *compose)
6210 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6215 const gchar *header = NULL;
6216 ComposeHeaderEntry *headerentry;
6217 gboolean standard_header = FALSE;
6218 #if !(GTK_CHECK_VERSION(2,12,0))
6219 GtkTooltips *tips = compose->tooltips;
6222 headerentry = g_new0(ComposeHeaderEntry, 1);
6225 combo = gtk_combo_box_entry_new_text();
6227 while(*string != NULL) {
6228 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6229 (gchar*)prefs_common_translated_header_name(*string));
6232 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6233 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6234 G_CALLBACK(compose_grab_focus_cb), compose);
6235 gtk_widget_show(combo);
6236 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6237 compose->header_nextrow, compose->header_nextrow+1,
6238 GTK_SHRINK, GTK_FILL, 0, 0);
6239 if (compose->header_last) {
6240 const gchar *last_header_entry = gtk_entry_get_text(
6241 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6243 while (*string != NULL) {
6244 if (!strcmp(*string, last_header_entry))
6245 standard_header = TRUE;
6248 if (standard_header)
6249 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6251 if (!compose->header_last || !standard_header) {
6252 switch(compose->account->protocol) {
6254 header = prefs_common_translated_header_name("Newsgroups:");
6257 header = prefs_common_translated_header_name("To:");
6262 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6264 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6265 G_CALLBACK(compose_grab_focus_cb), compose);
6268 entry = gtk_entry_new();
6269 gtk_widget_show(entry);
6270 CLAWS_SET_TIP(entry,
6271 _("Use <tab> to autocomplete from addressbook"));
6272 gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2,
6273 compose->header_nextrow, compose->header_nextrow+1,
6274 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6276 g_signal_connect(G_OBJECT(entry), "key-press-event",
6277 G_CALLBACK(compose_headerentry_key_press_event_cb),
6279 g_signal_connect(G_OBJECT(entry), "changed",
6280 G_CALLBACK(compose_headerentry_changed_cb),
6282 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6283 G_CALLBACK(compose_grab_focus_cb), compose);
6286 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6287 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6288 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6289 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6290 G_CALLBACK(compose_header_drag_received_cb),
6292 g_signal_connect(G_OBJECT(entry), "drag-drop",
6293 G_CALLBACK(compose_drag_drop),
6295 g_signal_connect(G_OBJECT(entry), "populate-popup",
6296 G_CALLBACK(compose_entry_popup_extend),
6299 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6301 headerentry->compose = compose;
6302 headerentry->combo = combo;
6303 headerentry->entry = entry;
6304 headerentry->headernum = compose->header_nextrow;
6306 compose->header_nextrow++;
6307 compose->header_last = headerentry;
6308 compose->header_list =
6309 g_slist_append(compose->header_list,
6313 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text)
6315 ComposeHeaderEntry *last_header;
6317 last_header = compose->header_last;
6319 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((last_header->combo)))), header);
6320 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6323 static void compose_remove_header_entries(Compose *compose)
6326 for (list = compose->header_list; list; list = list->next) {
6327 ComposeHeaderEntry *headerentry =
6328 (ComposeHeaderEntry *)list->data;
6329 gtk_widget_destroy(headerentry->combo);
6330 gtk_widget_destroy(headerentry->entry);
6331 g_free(headerentry);
6333 compose->header_last = NULL;
6334 g_slist_free(compose->header_list);
6335 compose->header_list = NULL;
6336 compose->header_nextrow = 1;
6337 compose_create_header_entry(compose);
6340 static GtkWidget *compose_create_header(Compose *compose)
6342 GtkWidget *from_optmenu_hbox;
6343 GtkWidget *header_scrolledwin;
6344 GtkWidget *header_table;
6348 /* header labels and entries */
6349 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6350 gtk_widget_show(header_scrolledwin);
6351 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6353 header_table = gtk_table_new(2, 2, FALSE);
6354 gtk_widget_show(header_table);
6355 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6356 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6357 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6360 /* option menu for selecting accounts */
6361 from_optmenu_hbox = compose_account_option_menu_create(compose);
6362 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6363 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6366 compose->header_table = header_table;
6367 compose->header_list = NULL;
6368 compose->header_nextrow = count;
6370 compose_create_header_entry(compose);
6372 compose->table = NULL;
6374 return header_scrolledwin ;
6377 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6379 Compose *compose = (Compose *)data;
6380 GdkEventButton event;
6383 event.time = gtk_get_current_event_time();
6385 return attach_button_pressed(compose->attach_clist, &event, compose);
6388 static GtkWidget *compose_create_attach(Compose *compose)
6390 GtkWidget *attach_scrwin;
6391 GtkWidget *attach_clist;
6393 GtkListStore *store;
6394 GtkCellRenderer *renderer;
6395 GtkTreeViewColumn *column;
6396 GtkTreeSelection *selection;
6398 /* attachment list */
6399 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6400 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6401 GTK_POLICY_AUTOMATIC,
6402 GTK_POLICY_AUTOMATIC);
6403 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6405 store = gtk_list_store_new(N_ATTACH_COLS,
6410 G_TYPE_AUTO_POINTER,
6412 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6413 (GTK_TREE_MODEL(store)));
6414 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6415 g_object_unref(store);
6417 renderer = gtk_cell_renderer_text_new();
6418 column = gtk_tree_view_column_new_with_attributes
6419 (_("Mime type"), renderer, "text",
6420 COL_MIMETYPE, NULL);
6421 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6423 renderer = gtk_cell_renderer_text_new();
6424 column = gtk_tree_view_column_new_with_attributes
6425 (_("Size"), renderer, "text",
6427 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6429 renderer = gtk_cell_renderer_text_new();
6430 column = gtk_tree_view_column_new_with_attributes
6431 (_("Name"), renderer, "text",
6433 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6435 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6436 prefs_common.use_stripes_everywhere);
6437 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6438 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6440 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6441 G_CALLBACK(attach_selected), compose);
6442 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6443 G_CALLBACK(attach_button_pressed), compose);
6445 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6446 G_CALLBACK(popup_attach_button_pressed), compose);
6448 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6449 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6450 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6451 G_CALLBACK(popup_attach_button_pressed), compose);
6453 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6454 G_CALLBACK(attach_key_pressed), compose);
6457 gtk_drag_dest_set(attach_clist,
6458 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6459 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6460 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6461 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6462 G_CALLBACK(compose_attach_drag_received_cb),
6464 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6465 G_CALLBACK(compose_drag_drop),
6468 compose->attach_scrwin = attach_scrwin;
6469 compose->attach_clist = attach_clist;
6471 return attach_scrwin;
6474 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6475 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6477 static GtkWidget *compose_create_others(Compose *compose)
6480 GtkWidget *savemsg_checkbtn;
6481 GtkWidget *savemsg_entry;
6482 GtkWidget *savemsg_select;
6485 gchar *folderidentifier;
6487 /* Table for settings */
6488 table = gtk_table_new(3, 1, FALSE);
6489 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6490 gtk_widget_show(table);
6491 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6494 /* Save Message to folder */
6495 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6496 gtk_widget_show(savemsg_checkbtn);
6497 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6498 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6499 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6501 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6502 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6504 savemsg_entry = gtk_entry_new();
6505 gtk_widget_show(savemsg_entry);
6506 gtk_table_attach_defaults(GTK_TABLE(table), savemsg_entry, 1, 2, rowcount, rowcount + 1);
6507 gtk_editable_set_editable(GTK_EDITABLE(savemsg_entry), prefs_common.savemsg);
6508 g_signal_connect_after(G_OBJECT(savemsg_entry), "grab_focus",
6509 G_CALLBACK(compose_grab_focus_cb), compose);
6510 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6511 folderidentifier = folder_item_get_identifier(account_get_special_folder
6512 (compose->account, F_OUTBOX));
6513 gtk_entry_set_text(GTK_ENTRY(savemsg_entry), folderidentifier);
6514 g_free(folderidentifier);
6517 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6518 gtk_widget_show(savemsg_select);
6519 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6520 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6521 G_CALLBACK(compose_savemsg_select_cb),
6526 compose->savemsg_checkbtn = savemsg_checkbtn;
6527 compose->savemsg_entry = savemsg_entry;
6532 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6534 gtk_editable_set_editable(GTK_EDITABLE(compose->savemsg_entry),
6535 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6538 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6543 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6546 path = folder_item_get_identifier(dest);
6548 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), path);
6552 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6553 GdkAtom clip, GtkTextIter *insert_place);
6556 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6560 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6562 if (event->button == 3) {
6564 GtkTextIter sel_start, sel_end;
6565 gboolean stuff_selected;
6567 /* move the cursor to allow GtkAspell to check the word
6568 * under the mouse */
6569 if (event->x && event->y) {
6570 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6571 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6573 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6576 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6577 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6580 stuff_selected = gtk_text_buffer_get_selection_bounds(
6582 &sel_start, &sel_end);
6584 gtk_text_buffer_place_cursor (buffer, &iter);
6585 /* reselect stuff */
6587 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6588 gtk_text_buffer_select_range(buffer,
6589 &sel_start, &sel_end);
6591 return FALSE; /* pass the event so that the right-click goes through */
6594 if (event->button == 2) {
6599 /* get the middle-click position to paste at the correct place */
6600 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6601 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6603 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6606 entry_paste_clipboard(compose, text,
6607 prefs_common.linewrap_pastes,
6608 GDK_SELECTION_PRIMARY, &iter);
6616 static void compose_spell_menu_changed(void *data)
6618 Compose *compose = (Compose *)data;
6620 GtkWidget *menuitem;
6621 GtkWidget *parent_item;
6622 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6625 if (compose->gtkaspell == NULL)
6628 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6629 "/Menu/Spelling/Options");
6631 /* setting the submenu removes /Spelling/Options from the factory
6632 * so we need to save it */
6634 if (parent_item == NULL) {
6635 parent_item = compose->aspell_options_menu;
6636 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6638 compose->aspell_options_menu = parent_item;
6640 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6642 spell_menu = g_slist_reverse(spell_menu);
6643 for (items = spell_menu;
6644 items; items = items->next) {
6645 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6646 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6647 gtk_widget_show(GTK_WIDGET(menuitem));
6649 g_slist_free(spell_menu);
6651 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6656 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6658 Compose *compose = (Compose *)data;
6659 GdkEventButton event;
6662 event.time = gtk_get_current_event_time();
6666 return text_clicked(compose->text, &event, compose);
6669 static gboolean compose_force_window_origin = TRUE;
6670 static Compose *compose_create(PrefsAccount *account,
6679 GtkWidget *handlebox;
6681 GtkWidget *notebook;
6683 GtkWidget *attach_hbox;
6684 GtkWidget *attach_lab1;
6685 GtkWidget *attach_lab2;
6690 GtkWidget *subject_hbox;
6691 GtkWidget *subject_frame;
6692 GtkWidget *subject_entry;
6696 GtkWidget *edit_vbox;
6697 GtkWidget *ruler_hbox;
6699 GtkWidget *scrolledwin;
6701 GtkTextBuffer *buffer;
6702 GtkClipboard *clipboard;
6705 UndoMain *undostruct;
6707 gchar *titles[N_ATTACH_COLS];
6708 GtkWidget *popupmenu;
6709 GtkWidget *tmpl_menu;
6710 GtkActionGroup *action_group = NULL;
6713 GtkAspell * gtkaspell = NULL;
6716 static GdkGeometry geometry;
6718 g_return_val_if_fail(account != NULL, NULL);
6720 debug_print("Creating compose window...\n");
6721 compose = g_new0(Compose, 1);
6723 titles[COL_MIMETYPE] = _("MIME type");
6724 titles[COL_SIZE] = _("Size");
6725 titles[COL_NAME] = _("Name");
6727 compose->batch = batch;
6728 compose->account = account;
6729 compose->folder = folder;
6731 compose->mutex = g_mutex_new();
6732 compose->set_cursor_pos = -1;
6734 #if !(GTK_CHECK_VERSION(2,12,0))
6735 compose->tooltips = tips;
6738 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6740 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6741 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6743 if (!geometry.max_width) {
6744 geometry.max_width = gdk_screen_width();
6745 geometry.max_height = gdk_screen_height();
6748 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6749 &geometry, GDK_HINT_MAX_SIZE);
6750 if (!geometry.min_width) {
6751 geometry.min_width = 600;
6752 geometry.min_height = 480;
6754 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6755 &geometry, GDK_HINT_MIN_SIZE);
6757 #ifndef GENERIC_UMPC
6758 if (compose_force_window_origin)
6759 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
6760 prefs_common.compose_y);
6762 g_signal_connect(G_OBJECT(window), "delete_event",
6763 G_CALLBACK(compose_delete_cb), compose);
6764 MANAGE_WINDOW_SIGNALS_CONNECT(window);
6765 gtk_widget_realize(window);
6767 gtkut_widget_set_composer_icon(window);
6769 vbox = gtk_vbox_new(FALSE, 0);
6770 gtk_container_add(GTK_CONTAINER(window), vbox);
6772 compose->ui_manager = gtk_ui_manager_new();
6773 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
6774 G_N_ELEMENTS(compose_entries), (gpointer)compose);
6775 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
6776 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
6777 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
6778 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
6779 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
6780 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
6781 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
6782 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
6785 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
6787 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENU)
6790 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
6791 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
6793 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
6795 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
6796 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
6797 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
6800 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
6801 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
6802 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6803 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
6804 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
6805 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
6806 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6807 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
6808 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6809 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
6812 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
6813 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
6814 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6816 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
6817 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
6818 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
6820 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
6821 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
6822 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
6823 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
6825 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
6827 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
6828 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
6829 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
6830 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
6831 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
6832 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
6833 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
6834 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
6835 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
6836 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
6837 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
6838 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
6839 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
6840 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
6841 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
6843 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6845 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
6846 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
6847 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
6848 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
6850 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6852 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
6856 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
6857 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
6858 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
6859 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
6860 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
6861 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
6865 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
6866 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
6867 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
6868 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
6869 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
6871 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6872 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
6873 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6874 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
6875 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
6878 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6879 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
6880 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
6881 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
6882 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
6883 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
6884 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
6886 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6887 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
6888 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6889 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
6890 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6892 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
6894 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
6895 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6896 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
6897 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
6898 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6900 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
6901 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)
6902 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)
6903 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
6905 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
6907 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
6908 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)
6909 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)
6911 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
6913 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
6914 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)
6915 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
6917 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
6918 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)
6919 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
6921 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
6923 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
6924 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)
6925 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
6926 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
6927 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
6929 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
6930 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)
6931 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)
6932 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
6933 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
6935 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
6936 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
6937 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
6938 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
6939 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
6941 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
6942 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
6943 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)
6945 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
6946 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
6947 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
6951 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
6952 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
6953 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
6954 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6955 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
6956 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6959 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
6961 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
6962 gtk_widget_show_all(menubar);
6964 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
6966 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
6968 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
6971 if (prefs_common.toolbar_detachable) {
6972 handlebox = gtk_handle_box_new();
6974 handlebox = gtk_hbox_new(FALSE, 0);
6976 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
6978 gtk_widget_realize(handlebox);
6980 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
6983 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
6987 vbox2 = gtk_vbox_new(FALSE, 2);
6988 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
6989 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
6992 notebook = gtk_notebook_new();
6993 gtk_widget_set_size_request(notebook, -1, 130);
6994 gtk_widget_show(notebook);
6996 /* header labels and entries */
6997 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6998 compose_create_header(compose),
6999 gtk_label_new_with_mnemonic(_("Hea_der")));
7000 /* attachment list */
7001 attach_hbox = gtk_hbox_new(FALSE, 0);
7002 gtk_widget_show(attach_hbox);
7004 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7005 gtk_widget_show(attach_lab1);
7006 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7008 attach_lab2 = gtk_label_new("");
7009 gtk_widget_show(attach_lab2);
7010 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7012 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7013 compose_create_attach(compose),
7016 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7017 compose_create_others(compose),
7018 gtk_label_new_with_mnemonic(_("Othe_rs")));
7021 subject_hbox = gtk_hbox_new(FALSE, 0);
7022 gtk_widget_show(subject_hbox);
7024 subject_frame = gtk_frame_new(NULL);
7025 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7026 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7027 gtk_widget_show(subject_frame);
7029 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7030 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7031 gtk_widget_show(subject);
7033 label = gtk_label_new(_("Subject:"));
7034 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7035 gtk_widget_show(label);
7037 subject_entry = gtk_entry_new();
7038 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7039 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7040 G_CALLBACK(compose_grab_focus_cb), compose);
7041 gtk_widget_show(subject_entry);
7042 compose->subject_entry = subject_entry;
7043 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7045 edit_vbox = gtk_vbox_new(FALSE, 0);
7047 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7050 ruler_hbox = gtk_hbox_new(FALSE, 0);
7051 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7053 ruler = gtk_shruler_new();
7054 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7055 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7059 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7060 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7061 GTK_POLICY_AUTOMATIC,
7062 GTK_POLICY_AUTOMATIC);
7063 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7065 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7066 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7068 text = gtk_text_view_new();
7069 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7070 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7071 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7072 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7073 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7075 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7077 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7078 G_CALLBACK(compose_edit_size_alloc),
7080 g_signal_connect(G_OBJECT(buffer), "changed",
7081 G_CALLBACK(compose_changed_cb), compose);
7082 g_signal_connect(G_OBJECT(text), "grab_focus",
7083 G_CALLBACK(compose_grab_focus_cb), compose);
7084 g_signal_connect(G_OBJECT(buffer), "insert_text",
7085 G_CALLBACK(text_inserted), compose);
7086 g_signal_connect(G_OBJECT(text), "button_press_event",
7087 G_CALLBACK(text_clicked), compose);
7089 g_signal_connect(G_OBJECT(text), "popup-menu",
7090 G_CALLBACK(compose_popup_menu), compose);
7092 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7093 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7094 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7095 G_CALLBACK(compose_popup_menu), compose);
7097 g_signal_connect(G_OBJECT(subject_entry), "changed",
7098 G_CALLBACK(compose_changed_cb), compose);
7101 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7102 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7103 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7104 g_signal_connect(G_OBJECT(text), "drag_data_received",
7105 G_CALLBACK(compose_insert_drag_received_cb),
7107 g_signal_connect(G_OBJECT(text), "drag-drop",
7108 G_CALLBACK(compose_drag_drop),
7110 gtk_widget_show_all(vbox);
7112 /* pane between attach clist and text */
7113 paned = gtk_vpaned_new();
7114 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7116 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7117 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7119 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7121 gtk_paned_add1(GTK_PANED(paned), notebook);
7122 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7123 gtk_widget_show_all(paned);
7126 if (prefs_common.textfont) {
7127 PangoFontDescription *font_desc;
7129 font_desc = pango_font_description_from_string
7130 (prefs_common.textfont);
7132 gtk_widget_modify_font(text, font_desc);
7133 pango_font_description_free(font_desc);
7137 gtk_action_group_add_actions(action_group, compose_popup_entries,
7138 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7139 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7140 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7141 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7142 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7143 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7144 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7146 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7148 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7149 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7150 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7152 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7154 undostruct = undo_init(text);
7155 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7158 address_completion_start(window);
7160 compose->window = window;
7161 compose->vbox = vbox;
7162 compose->menubar = menubar;
7163 compose->handlebox = handlebox;
7165 compose->vbox2 = vbox2;
7167 compose->paned = paned;
7169 compose->attach_label = attach_lab2;
7171 compose->notebook = notebook;
7172 compose->edit_vbox = edit_vbox;
7173 compose->ruler_hbox = ruler_hbox;
7174 compose->ruler = ruler;
7175 compose->scrolledwin = scrolledwin;
7176 compose->text = text;
7178 compose->focused_editable = NULL;
7180 compose->popupmenu = popupmenu;
7182 compose->tmpl_menu = tmpl_menu;
7184 compose->mode = mode;
7185 compose->rmode = mode;
7187 compose->targetinfo = NULL;
7188 compose->replyinfo = NULL;
7189 compose->fwdinfo = NULL;
7191 compose->replyto = NULL;
7193 compose->bcc = NULL;
7194 compose->followup_to = NULL;
7196 compose->ml_post = NULL;
7198 compose->inreplyto = NULL;
7199 compose->references = NULL;
7200 compose->msgid = NULL;
7201 compose->boundary = NULL;
7203 compose->autowrap = prefs_common.autowrap;
7205 compose->use_signing = FALSE;
7206 compose->use_encryption = FALSE;
7207 compose->privacy_system = NULL;
7209 compose->modified = FALSE;
7211 compose->return_receipt = FALSE;
7213 compose->to_list = NULL;
7214 compose->newsgroup_list = NULL;
7216 compose->undostruct = undostruct;
7218 compose->sig_str = NULL;
7220 compose->exteditor_file = NULL;
7221 compose->exteditor_pid = -1;
7222 compose->exteditor_tag = -1;
7223 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7226 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7227 if (mode != COMPOSE_REDIRECT) {
7228 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7229 strcmp(prefs_common.dictionary, "")) {
7230 gtkaspell = gtkaspell_new(prefs_common.aspell_path,
7231 prefs_common.dictionary,
7232 prefs_common.alt_dictionary,
7233 conv_get_locale_charset_str(),
7234 prefs_common.misspelled_col,
7235 prefs_common.check_while_typing,
7236 prefs_common.recheck_when_changing_dict,
7237 prefs_common.use_alternate,
7238 prefs_common.use_both_dicts,
7239 GTK_TEXT_VIEW(text),
7240 GTK_WINDOW(compose->window),
7241 compose_spell_menu_changed,
7244 alertpanel_error(_("Spell checker could not "
7246 gtkaspell_checkers_strerror());
7247 gtkaspell_checkers_reset_error();
7249 if (!gtkaspell_set_sug_mode(gtkaspell,
7250 prefs_common.aspell_sugmode)) {
7251 debug_print("Aspell: could not set "
7252 "suggestion mode %s\n",
7253 gtkaspell_checkers_strerror());
7254 gtkaspell_checkers_reset_error();
7257 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7261 compose->gtkaspell = gtkaspell;
7262 compose_spell_menu_changed(compose);
7265 compose_select_account(compose, account, TRUE);
7267 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7269 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7270 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7272 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7273 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7275 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7276 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7278 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7279 if (account->protocol != A_NNTP)
7280 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7281 prefs_common_translated_header_name("To:"));
7283 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7284 prefs_common_translated_header_name("Newsgroups:"));
7286 addressbook_set_target_compose(compose);
7288 if (mode != COMPOSE_REDIRECT)
7289 compose_set_template_menu(compose);
7291 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7294 compose_list = g_list_append(compose_list, compose);
7296 if (!prefs_common.show_ruler)
7297 gtk_widget_hide(ruler_hbox);
7299 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7302 compose->priority = PRIORITY_NORMAL;
7303 compose_update_priority_menu_item(compose);
7305 compose_set_out_encoding(compose);
7308 compose_update_actions_menu(compose);
7310 /* Privacy Systems menu */
7311 compose_update_privacy_systems_menu(compose);
7313 activate_privacy_system(compose, account, TRUE);
7314 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7316 gtk_widget_realize(window);
7318 gtk_widget_show(window);
7320 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7321 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7328 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7333 GtkWidget *optmenubox;
7336 GtkWidget *from_name = NULL;
7337 #if !(GTK_CHECK_VERSION(2,12,0))
7338 GtkTooltips *tips = compose->tooltips;
7341 gint num = 0, def_menu = 0;
7343 accounts = account_get_list();
7344 g_return_val_if_fail(accounts != NULL, NULL);
7346 optmenubox = gtk_event_box_new();
7347 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7348 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7350 hbox = gtk_hbox_new(FALSE, 6);
7351 from_name = gtk_entry_new();
7353 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7354 G_CALLBACK(compose_grab_focus_cb), compose);
7356 for (; accounts != NULL; accounts = accounts->next, num++) {
7357 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7358 gchar *name, *from = NULL;
7360 if (ac == compose->account) def_menu = num;
7362 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7365 if (ac == compose->account) {
7366 if (ac->name && *ac->name) {
7368 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7369 from = g_strdup_printf("%s <%s>",
7371 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7373 from = g_strdup_printf("%s",
7375 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7378 COMBOBOX_ADD(menu, name, ac->account_id);
7383 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7385 g_signal_connect(G_OBJECT(optmenu), "changed",
7386 G_CALLBACK(account_activated),
7388 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7389 G_CALLBACK(compose_entry_popup_extend),
7392 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7393 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7395 CLAWS_SET_TIP(optmenubox,
7396 _("Account to use for this email"));
7397 CLAWS_SET_TIP(from_name,
7398 _("Sender address to be used"));
7400 compose->from_name = from_name;
7405 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7407 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7408 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7409 Compose *compose = (Compose *) data;
7411 compose->priority = value;
7415 static void compose_reply_change_mode(Compose *compose,
7418 gboolean was_modified = compose->modified;
7420 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7422 g_return_if_fail(compose->replyinfo != NULL);
7424 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7426 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7428 if (action == COMPOSE_REPLY_TO_ALL)
7430 if (action == COMPOSE_REPLY_TO_SENDER)
7432 if (action == COMPOSE_REPLY_TO_LIST)
7435 compose_remove_header_entries(compose);
7436 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7437 if (compose->account->set_autocc && compose->account->auto_cc)
7438 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7440 if (compose->account->set_autobcc && compose->account->auto_bcc)
7441 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7443 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7444 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7445 compose_show_first_last_header(compose, TRUE);
7446 compose->modified = was_modified;
7447 compose_set_title(compose);
7450 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7452 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7453 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7454 Compose *compose = (Compose *) data;
7457 compose_reply_change_mode(compose, value);
7460 static void compose_update_priority_menu_item(Compose * compose)
7462 GtkWidget *menuitem = NULL;
7463 switch (compose->priority) {
7464 case PRIORITY_HIGHEST:
7465 menuitem = gtk_ui_manager_get_widget
7466 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7469 menuitem = gtk_ui_manager_get_widget
7470 (compose->ui_manager, "/Menu/Options/Priority/High");
7472 case PRIORITY_NORMAL:
7473 menuitem = gtk_ui_manager_get_widget
7474 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7477 menuitem = gtk_ui_manager_get_widget
7478 (compose->ui_manager, "/Menu/Options/Priority/Low");
7480 case PRIORITY_LOWEST:
7481 menuitem = gtk_ui_manager_get_widget
7482 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7485 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7488 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7490 Compose *compose = (Compose *) data;
7492 gboolean can_sign = FALSE, can_encrypt = FALSE;
7494 g_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7496 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7499 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7500 g_free(compose->privacy_system);
7501 compose->privacy_system = NULL;
7502 if (systemid != NULL) {
7503 compose->privacy_system = g_strdup(systemid);
7505 can_sign = privacy_system_can_sign(systemid);
7506 can_encrypt = privacy_system_can_encrypt(systemid);
7509 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7511 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7512 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7515 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7517 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7518 GtkWidget *menuitem = NULL;
7520 gboolean can_sign = FALSE, can_encrypt = FALSE;
7521 gboolean found = FALSE;
7523 if (compose->privacy_system != NULL) {
7525 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7526 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7527 g_return_if_fail(menuitem != NULL);
7529 amenu = GTK_MENU_SHELL(menuitem)->children;
7531 while (amenu != NULL) {
7532 GList *alist = amenu->next;
7534 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7535 if (systemid != NULL) {
7536 if (strcmp(systemid, compose->privacy_system) == 0 &&
7537 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7538 menuitem = GTK_WIDGET(amenu->data);
7540 can_sign = privacy_system_can_sign(systemid);
7541 can_encrypt = privacy_system_can_encrypt(systemid);
7545 } else if (strlen(compose->privacy_system) == 0 &&
7546 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7547 menuitem = GTK_WIDGET(amenu->data);
7550 can_encrypt = FALSE;
7557 if (menuitem != NULL)
7558 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7560 if (warn && !found && strlen(compose->privacy_system)) {
7561 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7562 "will not be able to sign or encrypt this message."),
7563 compose->privacy_system);
7567 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7568 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7571 static void compose_set_out_encoding(Compose *compose)
7573 CharSet out_encoding;
7574 const gchar *branch = NULL;
7575 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7577 switch(out_encoding) {
7578 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7579 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7580 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7581 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7582 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7583 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7584 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7585 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7586 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7587 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7588 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7589 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7590 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7591 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7592 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7593 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7594 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7595 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7596 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7597 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7598 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7599 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7600 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7601 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7602 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7603 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7604 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7605 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7606 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7607 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7608 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7609 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7611 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7614 static void compose_set_template_menu(Compose *compose)
7616 GSList *tmpl_list, *cur;
7620 tmpl_list = template_get_config();
7622 menu = gtk_menu_new();
7624 gtk_menu_set_accel_group (GTK_MENU (menu),
7625 gtk_ui_manager_get_accel_group(compose->ui_manager));
7626 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7627 Template *tmpl = (Template *)cur->data;
7628 gchar *accel_path = NULL;
7629 item = gtk_menu_item_new_with_label(tmpl->name);
7630 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7631 g_signal_connect(G_OBJECT(item), "activate",
7632 G_CALLBACK(compose_template_activate_cb),
7634 g_object_set_data(G_OBJECT(item), "template", tmpl);
7635 gtk_widget_show(item);
7636 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7637 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7641 gtk_widget_show(menu);
7642 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7645 void compose_update_actions_menu(Compose *compose)
7647 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7650 static void compose_update_privacy_systems_menu(Compose *compose)
7652 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7653 GSList *systems, *cur;
7655 GtkWidget *system_none;
7657 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7658 GtkWidget *privacy_menu = gtk_menu_new();
7660 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7661 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7663 g_signal_connect(G_OBJECT(system_none), "activate",
7664 G_CALLBACK(compose_set_privacy_system_cb), compose);
7666 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7667 gtk_widget_show(system_none);
7669 systems = privacy_get_system_ids();
7670 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7671 gchar *systemid = cur->data;
7673 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7674 widget = gtk_radio_menu_item_new_with_label(group,
7675 privacy_system_get_name(systemid));
7676 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7677 g_strdup(systemid), g_free);
7678 g_signal_connect(G_OBJECT(widget), "activate",
7679 G_CALLBACK(compose_set_privacy_system_cb), compose);
7681 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7682 gtk_widget_show(widget);
7685 g_slist_free(systems);
7686 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7687 gtk_widget_show_all(privacy_menu);
7688 gtk_widget_show_all(privacy_menuitem);
7691 void compose_reflect_prefs_all(void)
7696 for (cur = compose_list; cur != NULL; cur = cur->next) {
7697 compose = (Compose *)cur->data;
7698 compose_set_template_menu(compose);
7702 void compose_reflect_prefs_pixmap_theme(void)
7707 for (cur = compose_list; cur != NULL; cur = cur->next) {
7708 compose = (Compose *)cur->data;
7709 toolbar_update(TOOLBAR_COMPOSE, compose);
7713 static const gchar *compose_quote_char_from_context(Compose *compose)
7715 const gchar *qmark = NULL;
7717 g_return_val_if_fail(compose != NULL, NULL);
7719 switch (compose->mode) {
7720 /* use forward-specific quote char */
7721 case COMPOSE_FORWARD:
7722 case COMPOSE_FORWARD_AS_ATTACH:
7723 case COMPOSE_FORWARD_INLINE:
7724 if (compose->folder && compose->folder->prefs &&
7725 compose->folder->prefs->forward_with_format)
7726 qmark = compose->folder->prefs->forward_quotemark;
7727 else if (compose->account->forward_with_format)
7728 qmark = compose->account->forward_quotemark;
7730 qmark = prefs_common.fw_quotemark;
7733 /* use reply-specific quote char in all other modes */
7735 if (compose->folder && compose->folder->prefs &&
7736 compose->folder->prefs->reply_with_format)
7737 qmark = compose->folder->prefs->reply_quotemark;
7738 else if (compose->account->reply_with_format)
7739 qmark = compose->account->reply_quotemark;
7741 qmark = prefs_common.quotemark;
7745 if (qmark == NULL || *qmark == '\0')
7751 static void compose_template_apply(Compose *compose, Template *tmpl,
7755 GtkTextBuffer *buffer;
7759 gchar *parsed_str = NULL;
7760 gint cursor_pos = 0;
7761 const gchar *err_msg = _("Template body format error at line %d.");
7764 /* process the body */
7766 text = GTK_TEXT_VIEW(compose->text);
7767 buffer = gtk_text_view_get_buffer(text);
7770 qmark = compose_quote_char_from_context(compose);
7772 if (compose->replyinfo != NULL) {
7775 gtk_text_buffer_set_text(buffer, "", -1);
7776 mark = gtk_text_buffer_get_insert(buffer);
7777 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7779 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7780 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7782 } else if (compose->fwdinfo != NULL) {
7785 gtk_text_buffer_set_text(buffer, "", -1);
7786 mark = gtk_text_buffer_get_insert(buffer);
7787 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7789 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7790 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7793 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7795 GtkTextIter start, end;
7798 gtk_text_buffer_get_start_iter(buffer, &start);
7799 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7800 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7802 /* clear the buffer now */
7804 gtk_text_buffer_set_text(buffer, "", -1);
7806 parsed_str = compose_quote_fmt(compose, dummyinfo,
7807 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7808 procmsg_msginfo_free( dummyinfo );
7814 gtk_text_buffer_set_text(buffer, "", -1);
7815 mark = gtk_text_buffer_get_insert(buffer);
7816 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7819 if (replace && parsed_str && compose->account->auto_sig)
7820 compose_insert_sig(compose, FALSE);
7822 if (replace && parsed_str) {
7823 gtk_text_buffer_get_start_iter(buffer, &iter);
7824 gtk_text_buffer_place_cursor(buffer, &iter);
7828 cursor_pos = quote_fmt_get_cursor_pos();
7829 compose->set_cursor_pos = cursor_pos;
7830 if (cursor_pos == -1)
7832 gtk_text_buffer_get_start_iter(buffer, &iter);
7833 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7834 gtk_text_buffer_place_cursor(buffer, &iter);
7837 /* process the other fields */
7839 compose_template_apply_fields(compose, tmpl);
7840 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7841 quote_fmt_reset_vartable();
7842 compose_changed_cb(NULL, compose);
7845 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7847 MsgInfo* dummyinfo = NULL;
7848 MsgInfo *msginfo = NULL;
7851 if (compose->replyinfo != NULL)
7852 msginfo = compose->replyinfo;
7853 else if (compose->fwdinfo != NULL)
7854 msginfo = compose->fwdinfo;
7856 dummyinfo = compose_msginfo_new_from_compose(compose);
7857 msginfo = dummyinfo;
7860 if (tmpl->from && *tmpl->from != '\0') {
7862 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7863 compose->gtkaspell);
7865 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7867 quote_fmt_scan_string(tmpl->from);
7870 buf = quote_fmt_get_buffer();
7872 alertpanel_error(_("Template From format error."));
7874 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
7878 if (tmpl->to && *tmpl->to != '\0') {
7880 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7881 compose->gtkaspell);
7883 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7885 quote_fmt_scan_string(tmpl->to);
7888 buf = quote_fmt_get_buffer();
7890 alertpanel_error(_("Template To format error."));
7892 compose_entry_append(compose, buf, COMPOSE_TO);
7896 if (tmpl->cc && *tmpl->cc != '\0') {
7898 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7899 compose->gtkaspell);
7901 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7903 quote_fmt_scan_string(tmpl->cc);
7906 buf = quote_fmt_get_buffer();
7908 alertpanel_error(_("Template Cc format error."));
7910 compose_entry_append(compose, buf, COMPOSE_CC);
7914 if (tmpl->bcc && *tmpl->bcc != '\0') {
7916 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7917 compose->gtkaspell);
7919 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7921 quote_fmt_scan_string(tmpl->bcc);
7924 buf = quote_fmt_get_buffer();
7926 alertpanel_error(_("Template Bcc format error."));
7928 compose_entry_append(compose, buf, COMPOSE_BCC);
7932 /* process the subject */
7933 if (tmpl->subject && *tmpl->subject != '\0') {
7935 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7936 compose->gtkaspell);
7938 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7940 quote_fmt_scan_string(tmpl->subject);
7943 buf = quote_fmt_get_buffer();
7945 alertpanel_error(_("Template subject format error."));
7947 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
7951 procmsg_msginfo_free( dummyinfo );
7954 static void compose_destroy(Compose *compose)
7956 GtkTextBuffer *buffer;
7957 GtkClipboard *clipboard;
7959 compose_list = g_list_remove(compose_list, compose);
7961 if (compose->updating) {
7962 debug_print("danger, not destroying anything now\n");
7963 compose->deferred_destroy = TRUE;
7966 /* NOTE: address_completion_end() does nothing with the window
7967 * however this may change. */
7968 address_completion_end(compose->window);
7970 slist_free_strings(compose->to_list);
7971 g_slist_free(compose->to_list);
7972 slist_free_strings(compose->newsgroup_list);
7973 g_slist_free(compose->newsgroup_list);
7974 slist_free_strings(compose->header_list);
7975 g_slist_free(compose->header_list);
7977 procmsg_msginfo_free(compose->targetinfo);
7978 procmsg_msginfo_free(compose->replyinfo);
7979 procmsg_msginfo_free(compose->fwdinfo);
7981 g_free(compose->replyto);
7982 g_free(compose->cc);
7983 g_free(compose->bcc);
7984 g_free(compose->newsgroups);
7985 g_free(compose->followup_to);
7987 g_free(compose->ml_post);
7989 g_free(compose->inreplyto);
7990 g_free(compose->references);
7991 g_free(compose->msgid);
7992 g_free(compose->boundary);
7994 g_free(compose->redirect_filename);
7995 if (compose->undostruct)
7996 undo_destroy(compose->undostruct);
7998 g_free(compose->sig_str);
8000 g_free(compose->exteditor_file);
8002 g_free(compose->orig_charset);
8004 g_free(compose->privacy_system);
8006 if (addressbook_get_target_compose() == compose)
8007 addressbook_set_target_compose(NULL);
8010 if (compose->gtkaspell) {
8011 gtkaspell_delete(compose->gtkaspell);
8012 compose->gtkaspell = NULL;
8016 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8017 prefs_common.compose_height = compose->window->allocation.height;
8019 if (!gtk_widget_get_parent(compose->paned))
8020 gtk_widget_destroy(compose->paned);
8021 gtk_widget_destroy(compose->popupmenu);
8023 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8024 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8025 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8027 gtk_widget_destroy(compose->window);
8028 toolbar_destroy(compose->toolbar);
8029 g_free(compose->toolbar);
8030 g_mutex_free(compose->mutex);
8034 static void compose_attach_info_free(AttachInfo *ainfo)
8036 g_free(ainfo->file);
8037 g_free(ainfo->content_type);
8038 g_free(ainfo->name);
8042 static void compose_attach_update_label(Compose *compose)
8047 GtkTreeModel *model;
8052 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8053 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8054 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8058 while(gtk_tree_model_iter_next(model, &iter))
8061 text = g_strdup_printf("(%d)", i);
8062 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8066 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8068 Compose *compose = (Compose *)data;
8069 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8070 GtkTreeSelection *selection;
8072 GtkTreeModel *model;
8074 selection = gtk_tree_view_get_selection(tree_view);
8075 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8080 for (cur = sel; cur != NULL; cur = cur->next) {
8081 GtkTreePath *path = cur->data;
8082 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8085 gtk_tree_path_free(path);
8088 for (cur = sel; cur != NULL; cur = cur->next) {
8089 GtkTreeRowReference *ref = cur->data;
8090 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8093 if (gtk_tree_model_get_iter(model, &iter, path))
8094 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8096 gtk_tree_path_free(path);
8097 gtk_tree_row_reference_free(ref);
8101 compose_attach_update_label(compose);
8104 static struct _AttachProperty
8107 GtkWidget *mimetype_entry;
8108 GtkWidget *encoding_optmenu;
8109 GtkWidget *path_entry;
8110 GtkWidget *filename_entry;
8112 GtkWidget *cancel_btn;
8115 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8117 gtk_tree_path_free((GtkTreePath *)ptr);
8120 static void compose_attach_property(GtkAction *action, gpointer data)
8122 Compose *compose = (Compose *)data;
8123 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8125 GtkComboBox *optmenu;
8126 GtkTreeSelection *selection;
8128 GtkTreeModel *model;
8131 static gboolean cancelled;
8133 /* only if one selected */
8134 selection = gtk_tree_view_get_selection(tree_view);
8135 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8138 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8142 path = (GtkTreePath *) sel->data;
8143 gtk_tree_model_get_iter(model, &iter, path);
8144 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8147 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8153 if (!attach_prop.window)
8154 compose_attach_property_create(&cancelled);
8155 gtk_widget_grab_focus(attach_prop.ok_btn);
8156 gtk_widget_show(attach_prop.window);
8157 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8159 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8160 if (ainfo->encoding == ENC_UNKNOWN)
8161 combobox_select_by_data(optmenu, ENC_BASE64);
8163 combobox_select_by_data(optmenu, ainfo->encoding);
8165 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8166 ainfo->content_type ? ainfo->content_type : "");
8167 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8168 ainfo->file ? ainfo->file : "");
8169 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8170 ainfo->name ? ainfo->name : "");
8173 const gchar *entry_text;
8175 gchar *cnttype = NULL;
8182 gtk_widget_hide(attach_prop.window);
8187 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8188 if (*entry_text != '\0') {
8191 text = g_strstrip(g_strdup(entry_text));
8192 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8193 cnttype = g_strdup(text);
8196 alertpanel_error(_("Invalid MIME type."));
8202 ainfo->encoding = combobox_get_active_data(optmenu);
8204 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8205 if (*entry_text != '\0') {
8206 if (is_file_exist(entry_text) &&
8207 (size = get_file_size(entry_text)) > 0)
8208 file = g_strdup(entry_text);
8211 (_("File doesn't exist or is empty."));
8217 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8218 if (*entry_text != '\0') {
8219 g_free(ainfo->name);
8220 ainfo->name = g_strdup(entry_text);
8224 g_free(ainfo->content_type);
8225 ainfo->content_type = cnttype;
8228 g_free(ainfo->file);
8232 ainfo->size = (goffset)size;
8234 /* update tree store */
8235 text = to_human_readable(ainfo->size);
8236 gtk_tree_model_get_iter(model, &iter, path);
8237 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8238 COL_MIMETYPE, ainfo->content_type,
8240 COL_NAME, ainfo->name,
8246 gtk_tree_path_free(path);
8249 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8251 label = gtk_label_new(str); \
8252 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8253 GTK_FILL, 0, 0, 0); \
8254 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8256 entry = gtk_entry_new(); \
8257 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8258 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8261 static void compose_attach_property_create(gboolean *cancelled)
8267 GtkWidget *mimetype_entry;
8270 GtkListStore *optmenu_menu;
8271 GtkWidget *path_entry;
8272 GtkWidget *filename_entry;
8275 GtkWidget *cancel_btn;
8276 GList *mime_type_list, *strlist;
8279 debug_print("Creating attach_property window...\n");
8281 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8282 gtk_widget_set_size_request(window, 480, -1);
8283 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8284 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8285 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8286 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8287 g_signal_connect(G_OBJECT(window), "delete_event",
8288 G_CALLBACK(attach_property_delete_event),
8290 g_signal_connect(G_OBJECT(window), "key_press_event",
8291 G_CALLBACK(attach_property_key_pressed),
8294 vbox = gtk_vbox_new(FALSE, 8);
8295 gtk_container_add(GTK_CONTAINER(window), vbox);
8297 table = gtk_table_new(4, 2, FALSE);
8298 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8299 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8300 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8302 label = gtk_label_new(_("MIME type"));
8303 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8305 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8306 mimetype_entry = gtk_combo_box_entry_new_text();
8307 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8308 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8310 /* stuff with list */
8311 mime_type_list = procmime_get_mime_type_list();
8313 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8314 MimeType *type = (MimeType *) mime_type_list->data;
8317 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8319 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8322 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8323 (GCompareFunc)strcmp2);
8326 for (mime_type_list = strlist; mime_type_list != NULL;
8327 mime_type_list = mime_type_list->next) {
8328 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8329 g_free(mime_type_list->data);
8331 g_list_free(strlist);
8332 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8333 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8335 label = gtk_label_new(_("Encoding"));
8336 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8338 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8340 hbox = gtk_hbox_new(FALSE, 0);
8341 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8342 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8344 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8345 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8347 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8348 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8349 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8350 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8351 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8353 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8355 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8356 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8358 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8359 &ok_btn, GTK_STOCK_OK,
8361 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8362 gtk_widget_grab_default(ok_btn);
8364 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8365 G_CALLBACK(attach_property_ok),
8367 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8368 G_CALLBACK(attach_property_cancel),
8371 gtk_widget_show_all(vbox);
8373 attach_prop.window = window;
8374 attach_prop.mimetype_entry = mimetype_entry;
8375 attach_prop.encoding_optmenu = optmenu;
8376 attach_prop.path_entry = path_entry;
8377 attach_prop.filename_entry = filename_entry;
8378 attach_prop.ok_btn = ok_btn;
8379 attach_prop.cancel_btn = cancel_btn;
8382 #undef SET_LABEL_AND_ENTRY
8384 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8390 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8396 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8397 gboolean *cancelled)
8405 static gboolean attach_property_key_pressed(GtkWidget *widget,
8407 gboolean *cancelled)
8409 if (event && event->keyval == GDK_Escape) {
8416 static void compose_exec_ext_editor(Compose *compose)
8423 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8424 G_DIR_SEPARATOR, compose);
8426 if (pipe(pipe_fds) < 0) {
8432 if ((pid = fork()) < 0) {
8439 /* close the write side of the pipe */
8442 compose->exteditor_file = g_strdup(tmp);
8443 compose->exteditor_pid = pid;
8445 compose_set_ext_editor_sensitive(compose, FALSE);
8447 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8448 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8452 } else { /* process-monitoring process */
8458 /* close the read side of the pipe */
8461 if (compose_write_body_to_file(compose, tmp) < 0) {
8462 fd_write_all(pipe_fds[1], "2\n", 2);
8466 pid_ed = compose_exec_ext_editor_real(tmp);
8468 fd_write_all(pipe_fds[1], "1\n", 2);
8472 /* wait until editor is terminated */
8473 waitpid(pid_ed, NULL, 0);
8475 fd_write_all(pipe_fds[1], "0\n", 2);
8482 #endif /* G_OS_UNIX */
8486 static gint compose_exec_ext_editor_real(const gchar *file)
8493 g_return_val_if_fail(file != NULL, -1);
8495 if ((pid = fork()) < 0) {
8500 if (pid != 0) return pid;
8502 /* grandchild process */
8504 if (setpgid(0, getppid()))
8507 if (prefs_common_get_ext_editor_cmd() &&
8508 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8509 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8510 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8512 if (prefs_common_get_ext_editor_cmd())
8513 g_warning("External editor command line is invalid: '%s'\n",
8514 prefs_common_get_ext_editor_cmd());
8515 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8518 cmdline = strsplit_with_quote(buf, " ", 1024);
8519 execvp(cmdline[0], cmdline);
8522 g_strfreev(cmdline);
8527 static gboolean compose_ext_editor_kill(Compose *compose)
8529 pid_t pgid = compose->exteditor_pid * -1;
8532 ret = kill(pgid, 0);
8534 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8538 msg = g_strdup_printf
8539 (_("The external editor is still working.\n"
8540 "Force terminating the process?\n"
8541 "process group id: %d"), -pgid);
8542 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8543 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8547 if (val == G_ALERTALTERNATE) {
8548 g_source_remove(compose->exteditor_tag);
8549 g_io_channel_shutdown(compose->exteditor_ch,
8551 g_io_channel_unref(compose->exteditor_ch);
8553 if (kill(pgid, SIGTERM) < 0) perror("kill");
8554 waitpid(compose->exteditor_pid, NULL, 0);
8556 g_warning("Terminated process group id: %d", -pgid);
8557 g_warning("Temporary file: %s",
8558 compose->exteditor_file);
8560 compose_set_ext_editor_sensitive(compose, TRUE);
8562 g_free(compose->exteditor_file);
8563 compose->exteditor_file = NULL;
8564 compose->exteditor_pid = -1;
8565 compose->exteditor_ch = NULL;
8566 compose->exteditor_tag = -1;
8574 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8578 Compose *compose = (Compose *)data;
8581 debug_print(_("Compose: input from monitoring process\n"));
8583 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8585 g_io_channel_shutdown(source, FALSE, NULL);
8586 g_io_channel_unref(source);
8588 waitpid(compose->exteditor_pid, NULL, 0);
8590 if (buf[0] == '0') { /* success */
8591 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8592 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8594 gtk_text_buffer_set_text(buffer, "", -1);
8595 compose_insert_file(compose, compose->exteditor_file);
8596 compose_changed_cb(NULL, compose);
8598 if (claws_unlink(compose->exteditor_file) < 0)
8599 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8600 } else if (buf[0] == '1') { /* failed */
8601 g_warning("Couldn't exec external editor\n");
8602 if (claws_unlink(compose->exteditor_file) < 0)
8603 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8604 } else if (buf[0] == '2') {
8605 g_warning("Couldn't write to file\n");
8606 } else if (buf[0] == '3') {
8607 g_warning("Pipe read failed\n");
8610 compose_set_ext_editor_sensitive(compose, TRUE);
8612 g_free(compose->exteditor_file);
8613 compose->exteditor_file = NULL;
8614 compose->exteditor_pid = -1;
8615 compose->exteditor_ch = NULL;
8616 compose->exteditor_tag = -1;
8621 static void compose_set_ext_editor_sensitive(Compose *compose,
8624 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8625 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8626 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8627 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8628 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8629 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8630 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8632 gtk_widget_set_sensitive(compose->text, sensitive);
8633 if (compose->toolbar->send_btn)
8634 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8635 if (compose->toolbar->sendl_btn)
8636 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8637 if (compose->toolbar->draft_btn)
8638 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8639 if (compose->toolbar->insert_btn)
8640 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8641 if (compose->toolbar->sig_btn)
8642 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8643 if (compose->toolbar->exteditor_btn)
8644 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8645 if (compose->toolbar->linewrap_current_btn)
8646 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8647 if (compose->toolbar->linewrap_all_btn)
8648 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8650 #endif /* G_OS_UNIX */
8653 * compose_undo_state_changed:
8655 * Change the sensivity of the menuentries undo and redo
8657 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8658 gint redo_state, gpointer data)
8660 Compose *compose = (Compose *)data;
8662 switch (undo_state) {
8663 case UNDO_STATE_TRUE:
8664 if (!undostruct->undo_state) {
8665 undostruct->undo_state = TRUE;
8666 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8669 case UNDO_STATE_FALSE:
8670 if (undostruct->undo_state) {
8671 undostruct->undo_state = FALSE;
8672 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8675 case UNDO_STATE_UNCHANGED:
8677 case UNDO_STATE_REFRESH:
8678 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8681 g_warning("Undo state not recognized");
8685 switch (redo_state) {
8686 case UNDO_STATE_TRUE:
8687 if (!undostruct->redo_state) {
8688 undostruct->redo_state = TRUE;
8689 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8692 case UNDO_STATE_FALSE:
8693 if (undostruct->redo_state) {
8694 undostruct->redo_state = FALSE;
8695 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8698 case UNDO_STATE_UNCHANGED:
8700 case UNDO_STATE_REFRESH:
8701 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8704 g_warning("Redo state not recognized");
8709 /* callback functions */
8711 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8712 * includes "non-client" (windows-izm) in calculation, so this calculation
8713 * may not be accurate.
8715 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8716 GtkAllocation *allocation,
8717 GtkSHRuler *shruler)
8719 if (prefs_common.show_ruler) {
8720 gint char_width = 0, char_height = 0;
8721 gint line_width_in_chars;
8723 gtkut_get_font_size(GTK_WIDGET(widget),
8724 &char_width, &char_height);
8725 line_width_in_chars =
8726 (allocation->width - allocation->x) / char_width;
8728 /* got the maximum */
8729 gtk_ruler_set_range(GTK_RULER(shruler),
8730 0.0, line_width_in_chars, 0,
8731 /*line_width_in_chars*/ char_width);
8737 static void account_activated(GtkComboBox *optmenu, gpointer data)
8739 Compose *compose = (Compose *)data;
8742 gchar *folderidentifier;
8743 gint account_id = 0;
8747 /* Get ID of active account in the combo box */
8748 menu = gtk_combo_box_get_model(optmenu);
8749 gtk_combo_box_get_active_iter(optmenu, &iter);
8750 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8752 ac = account_find_from_id(account_id);
8753 g_return_if_fail(ac != NULL);
8755 if (ac != compose->account)
8756 compose_select_account(compose, ac, FALSE);
8758 /* Set message save folder */
8759 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8760 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8762 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8763 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8765 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8766 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8767 folderidentifier = folder_item_get_identifier(account_get_special_folder
8768 (compose->account, F_OUTBOX));
8769 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
8770 g_free(folderidentifier);
8774 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8775 GtkTreeViewColumn *column, Compose *compose)
8777 compose_attach_property(NULL, compose);
8780 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8783 Compose *compose = (Compose *)data;
8784 GtkTreeSelection *attach_selection;
8785 gint attach_nr_selected;
8787 if (!event) return FALSE;
8789 if (event->button == 3) {
8790 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8791 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8793 if (attach_nr_selected > 0)
8795 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8796 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8798 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8799 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8802 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8803 NULL, NULL, event->button, event->time);
8810 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8813 Compose *compose = (Compose *)data;
8815 if (!event) return FALSE;
8817 switch (event->keyval) {
8819 compose_attach_remove_selected(NULL, compose);
8825 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8827 toolbar_comp_set_sensitive(compose, allow);
8828 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8829 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8831 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8833 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8834 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8835 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8837 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8841 static void compose_send_cb(GtkAction *action, gpointer data)
8843 Compose *compose = (Compose *)data;
8845 if (prefs_common.work_offline &&
8846 !inc_offline_should_override(TRUE,
8847 _("Claws Mail needs network access in order "
8848 "to send this email.")))
8851 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8852 g_source_remove(compose->draft_timeout_tag);
8853 compose->draft_timeout_tag = -1;
8856 compose_send(compose);
8859 static void compose_send_later_cb(GtkAction *action, gpointer data)
8861 Compose *compose = (Compose *)data;
8865 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8869 compose_close(compose);
8870 } else if (val == -1) {
8871 alertpanel_error(_("Could not queue message."));
8872 } else if (val == -2) {
8873 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8874 } else if (val == -3) {
8875 if (privacy_peek_error())
8876 alertpanel_error(_("Could not queue message for sending:\n\n"
8877 "Signature failed: %s"), privacy_get_error());
8878 } else if (val == -4) {
8879 alertpanel_error(_("Could not queue message for sending:\n\n"
8880 "Charset conversion failed."));
8881 } else if (val == -5) {
8882 alertpanel_error(_("Could not queue message for sending:\n\n"
8883 "Couldn't get recipient encryption key."));
8884 } else if (val == -6) {
8887 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
8890 #define DRAFTED_AT_EXIT "drafted_at_exit"
8891 static void compose_register_draft(MsgInfo *info)
8893 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8894 DRAFTED_AT_EXIT, NULL);
8895 FILE *fp = fopen(filepath, "ab");
8898 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
8906 gboolean compose_draft (gpointer data, guint action)
8908 Compose *compose = (Compose *)data;
8912 MsgFlags flag = {0, 0};
8913 static gboolean lock = FALSE;
8914 MsgInfo *newmsginfo;
8916 gboolean target_locked = FALSE;
8917 gboolean err = FALSE;
8919 if (lock) return FALSE;
8921 if (compose->sending)
8924 draft = account_get_special_folder(compose->account, F_DRAFT);
8925 g_return_val_if_fail(draft != NULL, FALSE);
8927 if (!g_mutex_trylock(compose->mutex)) {
8928 /* we don't want to lock the mutex once it's available,
8929 * because as the only other part of compose.c locking
8930 * it is compose_close - which means once unlocked,
8931 * the compose struct will be freed */
8932 debug_print("couldn't lock mutex, probably sending\n");
8938 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
8939 G_DIR_SEPARATOR, compose);
8940 if ((fp = g_fopen(tmp, "wb")) == NULL) {
8941 FILE_OP_ERROR(tmp, "fopen");
8945 /* chmod for security */
8946 if (change_file_mode_rw(fp, tmp) < 0) {
8947 FILE_OP_ERROR(tmp, "chmod");
8948 g_warning("can't change file mode\n");
8951 /* Save draft infos */
8952 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
8953 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
8955 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
8956 gchar *savefolderid;
8958 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8959 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
8960 g_free(savefolderid);
8962 if (compose->return_receipt) {
8963 err |= (fprintf(fp, "RRCPT:1\n") < 0);
8965 if (compose->privacy_system) {
8966 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
8967 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
8968 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
8971 /* Message-ID of message replying to */
8972 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
8975 folderid = folder_item_get_identifier(compose->replyinfo->folder);
8976 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
8979 /* Message-ID of message forwarding to */
8980 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
8983 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
8984 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
8988 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
8990 /* end of headers */
8991 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
8998 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9002 if (fclose(fp) == EOF) {
9006 if (compose->targetinfo) {
9007 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9008 flag.perm_flags = target_locked?MSG_LOCKED:0;
9010 flag.tmp_flags = MSG_DRAFT;
9012 folder_item_scan(draft);
9013 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9014 MsgInfo *tmpinfo = NULL;
9015 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9016 if (compose->msgid) {
9017 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9020 msgnum = tmpinfo->msgnum;
9021 procmsg_msginfo_free(tmpinfo);
9022 debug_print("got draft msgnum %d from scanning\n", msgnum);
9024 debug_print("didn't get draft msgnum after scanning\n");
9027 debug_print("got draft msgnum %d from adding\n", msgnum);
9033 if (action != COMPOSE_AUTO_SAVE) {
9034 if (action != COMPOSE_DRAFT_FOR_EXIT)
9035 alertpanel_error(_("Could not save draft."));
9038 gtkut_window_popup(compose->window);
9039 val = alertpanel_full(_("Could not save draft"),
9040 _("Could not save draft.\n"
9041 "Do you want to cancel exit or discard this email?"),
9042 _("_Cancel exit"), _("_Discard email"), NULL,
9043 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9044 if (val == G_ALERTALTERNATE) {
9046 g_mutex_unlock(compose->mutex); /* must be done before closing */
9047 compose_close(compose);
9051 g_mutex_unlock(compose->mutex); /* must be done before closing */
9060 if (compose->mode == COMPOSE_REEDIT) {
9061 compose_remove_reedit_target(compose, TRUE);
9064 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9067 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9069 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9071 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9072 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9073 procmsg_msginfo_set_flags(newmsginfo, 0,
9074 MSG_HAS_ATTACHMENT);
9076 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9077 compose_register_draft(newmsginfo);
9079 procmsg_msginfo_free(newmsginfo);
9082 folder_item_scan(draft);
9084 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9086 g_mutex_unlock(compose->mutex); /* must be done before closing */
9087 compose_close(compose);
9093 path = folder_item_fetch_msg(draft, msgnum);
9095 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9098 if (g_stat(path, &s) < 0) {
9099 FILE_OP_ERROR(path, "stat");
9105 procmsg_msginfo_free(compose->targetinfo);
9106 compose->targetinfo = procmsg_msginfo_new();
9107 compose->targetinfo->msgnum = msgnum;
9108 compose->targetinfo->size = (goffset)s.st_size;
9109 compose->targetinfo->mtime = s.st_mtime;
9110 compose->targetinfo->folder = draft;
9112 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9113 compose->mode = COMPOSE_REEDIT;
9115 if (action == COMPOSE_AUTO_SAVE) {
9116 compose->autosaved_draft = compose->targetinfo;
9118 compose->modified = FALSE;
9119 compose_set_title(compose);
9123 g_mutex_unlock(compose->mutex);
9127 void compose_clear_exit_drafts(void)
9129 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9130 DRAFTED_AT_EXIT, NULL);
9131 if (is_file_exist(filepath))
9132 claws_unlink(filepath);
9137 void compose_reopen_exit_drafts(void)
9139 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9140 DRAFTED_AT_EXIT, NULL);
9141 FILE *fp = fopen(filepath, "rb");
9145 while (fgets(buf, sizeof(buf), fp)) {
9146 gchar **parts = g_strsplit(buf, "\t", 2);
9147 const gchar *folder = parts[0];
9148 int msgnum = parts[1] ? atoi(parts[1]):-1;
9150 if (folder && *folder && msgnum > -1) {
9151 FolderItem *item = folder_find_item_from_identifier(folder);
9152 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9154 compose_reedit(info, FALSE);
9161 compose_clear_exit_drafts();
9164 static void compose_save_cb(GtkAction *action, gpointer data)
9166 compose_draft(data, COMPOSE_KEEP_EDITING);
9169 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9171 if (compose && file_list) {
9174 for ( tmp = file_list; tmp; tmp = tmp->next) {
9175 gchar *file = (gchar *) tmp->data;
9176 gchar *utf8_filename = conv_filename_to_utf8(file);
9177 compose_attach_append(compose, file, utf8_filename, NULL);
9178 compose_changed_cb(NULL, compose);
9183 g_free(utf8_filename);
9188 static void compose_attach_cb(GtkAction *action, gpointer data)
9190 Compose *compose = (Compose *)data;
9193 if (compose->redirect_filename != NULL)
9196 file_list = filesel_select_multiple_files_open(_("Select file"));
9199 compose_attach_from_list(compose, file_list, TRUE);
9200 g_list_free(file_list);
9204 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9206 Compose *compose = (Compose *)data;
9209 file_list = filesel_select_multiple_files_open(_("Select file"));
9214 for ( tmp = file_list; tmp; tmp = tmp->next) {
9215 gchar *file = (gchar *) tmp->data;
9216 gchar *filedup = g_strdup(file);
9217 gchar *shortfile = g_path_get_basename(filedup);
9218 ComposeInsertResult res;
9220 res = compose_insert_file(compose, file);
9221 if (res == COMPOSE_INSERT_READ_ERROR) {
9222 alertpanel_error(_("File '%s' could not be read."), shortfile);
9223 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9224 alertpanel_error(_("File '%s' contained invalid characters\n"
9225 "for the current encoding, insertion may be incorrect."), shortfile);
9231 g_list_free(file_list);
9235 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9237 Compose *compose = (Compose *)data;
9239 compose_insert_sig(compose, FALSE);
9242 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9246 Compose *compose = (Compose *)data;
9248 gtkut_widget_get_uposition(widget, &x, &y);
9249 prefs_common.compose_x = x;
9250 prefs_common.compose_y = y;
9252 if (compose->sending || compose->updating)
9254 compose_close_cb(NULL, compose);
9258 void compose_close_toolbar(Compose *compose)
9260 compose_close_cb(NULL, compose);
9263 static void compose_close_cb(GtkAction *action, gpointer data)
9265 Compose *compose = (Compose *)data;
9269 if (compose->exteditor_tag != -1) {
9270 if (!compose_ext_editor_kill(compose))
9275 if (compose->modified) {
9276 if (!g_mutex_trylock(compose->mutex)) {
9277 /* we don't want to lock the mutex once it's available,
9278 * because as the only other part of compose.c locking
9279 * it is compose_close - which means once unlocked,
9280 * the compose struct will be freed */
9281 debug_print("couldn't lock mutex, probably sending\n");
9284 val = alertpanel(_("Discard message"),
9285 _("This message has been modified. Discard it?"),
9286 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9287 g_mutex_unlock(compose->mutex);
9289 case G_ALERTDEFAULT:
9290 if (prefs_common.autosave)
9291 compose_remove_draft(compose);
9293 case G_ALERTALTERNATE:
9294 compose_draft(data, COMPOSE_QUIT_EDITING);
9301 compose_close(compose);
9304 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9306 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9307 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9308 Compose *compose = (Compose *) data;
9311 compose->out_encoding = (CharSet)value;
9314 static void compose_address_cb(GtkAction *action, gpointer data)
9316 Compose *compose = (Compose *)data;
9318 addressbook_open(compose);
9321 static void about_show_cb(GtkAction *action, gpointer data)
9326 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9328 Compose *compose = (Compose *)data;
9333 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9334 g_return_if_fail(tmpl != NULL);
9336 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9338 val = alertpanel(_("Apply template"), msg,
9339 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9342 if (val == G_ALERTDEFAULT)
9343 compose_template_apply(compose, tmpl, TRUE);
9344 else if (val == G_ALERTALTERNATE)
9345 compose_template_apply(compose, tmpl, FALSE);
9348 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9350 Compose *compose = (Compose *)data;
9352 compose_exec_ext_editor(compose);
9355 static void compose_undo_cb(GtkAction *action, gpointer data)
9357 Compose *compose = (Compose *)data;
9358 gboolean prev_autowrap = compose->autowrap;
9360 compose->autowrap = FALSE;
9361 undo_undo(compose->undostruct);
9362 compose->autowrap = prev_autowrap;
9365 static void compose_redo_cb(GtkAction *action, gpointer data)
9367 Compose *compose = (Compose *)data;
9368 gboolean prev_autowrap = compose->autowrap;
9370 compose->autowrap = FALSE;
9371 undo_redo(compose->undostruct);
9372 compose->autowrap = prev_autowrap;
9375 static void entry_cut_clipboard(GtkWidget *entry)
9377 if (GTK_IS_EDITABLE(entry))
9378 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9379 else if (GTK_IS_TEXT_VIEW(entry))
9380 gtk_text_buffer_cut_clipboard(
9381 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9382 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9386 static void entry_copy_clipboard(GtkWidget *entry)
9388 if (GTK_IS_EDITABLE(entry))
9389 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9390 else if (GTK_IS_TEXT_VIEW(entry))
9391 gtk_text_buffer_copy_clipboard(
9392 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9393 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9396 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9397 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9399 if (GTK_IS_TEXT_VIEW(entry)) {
9400 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9401 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9402 GtkTextIter start_iter, end_iter;
9404 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9406 if (contents == NULL)
9409 /* we shouldn't delete the selection when middle-click-pasting, or we
9410 * can't mid-click-paste our own selection */
9411 if (clip != GDK_SELECTION_PRIMARY) {
9412 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9413 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9416 if (insert_place == NULL) {
9417 /* if insert_place isn't specified, insert at the cursor.
9418 * used for Ctrl-V pasting */
9419 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9420 start = gtk_text_iter_get_offset(&start_iter);
9421 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9423 /* if insert_place is specified, paste here.
9424 * used for mid-click-pasting */
9425 start = gtk_text_iter_get_offset(insert_place);
9426 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9430 /* paste unwrapped: mark the paste so it's not wrapped later */
9431 end = start + strlen(contents);
9432 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9433 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9434 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9435 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9436 /* rewrap paragraph now (after a mid-click-paste) */
9437 mark_start = gtk_text_buffer_get_insert(buffer);
9438 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9439 gtk_text_iter_backward_char(&start_iter);
9440 compose_beautify_paragraph(compose, &start_iter, TRUE);
9442 } else if (GTK_IS_EDITABLE(entry))
9443 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9445 compose->modified = TRUE;
9448 static void entry_allsel(GtkWidget *entry)
9450 if (GTK_IS_EDITABLE(entry))
9451 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9452 else if (GTK_IS_TEXT_VIEW(entry)) {
9453 GtkTextIter startiter, enditer;
9454 GtkTextBuffer *textbuf;
9456 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9457 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9458 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9460 gtk_text_buffer_move_mark_by_name(textbuf,
9461 "selection_bound", &startiter);
9462 gtk_text_buffer_move_mark_by_name(textbuf,
9463 "insert", &enditer);
9467 static void compose_cut_cb(GtkAction *action, gpointer data)
9469 Compose *compose = (Compose *)data;
9470 if (compose->focused_editable
9471 #ifndef GENERIC_UMPC
9472 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9475 entry_cut_clipboard(compose->focused_editable);
9478 static void compose_copy_cb(GtkAction *action, gpointer data)
9480 Compose *compose = (Compose *)data;
9481 if (compose->focused_editable
9482 #ifndef GENERIC_UMPC
9483 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9486 entry_copy_clipboard(compose->focused_editable);
9489 static void compose_paste_cb(GtkAction *action, gpointer data)
9491 Compose *compose = (Compose *)data;
9493 GtkTextBuffer *buffer;
9495 if (compose->focused_editable &&
9496 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9497 entry_paste_clipboard(compose, compose->focused_editable,
9498 prefs_common.linewrap_pastes,
9499 GDK_SELECTION_CLIPBOARD, NULL);
9503 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9505 Compose *compose = (Compose *)data;
9506 gint wrap_quote = prefs_common.linewrap_quote;
9507 if (compose->focused_editable
9508 #ifndef GENERIC_UMPC
9509 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9512 /* let text_insert() (called directly or at a later time
9513 * after the gtk_editable_paste_clipboard) know that
9514 * text is to be inserted as a quotation. implemented
9515 * by using a simple refcount... */
9516 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9517 G_OBJECT(compose->focused_editable),
9518 "paste_as_quotation"));
9519 g_object_set_data(G_OBJECT(compose->focused_editable),
9520 "paste_as_quotation",
9521 GINT_TO_POINTER(paste_as_quotation + 1));
9522 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9523 entry_paste_clipboard(compose, compose->focused_editable,
9524 prefs_common.linewrap_pastes,
9525 GDK_SELECTION_CLIPBOARD, NULL);
9526 prefs_common.linewrap_quote = wrap_quote;
9530 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9532 Compose *compose = (Compose *)data;
9534 GtkTextBuffer *buffer;
9536 if (compose->focused_editable
9537 #ifndef GENERIC_UMPC
9538 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9541 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9542 GDK_SELECTION_CLIPBOARD, NULL);
9546 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9548 Compose *compose = (Compose *)data;
9550 GtkTextBuffer *buffer;
9552 if (compose->focused_editable
9553 #ifndef GENERIC_UMPC
9554 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9557 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9558 GDK_SELECTION_CLIPBOARD, NULL);
9562 static void compose_allsel_cb(GtkAction *action, gpointer data)
9564 Compose *compose = (Compose *)data;
9565 if (compose->focused_editable
9566 #ifndef GENERIC_UMPC
9567 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9570 entry_allsel(compose->focused_editable);
9573 static void textview_move_beginning_of_line (GtkTextView *text)
9575 GtkTextBuffer *buffer;
9579 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9581 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9582 mark = gtk_text_buffer_get_insert(buffer);
9583 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9584 gtk_text_iter_set_line_offset(&ins, 0);
9585 gtk_text_buffer_place_cursor(buffer, &ins);
9588 static void textview_move_forward_character (GtkTextView *text)
9590 GtkTextBuffer *buffer;
9594 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9596 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9597 mark = gtk_text_buffer_get_insert(buffer);
9598 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9599 if (gtk_text_iter_forward_cursor_position(&ins))
9600 gtk_text_buffer_place_cursor(buffer, &ins);
9603 static void textview_move_backward_character (GtkTextView *text)
9605 GtkTextBuffer *buffer;
9609 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9611 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9612 mark = gtk_text_buffer_get_insert(buffer);
9613 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9614 if (gtk_text_iter_backward_cursor_position(&ins))
9615 gtk_text_buffer_place_cursor(buffer, &ins);
9618 static void textview_move_forward_word (GtkTextView *text)
9620 GtkTextBuffer *buffer;
9625 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9627 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9628 mark = gtk_text_buffer_get_insert(buffer);
9629 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9630 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9631 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9632 gtk_text_iter_backward_word_start(&ins);
9633 gtk_text_buffer_place_cursor(buffer, &ins);
9637 static void textview_move_backward_word (GtkTextView *text)
9639 GtkTextBuffer *buffer;
9644 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9646 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9647 mark = gtk_text_buffer_get_insert(buffer);
9648 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9649 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9650 if (gtk_text_iter_backward_word_starts(&ins, 1))
9651 gtk_text_buffer_place_cursor(buffer, &ins);
9654 static void textview_move_end_of_line (GtkTextView *text)
9656 GtkTextBuffer *buffer;
9660 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9662 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9663 mark = gtk_text_buffer_get_insert(buffer);
9664 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9665 if (gtk_text_iter_forward_to_line_end(&ins))
9666 gtk_text_buffer_place_cursor(buffer, &ins);
9669 static void textview_move_next_line (GtkTextView *text)
9671 GtkTextBuffer *buffer;
9676 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9678 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9679 mark = gtk_text_buffer_get_insert(buffer);
9680 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9681 offset = gtk_text_iter_get_line_offset(&ins);
9682 if (gtk_text_iter_forward_line(&ins)) {
9683 gtk_text_iter_set_line_offset(&ins, offset);
9684 gtk_text_buffer_place_cursor(buffer, &ins);
9688 static void textview_move_previous_line (GtkTextView *text)
9690 GtkTextBuffer *buffer;
9695 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9697 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9698 mark = gtk_text_buffer_get_insert(buffer);
9699 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9700 offset = gtk_text_iter_get_line_offset(&ins);
9701 if (gtk_text_iter_backward_line(&ins)) {
9702 gtk_text_iter_set_line_offset(&ins, offset);
9703 gtk_text_buffer_place_cursor(buffer, &ins);
9707 static void textview_delete_forward_character (GtkTextView *text)
9709 GtkTextBuffer *buffer;
9711 GtkTextIter ins, end_iter;
9713 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9715 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9716 mark = gtk_text_buffer_get_insert(buffer);
9717 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9719 if (gtk_text_iter_forward_char(&end_iter)) {
9720 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9724 static void textview_delete_backward_character (GtkTextView *text)
9726 GtkTextBuffer *buffer;
9728 GtkTextIter ins, end_iter;
9730 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9732 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9733 mark = gtk_text_buffer_get_insert(buffer);
9734 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9736 if (gtk_text_iter_backward_char(&end_iter)) {
9737 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9741 static void textview_delete_forward_word (GtkTextView *text)
9743 GtkTextBuffer *buffer;
9745 GtkTextIter ins, end_iter;
9747 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9749 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9750 mark = gtk_text_buffer_get_insert(buffer);
9751 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9753 if (gtk_text_iter_forward_word_end(&end_iter)) {
9754 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9758 static void textview_delete_backward_word (GtkTextView *text)
9760 GtkTextBuffer *buffer;
9762 GtkTextIter ins, end_iter;
9764 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9766 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9767 mark = gtk_text_buffer_get_insert(buffer);
9768 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9770 if (gtk_text_iter_backward_word_start(&end_iter)) {
9771 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9775 static void textview_delete_line (GtkTextView *text)
9777 GtkTextBuffer *buffer;
9779 GtkTextIter ins, start_iter, end_iter;
9781 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9783 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9784 mark = gtk_text_buffer_get_insert(buffer);
9785 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9788 gtk_text_iter_set_line_offset(&start_iter, 0);
9791 if (gtk_text_iter_ends_line(&end_iter)){
9792 if (!gtk_text_iter_forward_char(&end_iter))
9793 gtk_text_iter_backward_char(&start_iter);
9796 gtk_text_iter_forward_to_line_end(&end_iter);
9797 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9800 static void textview_delete_to_line_end (GtkTextView *text)
9802 GtkTextBuffer *buffer;
9804 GtkTextIter ins, end_iter;
9806 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9808 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9809 mark = gtk_text_buffer_get_insert(buffer);
9810 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9812 if (gtk_text_iter_ends_line(&end_iter))
9813 gtk_text_iter_forward_char(&end_iter);
9815 gtk_text_iter_forward_to_line_end(&end_iter);
9816 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9819 #define DO_ACTION(name, act) { \
9820 if(!strcmp(name, a_name)) { \
9824 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
9826 const gchar *a_name = gtk_action_get_name(action);
9827 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
9828 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
9829 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
9830 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
9831 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
9832 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
9833 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
9834 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
9835 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
9836 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
9837 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
9838 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
9839 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
9840 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
9844 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
9846 Compose *compose = (Compose *)data;
9847 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9848 ComposeCallAdvancedAction action = -1;
9850 action = compose_call_advanced_action_from_path(gaction);
9853 void (*do_action) (GtkTextView *text);
9854 } action_table[] = {
9855 {textview_move_beginning_of_line},
9856 {textview_move_forward_character},
9857 {textview_move_backward_character},
9858 {textview_move_forward_word},
9859 {textview_move_backward_word},
9860 {textview_move_end_of_line},
9861 {textview_move_next_line},
9862 {textview_move_previous_line},
9863 {textview_delete_forward_character},
9864 {textview_delete_backward_character},
9865 {textview_delete_forward_word},
9866 {textview_delete_backward_word},
9867 {textview_delete_line},
9868 {textview_delete_to_line_end}
9871 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
9873 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
9874 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
9875 if (action_table[action].do_action)
9876 action_table[action].do_action(text);
9878 g_warning("Not implemented yet.");
9882 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
9886 if (GTK_IS_EDITABLE(widget)) {
9887 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
9888 gtk_editable_set_position(GTK_EDITABLE(widget),
9891 if (widget->parent && widget->parent->parent
9892 && widget->parent->parent->parent) {
9893 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
9894 gint y = widget->allocation.y;
9895 gint height = widget->allocation.height;
9896 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
9897 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
9899 if (y < (int)shown->value) {
9900 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
9902 if (y + height > (int)shown->value + (int)shown->page_size) {
9903 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
9904 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9905 y + height - (int)shown->page_size - 1);
9907 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9908 (int)shown->upper - (int)shown->page_size - 1);
9915 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
9916 compose->focused_editable = widget;
9919 if (GTK_IS_TEXT_VIEW(widget)
9920 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
9921 g_object_ref(compose->notebook);
9922 g_object_ref(compose->edit_vbox);
9923 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9924 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9925 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
9926 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
9927 g_object_unref(compose->notebook);
9928 g_object_unref(compose->edit_vbox);
9929 g_signal_handlers_block_by_func(G_OBJECT(widget),
9930 G_CALLBACK(compose_grab_focus_cb),
9932 gtk_widget_grab_focus(widget);
9933 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9934 G_CALLBACK(compose_grab_focus_cb),
9936 } else if (!GTK_IS_TEXT_VIEW(widget)
9937 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
9938 g_object_ref(compose->notebook);
9939 g_object_ref(compose->edit_vbox);
9940 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9941 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9942 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
9943 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
9944 g_object_unref(compose->notebook);
9945 g_object_unref(compose->edit_vbox);
9946 g_signal_handlers_block_by_func(G_OBJECT(widget),
9947 G_CALLBACK(compose_grab_focus_cb),
9949 gtk_widget_grab_focus(widget);
9950 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9951 G_CALLBACK(compose_grab_focus_cb),
9957 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
9959 compose->modified = TRUE;
9960 #ifndef GENERIC_UMPC
9961 compose_set_title(compose);
9965 static void compose_wrap_cb(GtkAction *action, gpointer data)
9967 Compose *compose = (Compose *)data;
9968 compose_beautify_paragraph(compose, NULL, TRUE);
9971 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
9973 Compose *compose = (Compose *)data;
9974 compose_wrap_all_full(compose, TRUE);
9977 static void compose_find_cb(GtkAction *action, gpointer data)
9979 Compose *compose = (Compose *)data;
9981 message_search_compose(compose);
9984 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
9987 Compose *compose = (Compose *)data;
9988 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9989 if (compose->autowrap)
9990 compose_wrap_all_full(compose, TRUE);
9991 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9994 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
9996 Compose *compose = (Compose *)data;
9998 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10001 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10003 Compose *compose = (Compose *)data;
10005 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10008 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10010 g_free(compose->privacy_system);
10012 compose->privacy_system = g_strdup(account->default_privacy_system);
10013 compose_update_privacy_system_menu_item(compose, warn);
10016 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10018 Compose *compose = (Compose *)data;
10020 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10021 gtk_widget_show(compose->ruler_hbox);
10022 prefs_common.show_ruler = TRUE;
10024 gtk_widget_hide(compose->ruler_hbox);
10025 gtk_widget_queue_resize(compose->edit_vbox);
10026 prefs_common.show_ruler = FALSE;
10030 static void compose_attach_drag_received_cb (GtkWidget *widget,
10031 GdkDragContext *context,
10034 GtkSelectionData *data,
10037 gpointer user_data)
10039 Compose *compose = (Compose *)user_data;
10042 if (gdk_atom_name(data->type) &&
10043 !strcmp(gdk_atom_name(data->type), "text/uri-list")
10044 && gtk_drag_get_source_widget(context) !=
10045 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10046 list = uri_list_extract_filenames((const gchar *)data->data);
10047 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10048 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10049 compose_attach_append
10050 (compose, (const gchar *)tmp->data,
10051 utf8_filename, NULL);
10052 g_free(utf8_filename);
10054 if (list) compose_changed_cb(NULL, compose);
10055 list_free_strings(list);
10057 } else if (gtk_drag_get_source_widget(context)
10058 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10059 /* comes from our summaryview */
10060 SummaryView * summaryview = NULL;
10061 GSList * list = NULL, *cur = NULL;
10063 if (mainwindow_get_mainwindow())
10064 summaryview = mainwindow_get_mainwindow()->summaryview;
10067 list = summary_get_selected_msg_list(summaryview);
10069 for (cur = list; cur; cur = cur->next) {
10070 MsgInfo *msginfo = (MsgInfo *)cur->data;
10071 gchar *file = NULL;
10073 file = procmsg_get_message_file_full(msginfo,
10076 compose_attach_append(compose, (const gchar *)file,
10077 (const gchar *)file, "message/rfc822");
10081 g_slist_free(list);
10085 static gboolean compose_drag_drop(GtkWidget *widget,
10086 GdkDragContext *drag_context,
10088 guint time, gpointer user_data)
10090 /* not handling this signal makes compose_insert_drag_received_cb
10095 static void compose_insert_drag_received_cb (GtkWidget *widget,
10096 GdkDragContext *drag_context,
10099 GtkSelectionData *data,
10102 gpointer user_data)
10104 Compose *compose = (Compose *)user_data;
10107 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10109 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10110 AlertValue val = G_ALERTDEFAULT;
10112 list = uri_list_extract_filenames((const gchar *)data->data);
10114 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10115 /* Assume a list of no files, and data has ://, is a remote link */
10116 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10117 gchar *tmpfile = get_tmp_file();
10118 str_write_to_file(tmpdata, tmpfile);
10120 compose_insert_file(compose, tmpfile);
10121 claws_unlink(tmpfile);
10123 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10124 compose_beautify_paragraph(compose, NULL, TRUE);
10127 switch (prefs_common.compose_dnd_mode) {
10128 case COMPOSE_DND_ASK:
10129 val = alertpanel_full(_("Insert or attach?"),
10130 _("Do you want to insert the contents of the file(s) "
10131 "into the message body, or attach it to the email?"),
10132 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10133 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10135 case COMPOSE_DND_INSERT:
10136 val = G_ALERTALTERNATE;
10138 case COMPOSE_DND_ATTACH:
10139 val = G_ALERTOTHER;
10142 /* unexpected case */
10143 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10146 if (val & G_ALERTDISABLE) {
10147 val &= ~G_ALERTDISABLE;
10148 /* remember what action to perform by default, only if we don't click Cancel */
10149 if (val == G_ALERTALTERNATE)
10150 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10151 else if (val == G_ALERTOTHER)
10152 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10155 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10156 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10157 list_free_strings(list);
10160 } else if (val == G_ALERTOTHER) {
10161 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10162 list_free_strings(list);
10167 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10168 compose_insert_file(compose, (const gchar *)tmp->data);
10170 list_free_strings(list);
10172 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10175 #if GTK_CHECK_VERSION(2, 8, 0)
10176 /* do nothing, handled by GTK */
10178 gchar *tmpfile = get_tmp_file();
10179 str_write_to_file((const gchar *)data->data, tmpfile);
10180 compose_insert_file(compose, tmpfile);
10181 claws_unlink(tmpfile);
10183 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10187 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10190 static void compose_header_drag_received_cb (GtkWidget *widget,
10191 GdkDragContext *drag_context,
10194 GtkSelectionData *data,
10197 gpointer user_data)
10199 GtkEditable *entry = (GtkEditable *)user_data;
10200 gchar *email = (gchar *)data->data;
10202 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10205 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10206 gchar *decoded=g_new(gchar, strlen(email));
10209 email += strlen("mailto:");
10210 decode_uri(decoded, email); /* will fit */
10211 gtk_editable_delete_text(entry, 0, -1);
10212 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10213 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10217 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10220 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10222 Compose *compose = (Compose *)data;
10224 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10225 compose->return_receipt = TRUE;
10227 compose->return_receipt = FALSE;
10230 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10232 Compose *compose = (Compose *)data;
10234 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10235 compose->remove_references = TRUE;
10237 compose->remove_references = FALSE;
10240 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10241 GdkEventKey *event,
10242 ComposeHeaderEntry *headerentry)
10244 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10245 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10246 !(event->state & GDK_MODIFIER_MASK) &&
10247 (event->keyval == GDK_BackSpace) &&
10248 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10249 gtk_container_remove
10250 (GTK_CONTAINER(headerentry->compose->header_table),
10251 headerentry->combo);
10252 gtk_container_remove
10253 (GTK_CONTAINER(headerentry->compose->header_table),
10254 headerentry->entry);
10255 headerentry->compose->header_list =
10256 g_slist_remove(headerentry->compose->header_list,
10258 g_free(headerentry);
10259 } else if (event->keyval == GDK_Tab) {
10260 if (headerentry->compose->header_last == headerentry) {
10261 /* Override default next focus, and give it to subject_entry
10262 * instead of notebook tabs
10264 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10265 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10272 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10273 ComposeHeaderEntry *headerentry)
10275 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10276 compose_create_header_entry(headerentry->compose);
10277 g_signal_handlers_disconnect_matched
10278 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10279 0, 0, NULL, NULL, headerentry);
10281 /* Automatically scroll down */
10282 compose_show_first_last_header(headerentry->compose, FALSE);
10288 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10290 GtkAdjustment *vadj;
10292 g_return_if_fail(compose);
10293 g_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10294 g_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10296 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10297 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10298 gtk_adjustment_changed(vadj);
10301 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10302 const gchar *text, gint len, Compose *compose)
10304 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10305 (G_OBJECT(compose->text), "paste_as_quotation"));
10308 g_return_if_fail(text != NULL);
10310 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10311 G_CALLBACK(text_inserted),
10313 if (paste_as_quotation) {
10315 const gchar *qmark;
10317 GtkTextIter start_iter;
10320 len = strlen(text);
10322 new_text = g_strndup(text, len);
10324 qmark = compose_quote_char_from_context(compose);
10326 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10327 gtk_text_buffer_place_cursor(buffer, iter);
10329 pos = gtk_text_iter_get_offset(iter);
10331 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10332 _("Quote format error at line %d."));
10333 quote_fmt_reset_vartable();
10335 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10336 GINT_TO_POINTER(paste_as_quotation - 1));
10338 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10339 gtk_text_buffer_place_cursor(buffer, iter);
10340 gtk_text_buffer_delete_mark(buffer, mark);
10342 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10343 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10344 compose_beautify_paragraph(compose, &start_iter, FALSE);
10345 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10346 gtk_text_buffer_delete_mark(buffer, mark);
10348 if (strcmp(text, "\n") || compose->automatic_break
10349 || gtk_text_iter_starts_line(iter)) {
10350 GtkTextIter before_ins;
10351 gtk_text_buffer_insert(buffer, iter, text, len);
10352 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10353 before_ins = *iter;
10354 gtk_text_iter_backward_chars(&before_ins, len);
10355 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10358 /* check if the preceding is just whitespace or quote */
10359 GtkTextIter start_line;
10360 gchar *tmp = NULL, *quote = NULL;
10361 gint quote_len = 0, is_normal = 0;
10362 start_line = *iter;
10363 gtk_text_iter_set_line_offset(&start_line, 0);
10364 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10367 if (*tmp == '\0') {
10370 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10378 gtk_text_buffer_insert(buffer, iter, text, len);
10380 gtk_text_buffer_insert_with_tags_by_name(buffer,
10381 iter, text, len, "no_join", NULL);
10386 if (!paste_as_quotation) {
10387 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10388 compose_beautify_paragraph(compose, iter, FALSE);
10389 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10390 gtk_text_buffer_delete_mark(buffer, mark);
10393 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10394 G_CALLBACK(text_inserted),
10396 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10398 if (prefs_common.autosave &&
10399 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10400 compose->draft_timeout_tag != -2 /* disabled while loading */)
10401 compose->draft_timeout_tag = g_timeout_add
10402 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10404 static gint compose_defer_auto_save_draft(Compose *compose)
10406 compose->draft_timeout_tag = -1;
10407 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10412 static void compose_check_all(GtkAction *action, gpointer data)
10414 Compose *compose = (Compose *)data;
10415 if (compose->gtkaspell)
10416 gtkaspell_check_all(compose->gtkaspell);
10419 static void compose_highlight_all(GtkAction *action, gpointer data)
10421 Compose *compose = (Compose *)data;
10422 if (compose->gtkaspell)
10423 gtkaspell_highlight_all(compose->gtkaspell);
10426 static void compose_check_backwards(GtkAction *action, gpointer data)
10428 Compose *compose = (Compose *)data;
10429 if (compose->gtkaspell)
10430 gtkaspell_check_backwards(compose->gtkaspell);
10432 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10436 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10438 Compose *compose = (Compose *)data;
10439 if (compose->gtkaspell)
10440 gtkaspell_check_forwards_go(compose->gtkaspell);
10442 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10448 *\brief Guess originating forward account from MsgInfo and several
10449 * "common preference" settings. Return NULL if no guess.
10451 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10453 PrefsAccount *account = NULL;
10455 g_return_val_if_fail(msginfo, NULL);
10456 g_return_val_if_fail(msginfo->folder, NULL);
10457 g_return_val_if_fail(msginfo->folder->prefs, NULL);
10459 if (msginfo->folder->prefs->enable_default_account)
10460 account = account_find_from_id(msginfo->folder->prefs->default_account);
10463 account = msginfo->folder->folder->account;
10465 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10467 Xstrdup_a(to, msginfo->to, return NULL);
10468 extract_address(to);
10469 account = account_find_from_address(to, FALSE);
10472 if (!account && prefs_common.forward_account_autosel) {
10473 gchar cc[BUFFSIZE];
10474 if (!procheader_get_header_from_msginfo
10475 (msginfo, cc,sizeof cc , "Cc:")) {
10476 gchar *buf = cc + strlen("Cc:");
10477 extract_address(buf);
10478 account = account_find_from_address(buf, FALSE);
10482 if (!account && prefs_common.forward_account_autosel) {
10483 gchar deliveredto[BUFFSIZE];
10484 if (!procheader_get_header_from_msginfo
10485 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10486 gchar *buf = deliveredto + strlen("Delivered-To:");
10487 extract_address(buf);
10488 account = account_find_from_address(buf, FALSE);
10495 gboolean compose_close(Compose *compose)
10499 if (!g_mutex_trylock(compose->mutex)) {
10500 /* we have to wait for the (possibly deferred by auto-save)
10501 * drafting to be done, before destroying the compose under
10503 debug_print("waiting for drafting to finish...\n");
10504 compose_allow_user_actions(compose, FALSE);
10505 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10508 g_return_val_if_fail(compose, FALSE);
10509 gtkut_widget_get_uposition(compose->window, &x, &y);
10510 prefs_common.compose_x = x;
10511 prefs_common.compose_y = y;
10512 g_mutex_unlock(compose->mutex);
10513 compose_destroy(compose);
10518 * Add entry field for each address in list.
10519 * \param compose E-Mail composition object.
10520 * \param listAddress List of (formatted) E-Mail addresses.
10522 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10525 node = listAddress;
10527 addr = ( gchar * ) node->data;
10528 compose_entry_append( compose, addr, COMPOSE_TO );
10529 node = g_list_next( node );
10533 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10534 guint action, gboolean opening_multiple)
10536 gchar *body = NULL;
10537 GSList *new_msglist = NULL;
10538 MsgInfo *tmp_msginfo = NULL;
10539 gboolean originally_enc = FALSE;
10540 Compose *compose = NULL;
10542 g_return_if_fail(msgview != NULL);
10544 g_return_if_fail(msginfo_list != NULL);
10546 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10547 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10548 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10550 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10551 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10552 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10553 orig_msginfo, mimeinfo);
10554 if (tmp_msginfo != NULL) {
10555 new_msglist = g_slist_append(NULL, tmp_msginfo);
10557 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10558 tmp_msginfo->folder = orig_msginfo->folder;
10559 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10560 if (orig_msginfo->tags)
10561 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10566 if (!opening_multiple)
10567 body = messageview_get_selection(msgview);
10570 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10571 procmsg_msginfo_free(tmp_msginfo);
10572 g_slist_free(new_msglist);
10574 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10576 if (compose && originally_enc) {
10577 compose_force_encryption(compose, compose->account, FALSE);
10583 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10586 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
10587 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10588 GSList *cur = msginfo_list;
10589 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10590 "messages. Opening the windows "
10591 "could take some time. Do you "
10592 "want to continue?"),
10593 g_slist_length(msginfo_list));
10594 if (g_slist_length(msginfo_list) > 9
10595 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10596 != G_ALERTALTERNATE) {
10601 /* We'll open multiple compose windows */
10602 /* let the WM place the next windows */
10603 compose_force_window_origin = FALSE;
10604 for (; cur; cur = cur->next) {
10606 tmplist.data = cur->data;
10607 tmplist.next = NULL;
10608 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10610 compose_force_window_origin = TRUE;
10612 /* forwarding multiple mails as attachments is done via a
10613 * single compose window */
10614 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10618 void compose_set_position(Compose *compose, gint pos)
10620 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10622 gtkut_text_view_set_position(text, pos);
10625 gboolean compose_search_string(Compose *compose,
10626 const gchar *str, gboolean case_sens)
10628 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10630 return gtkut_text_view_search_string(text, str, case_sens);
10633 gboolean compose_search_string_backward(Compose *compose,
10634 const gchar *str, gboolean case_sens)
10636 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10638 return gtkut_text_view_search_string_backward(text, str, case_sens);
10641 /* allocate a msginfo structure and populate its data from a compose data structure */
10642 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10644 MsgInfo *newmsginfo;
10646 gchar buf[BUFFSIZE];
10648 g_return_val_if_fail( compose != NULL, NULL );
10650 newmsginfo = procmsg_msginfo_new();
10653 get_rfc822_date(buf, sizeof(buf));
10654 newmsginfo->date = g_strdup(buf);
10657 if (compose->from_name) {
10658 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10659 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10663 if (compose->subject_entry)
10664 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10666 /* to, cc, reply-to, newsgroups */
10667 for (list = compose->header_list; list; list = list->next) {
10668 gchar *header = gtk_editable_get_chars(
10670 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
10671 gchar *entry = gtk_editable_get_chars(
10672 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10674 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10675 if ( newmsginfo->to == NULL ) {
10676 newmsginfo->to = g_strdup(entry);
10677 } else if (entry && *entry) {
10678 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10679 g_free(newmsginfo->to);
10680 newmsginfo->to = tmp;
10683 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10684 if ( newmsginfo->cc == NULL ) {
10685 newmsginfo->cc = g_strdup(entry);
10686 } else if (entry && *entry) {
10687 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10688 g_free(newmsginfo->cc);
10689 newmsginfo->cc = tmp;
10692 if ( strcasecmp(header,
10693 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10694 if ( newmsginfo->newsgroups == NULL ) {
10695 newmsginfo->newsgroups = g_strdup(entry);
10696 } else if (entry && *entry) {
10697 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10698 g_free(newmsginfo->newsgroups);
10699 newmsginfo->newsgroups = tmp;
10707 /* other data is unset */
10713 /* update compose's dictionaries from folder dict settings */
10714 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10715 FolderItem *folder_item)
10717 g_return_if_fail(compose != NULL);
10719 if (compose->gtkaspell && folder_item && folder_item->prefs) {
10720 FolderItemPrefs *prefs = folder_item->prefs;
10722 if (prefs->enable_default_dictionary)
10723 gtkaspell_change_dict(compose->gtkaspell,
10724 prefs->default_dictionary, FALSE);
10725 if (folder_item->prefs->enable_default_alt_dictionary)
10726 gtkaspell_change_alt_dict(compose->gtkaspell,
10727 prefs->default_alt_dictionary);
10728 if (prefs->enable_default_dictionary
10729 || prefs->enable_default_alt_dictionary)
10730 compose_spell_menu_changed(compose);