2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2008 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 ComposeInsertResult compose_insert_file (Compose *compose,
244 static gboolean compose_attach_append (Compose *compose,
247 const gchar *content_type);
248 static void compose_attach_parts (Compose *compose,
251 static gboolean compose_beautify_paragraph (Compose *compose,
252 GtkTextIter *par_iter,
254 static void compose_wrap_all (Compose *compose);
255 static void compose_wrap_all_full (Compose *compose,
258 static void compose_set_title (Compose *compose);
259 static void compose_select_account (Compose *compose,
260 PrefsAccount *account,
263 static PrefsAccount *compose_current_mail_account(void);
264 /* static gint compose_send (Compose *compose); */
265 static gboolean compose_check_for_valid_recipient
267 static gboolean compose_check_entries (Compose *compose,
268 gboolean check_everything);
269 static gint compose_write_to_file (Compose *compose,
272 gboolean attach_parts);
273 static gint compose_write_body_to_file (Compose *compose,
275 static gint compose_remove_reedit_target (Compose *compose,
277 static void compose_remove_draft (Compose *compose);
278 static gint compose_queue_sub (Compose *compose,
282 gboolean check_subject,
283 gboolean remove_reedit_target);
284 static void compose_add_attachments (Compose *compose,
286 static gchar *compose_get_header (Compose *compose);
288 static void compose_convert_header (Compose *compose,
293 gboolean addr_field);
295 static void compose_attach_info_free (AttachInfo *ainfo);
296 static void compose_attach_remove_selected (GtkAction *action,
299 static void compose_attach_property (GtkAction *action,
301 static void compose_attach_property_create (gboolean *cancelled);
302 static void attach_property_ok (GtkWidget *widget,
303 gboolean *cancelled);
304 static void attach_property_cancel (GtkWidget *widget,
305 gboolean *cancelled);
306 static gint attach_property_delete_event (GtkWidget *widget,
308 gboolean *cancelled);
309 static gboolean attach_property_key_pressed (GtkWidget *widget,
311 gboolean *cancelled);
313 static void compose_exec_ext_editor (Compose *compose);
315 static gint compose_exec_ext_editor_real (const gchar *file);
316 static gboolean compose_ext_editor_kill (Compose *compose);
317 static gboolean compose_input_cb (GIOChannel *source,
318 GIOCondition condition,
320 static void compose_set_ext_editor_sensitive (Compose *compose,
322 #endif /* G_OS_UNIX */
324 static void compose_undo_state_changed (UndoMain *undostruct,
329 static void compose_create_header_entry (Compose *compose);
330 static void compose_add_header_entry (Compose *compose, const gchar *header, gchar *text);
331 static void compose_remove_header_entries(Compose *compose);
333 static void compose_update_priority_menu_item(Compose * compose);
335 static void compose_spell_menu_changed (void *data);
337 static void compose_add_field_list ( Compose *compose,
338 GList *listAddress );
340 /* callback functions */
342 static gboolean compose_edit_size_alloc (GtkEditable *widget,
343 GtkAllocation *allocation,
344 GtkSHRuler *shruler);
345 static void account_activated (GtkComboBox *optmenu,
347 static void attach_selected (GtkTreeView *tree_view,
348 GtkTreePath *tree_path,
349 GtkTreeViewColumn *column,
351 static gboolean attach_button_pressed (GtkWidget *widget,
352 GdkEventButton *event,
354 static gboolean attach_key_pressed (GtkWidget *widget,
357 static void compose_send_cb (GtkAction *action, gpointer data);
358 static void compose_send_later_cb (GtkAction *action, gpointer data);
360 static void compose_save_cb (GtkAction *action,
363 static void compose_attach_cb (GtkAction *action,
365 static void compose_insert_file_cb (GtkAction *action,
367 static void compose_insert_sig_cb (GtkAction *action,
370 static void compose_close_cb (GtkAction *action,
373 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
375 static void compose_address_cb (GtkAction *action,
377 static void about_show_cb (GtkAction *action,
379 static void compose_template_activate_cb(GtkWidget *widget,
382 static void compose_ext_editor_cb (GtkAction *action,
385 static gint compose_delete_cb (GtkWidget *widget,
389 static void compose_undo_cb (GtkAction *action,
391 static void compose_redo_cb (GtkAction *action,
393 static void compose_cut_cb (GtkAction *action,
395 static void compose_copy_cb (GtkAction *action,
397 static void compose_paste_cb (GtkAction *action,
399 static void compose_paste_as_quote_cb (GtkAction *action,
401 static void compose_paste_no_wrap_cb (GtkAction *action,
403 static void compose_paste_wrap_cb (GtkAction *action,
405 static void compose_allsel_cb (GtkAction *action,
408 static void compose_advanced_action_cb (GtkAction *action,
411 static void compose_grab_focus_cb (GtkWidget *widget,
414 static void compose_changed_cb (GtkTextBuffer *textbuf,
417 static void compose_wrap_cb (GtkAction *action,
419 static void compose_wrap_all_cb (GtkAction *action,
421 static void compose_find_cb (GtkAction *action,
423 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
425 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
428 static void compose_toggle_ruler_cb (GtkToggleAction *action,
430 static void compose_toggle_sign_cb (GtkToggleAction *action,
432 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
434 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
435 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
436 static void activate_privacy_system (Compose *compose,
437 PrefsAccount *account,
439 static void compose_use_signing(Compose *compose, gboolean use_signing);
440 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
441 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
443 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
445 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
446 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
447 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
449 static void compose_attach_drag_received_cb (GtkWidget *widget,
450 GdkDragContext *drag_context,
453 GtkSelectionData *data,
457 static void compose_insert_drag_received_cb (GtkWidget *widget,
458 GdkDragContext *drag_context,
461 GtkSelectionData *data,
465 static void compose_header_drag_received_cb (GtkWidget *widget,
466 GdkDragContext *drag_context,
469 GtkSelectionData *data,
474 static gboolean compose_drag_drop (GtkWidget *widget,
475 GdkDragContext *drag_context,
477 guint time, gpointer user_data);
479 static void text_inserted (GtkTextBuffer *buffer,
484 static Compose *compose_generic_reply(MsgInfo *msginfo,
485 ComposeQuoteMode quote_mode,
489 gboolean followup_and_reply_to,
492 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
493 ComposeHeaderEntry *headerentry);
494 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
496 ComposeHeaderEntry *headerentry);
498 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
500 static void compose_allow_user_actions (Compose *compose, gboolean allow);
502 static void compose_nothing_cb (GtkAction *action, gpointer data)
508 static void compose_check_all (GtkAction *action, gpointer data);
509 static void compose_highlight_all (GtkAction *action, gpointer data);
510 static void compose_check_backwards (GtkAction *action, gpointer data);
511 static void compose_check_forwards_go (GtkAction *action, gpointer data);
514 static gint compose_defer_auto_save_draft (Compose *compose);
515 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
517 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
520 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
521 FolderItem *folder_item);
523 static void compose_attach_update_label(Compose *compose);
525 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data);
527 static GtkActionEntry compose_popup_entries[] =
529 {"Compose", NULL, "Compose" },
530 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
531 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
532 {"Compose/---", NULL, "---", NULL, NULL, NULL },
533 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
536 static GtkActionEntry compose_entries[] =
538 {"Menu", NULL, "Menu" },
540 {"Message", NULL, N_("_Message") },
541 {"Edit", NULL, N_("_Edit") },
543 {"Spelling", NULL, N_("_Spelling") },
545 {"Options", NULL, N_("_Options") },
546 {"Tools", NULL, N_("_Tools") },
547 {"Help", NULL, N_("_Help") },
549 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
550 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
551 {"Message/---", NULL, "---" },
553 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
554 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
555 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
556 /* {"Message/---", NULL, "---" }, */
557 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
558 /* {"Message/---", NULL, "---" }, */
559 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
562 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
563 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
564 {"Edit/---", NULL, "---" },
566 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
567 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
568 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
570 {"Edit/SpecialPaste", NULL, N_("Special paste") },
571 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
572 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
573 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
575 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
577 {"Edit/Advanced", NULL, N_("A_dvanced") },
578 {"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*/
579 {"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*/
580 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
581 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
582 {"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*/
583 {"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*/
584 {"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*/
585 {"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*/
586 {"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*/
587 {"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*/
588 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
589 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
590 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
591 {"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*/
593 /* {"Edit/---", NULL, "---" }, */
594 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
596 /* {"Edit/---", NULL, "---" }, */
597 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
598 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
599 /* {"Edit/---", NULL, "---" }, */
600 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
603 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
604 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
605 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
606 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
608 {"Spelling/---", NULL, "---" },
609 {"Spelling/Options", NULL, N_("_Options") },
614 {"Options/ReplyMode", NULL, N_("Reply _mode") },
615 {"Options/---", NULL, "---" },
616 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
617 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
619 /* {"Options/---", NULL, "---" }, */
621 {"Options/Priority", NULL, N_("_Priority") },
623 {"Options/Encoding", NULL, N_("Character _encoding") },
624 {"Options/Encoding/---", NULL, "---" },
625 #define ENC_ACTION(cs_char,c_char,string) \
626 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
628 {"Options/Encoding/Western", NULL, N_("Western European") },
629 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
630 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
631 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
632 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
633 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
634 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
635 {"Options/Encoding/Korean", NULL, N_("Korean") },
636 {"Options/Encoding/Thai", NULL, N_("Thai") },
639 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
641 {"Tools/Template", NULL, N_("_Template") },
642 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
643 {"Tools/Actions", NULL, N_("Actio_ns") },
644 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
647 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
650 static GtkToggleActionEntry compose_toggle_entries[] =
652 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
653 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
654 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
655 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
656 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
657 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
658 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
661 static GtkRadioActionEntry compose_radio_rm_entries[] =
663 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
664 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
665 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
666 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
669 static GtkRadioActionEntry compose_radio_prio_entries[] =
671 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
672 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
673 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
674 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
675 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
678 static GtkRadioActionEntry compose_radio_enc_entries[] =
680 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
681 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
682 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
683 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
684 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
685 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
686 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
687 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
688 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
689 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
690 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
691 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
692 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
693 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
713 static GtkTargetEntry compose_mime_types[] =
715 {"text/uri-list", 0, 0},
716 {"UTF8_STRING", 0, 0},
720 static gboolean compose_put_existing_to_front(MsgInfo *info)
722 GList *compose_list = compose_get_compose_list();
726 for (elem = compose_list; elem != NULL && elem->data != NULL;
728 Compose *c = (Compose*)elem->data;
730 if (!c->targetinfo || !c->targetinfo->msgid ||
734 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
735 gtkut_window_popup(c->window);
743 static GdkColor quote_color1 =
744 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
745 static GdkColor quote_color2 =
746 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
747 static GdkColor quote_color3 =
748 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
750 static GdkColor quote_bgcolor1 =
751 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
752 static GdkColor quote_bgcolor2 =
753 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
754 static GdkColor quote_bgcolor3 =
755 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
757 static GdkColor signature_color = {
764 static GdkColor uri_color = {
771 static void compose_create_tags(GtkTextView *text, Compose *compose)
773 GtkTextBuffer *buffer;
774 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
780 buffer = gtk_text_view_get_buffer(text);
782 if (prefs_common.enable_color) {
783 /* grab the quote colors, converting from an int to a GdkColor */
784 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
786 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
788 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
790 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
792 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
794 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
796 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
798 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
801 signature_color = quote_color1 = quote_color2 = quote_color3 =
802 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
805 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
806 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
807 "foreground-gdk", "e_color1,
808 "paragraph-background-gdk", "e_bgcolor1,
810 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
811 "foreground-gdk", "e_color2,
812 "paragraph-background-gdk", "e_bgcolor2,
814 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
815 "foreground-gdk", "e_color3,
816 "paragraph-background-gdk", "e_bgcolor3,
819 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
820 "foreground-gdk", "e_color1,
822 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
823 "foreground-gdk", "e_color2,
825 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
826 "foreground-gdk", "e_color3,
830 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
831 "foreground-gdk", &signature_color,
834 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
835 "foreground-gdk", &uri_color,
837 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
838 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
840 color[0] = quote_color1;
841 color[1] = quote_color2;
842 color[2] = quote_color3;
843 color[3] = quote_bgcolor1;
844 color[4] = quote_bgcolor2;
845 color[5] = quote_bgcolor3;
846 color[6] = signature_color;
847 color[7] = uri_color;
848 cmap = gdk_drawable_get_colormap(compose->window->window);
849 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
851 for (i = 0; i < 8; i++) {
852 if (success[i] == FALSE) {
855 g_warning("Compose: color allocation failed.\n");
856 style = gtk_widget_get_style(GTK_WIDGET(text));
857 quote_color1 = quote_color2 = quote_color3 =
858 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
859 signature_color = uri_color = black;
864 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
865 GPtrArray *attach_files)
867 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
870 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
872 return compose_generic_new(account, mailto, item, NULL, NULL);
875 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
877 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
880 #define SCROLL_TO_CURSOR(compose) { \
881 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
882 gtk_text_view_get_buffer( \
883 GTK_TEXT_VIEW(compose->text))); \
884 gtk_text_view_scroll_mark_onscreen( \
885 GTK_TEXT_VIEW(compose->text), \
889 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
890 GPtrArray *attach_files, GList *listAddress )
893 GtkTextView *textview;
894 GtkTextBuffer *textbuf;
896 const gchar *subject_format = NULL;
897 const gchar *body_format = NULL;
898 gchar *mailto_from = NULL;
899 PrefsAccount *mailto_account = NULL;
900 MsgInfo* dummyinfo = NULL;
902 /* check if mailto defines a from */
903 if (mailto && *mailto != '\0') {
904 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL);
905 /* mailto defines a from, check if we can get account prefs from it,
906 if not, the account prefs will be guessed using other ways, but we'll keep
909 mailto_account = account_find_from_address(mailto_from, TRUE);
911 account = mailto_account;
914 /* if no account prefs set from mailto, set if from folder prefs (if any) */
915 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
916 account = account_find_from_id(item->prefs->default_account);
918 /* if no account prefs set, fallback to the current one */
919 if (!account) account = cur_account;
920 g_return_val_if_fail(account != NULL, NULL);
922 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
924 /* override from name if mailto asked for it */
926 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
929 /* override from name according to folder properties */
930 if (item && item->prefs &&
931 item->prefs->compose_with_format &&
932 item->prefs->compose_override_from_format &&
933 *item->prefs->compose_override_from_format != '\0') {
938 dummyinfo = compose_msginfo_new_from_compose(compose);
940 /* decode \-escape sequences in the internal representation of the quote format */
941 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
942 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
945 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
948 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
950 quote_fmt_scan_string(tmp);
953 buf = quote_fmt_get_buffer();
955 alertpanel_error(_("New message From format error."));
957 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
958 quote_fmt_reset_vartable();
963 compose->replyinfo = NULL;
964 compose->fwdinfo = NULL;
966 textview = GTK_TEXT_VIEW(compose->text);
967 textbuf = gtk_text_view_get_buffer(textview);
968 compose_create_tags(textview, compose);
970 undo_block(compose->undostruct);
972 compose_set_dictionaries_from_folder_prefs(compose, item);
975 if (account->auto_sig)
976 compose_insert_sig(compose, FALSE);
977 gtk_text_buffer_get_start_iter(textbuf, &iter);
978 gtk_text_buffer_place_cursor(textbuf, &iter);
980 if (account->protocol != A_NNTP) {
981 if (mailto && *mailto != '\0') {
982 compose_entries_set(compose, mailto, COMPOSE_TO);
984 } else if (item && item->prefs->enable_default_to) {
985 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
986 compose_entry_mark_default_to(compose, item->prefs->default_to);
988 if (item && item->ret_rcpt) {
989 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
992 if (mailto && *mailto != '\0') {
993 if (!strchr(mailto, '@'))
994 compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
996 compose_entries_set(compose, mailto, COMPOSE_TO);
997 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
998 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1001 * CLAWS: just don't allow return receipt request, even if the user
1002 * may want to send an email. simple but foolproof.
1004 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1006 compose_add_field_list( compose, listAddress );
1008 if (item && item->prefs && item->prefs->compose_with_format) {
1009 subject_format = item->prefs->compose_subject_format;
1010 body_format = item->prefs->compose_body_format;
1011 } else if (account->compose_with_format) {
1012 subject_format = account->compose_subject_format;
1013 body_format = account->compose_body_format;
1014 } else if (prefs_common.compose_with_format) {
1015 subject_format = prefs_common.compose_subject_format;
1016 body_format = prefs_common.compose_body_format;
1019 if (subject_format || body_format) {
1022 && *subject_format != '\0' )
1024 gchar *subject = NULL;
1029 dummyinfo = compose_msginfo_new_from_compose(compose);
1031 /* decode \-escape sequences in the internal representation of the quote format */
1032 tmp = malloc(strlen(subject_format)+1);
1033 pref_get_unescaped_pref(tmp, subject_format);
1035 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1037 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1038 compose->gtkaspell);
1040 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1042 quote_fmt_scan_string(tmp);
1045 buf = quote_fmt_get_buffer();
1047 alertpanel_error(_("New message subject format error."));
1049 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1050 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1051 quote_fmt_reset_vartable();
1058 && *body_format != '\0' )
1061 GtkTextBuffer *buffer;
1062 GtkTextIter start, end;
1066 dummyinfo = compose_msginfo_new_from_compose(compose);
1068 text = GTK_TEXT_VIEW(compose->text);
1069 buffer = gtk_text_view_get_buffer(text);
1070 gtk_text_buffer_get_start_iter(buffer, &start);
1071 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1072 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1074 compose_quote_fmt(compose, dummyinfo,
1076 NULL, tmp, FALSE, TRUE,
1077 _("New message body format error at line %d."));
1078 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1079 quote_fmt_reset_vartable();
1085 procmsg_msginfo_free( dummyinfo );
1091 for (i = 0; i < attach_files->len; i++) {
1092 file = g_ptr_array_index(attach_files, i);
1093 compose_attach_append(compose, file, file, NULL);
1097 compose_show_first_last_header(compose, TRUE);
1099 /* Set save folder */
1100 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1101 gchar *folderidentifier;
1103 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1104 folderidentifier = folder_item_get_identifier(item);
1105 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1106 g_free(folderidentifier);
1109 gtk_widget_grab_focus(compose->header_last->entry);
1111 undo_unblock(compose->undostruct);
1113 if (prefs_common.auto_exteditor)
1114 compose_exec_ext_editor(compose);
1116 compose->draft_timeout_tag = -1;
1117 SCROLL_TO_CURSOR(compose);
1119 compose->modified = FALSE;
1120 compose_set_title(compose);
1124 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1125 gboolean override_pref)
1127 gchar *privacy = NULL;
1129 g_return_if_fail(compose != NULL);
1130 g_return_if_fail(account != NULL);
1132 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1135 if (account->default_privacy_system
1136 && strlen(account->default_privacy_system)) {
1137 privacy = account->default_privacy_system;
1139 GSList *privacy_avail = privacy_get_system_ids();
1140 if (privacy_avail && g_slist_length(privacy_avail)) {
1141 privacy = (gchar *)(privacy_avail->data);
1144 if (privacy != NULL) {
1145 if (compose->privacy_system == NULL)
1146 compose->privacy_system = g_strdup(privacy);
1147 else if (*(compose->privacy_system) == '\0') {
1148 g_free(compose->privacy_system);
1149 compose->privacy_system = g_strdup(privacy);
1151 compose_update_privacy_system_menu_item(compose, FALSE);
1152 compose_use_encryption(compose, TRUE);
1156 static void compose_force_signing(Compose *compose, PrefsAccount *account)
1158 gchar *privacy = NULL;
1160 if (account->default_privacy_system
1161 && strlen(account->default_privacy_system)) {
1162 privacy = account->default_privacy_system;
1164 GSList *privacy_avail = privacy_get_system_ids();
1165 if (privacy_avail && g_slist_length(privacy_avail)) {
1166 privacy = (gchar *)(privacy_avail->data);
1169 if (privacy != NULL) {
1170 if (compose->privacy_system == NULL)
1171 compose->privacy_system = g_strdup(privacy);
1172 compose_update_privacy_system_menu_item(compose, FALSE);
1173 compose_use_signing(compose, TRUE);
1177 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1181 Compose *compose = NULL;
1183 g_return_val_if_fail(msginfo_list != NULL, NULL);
1185 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1186 g_return_val_if_fail(msginfo != NULL, NULL);
1188 list_len = g_slist_length(msginfo_list);
1192 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1193 FALSE, prefs_common.default_reply_list, FALSE, body);
1195 case COMPOSE_REPLY_WITH_QUOTE:
1196 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1197 FALSE, prefs_common.default_reply_list, FALSE, body);
1199 case COMPOSE_REPLY_WITHOUT_QUOTE:
1200 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1201 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1203 case COMPOSE_REPLY_TO_SENDER:
1204 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1205 FALSE, FALSE, TRUE, body);
1207 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1208 compose = compose_followup_and_reply_to(msginfo,
1209 COMPOSE_QUOTE_CHECK,
1210 FALSE, FALSE, body);
1212 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1213 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1214 FALSE, FALSE, TRUE, body);
1216 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1217 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1218 FALSE, FALSE, TRUE, NULL);
1220 case COMPOSE_REPLY_TO_ALL:
1221 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1222 TRUE, FALSE, FALSE, body);
1224 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1225 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1226 TRUE, FALSE, FALSE, body);
1228 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1229 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1230 TRUE, FALSE, FALSE, NULL);
1232 case COMPOSE_REPLY_TO_LIST:
1233 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1234 FALSE, TRUE, FALSE, body);
1236 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1237 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1238 FALSE, TRUE, FALSE, body);
1240 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1241 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1242 FALSE, TRUE, FALSE, NULL);
1244 case COMPOSE_FORWARD:
1245 if (prefs_common.forward_as_attachment) {
1246 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1249 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1253 case COMPOSE_FORWARD_INLINE:
1254 /* check if we reply to more than one Message */
1255 if (list_len == 1) {
1256 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1259 /* more messages FALL THROUGH */
1260 case COMPOSE_FORWARD_AS_ATTACH:
1261 compose = compose_forward_multiple(NULL, msginfo_list);
1263 case COMPOSE_REDIRECT:
1264 compose = compose_redirect(NULL, msginfo, FALSE);
1267 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1270 if (compose == NULL) {
1271 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1275 compose->rmode = mode;
1276 switch (compose->rmode) {
1278 case COMPOSE_REPLY_WITH_QUOTE:
1279 case COMPOSE_REPLY_WITHOUT_QUOTE:
1280 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1281 debug_print("reply mode Normal\n");
1282 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1283 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1285 case COMPOSE_REPLY_TO_SENDER:
1286 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1287 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1288 debug_print("reply mode Sender\n");
1289 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1291 case COMPOSE_REPLY_TO_ALL:
1292 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1293 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1294 debug_print("reply mode All\n");
1295 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1297 case COMPOSE_REPLY_TO_LIST:
1298 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1299 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1300 debug_print("reply mode List\n");
1301 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1309 static Compose *compose_reply(MsgInfo *msginfo,
1310 ComposeQuoteMode quote_mode,
1316 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1317 to_sender, FALSE, body);
1320 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1321 ComposeQuoteMode quote_mode,
1326 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1327 to_sender, TRUE, body);
1330 static void compose_extract_original_charset(Compose *compose)
1332 MsgInfo *info = NULL;
1333 if (compose->replyinfo) {
1334 info = compose->replyinfo;
1335 } else if (compose->fwdinfo) {
1336 info = compose->fwdinfo;
1337 } else if (compose->targetinfo) {
1338 info = compose->targetinfo;
1341 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1342 MimeInfo *partinfo = mimeinfo;
1343 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1344 partinfo = procmime_mimeinfo_next(partinfo);
1346 compose->orig_charset =
1347 g_strdup(procmime_mimeinfo_get_parameter(
1348 partinfo, "charset"));
1350 procmime_mimeinfo_free_all(mimeinfo);
1354 #define SIGNAL_BLOCK(buffer) { \
1355 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1356 G_CALLBACK(compose_changed_cb), \
1358 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1359 G_CALLBACK(text_inserted), \
1363 #define SIGNAL_UNBLOCK(buffer) { \
1364 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1365 G_CALLBACK(compose_changed_cb), \
1367 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1368 G_CALLBACK(text_inserted), \
1372 static Compose *compose_generic_reply(MsgInfo *msginfo,
1373 ComposeQuoteMode quote_mode,
1374 gboolean to_all, gboolean to_ml,
1376 gboolean followup_and_reply_to,
1380 PrefsAccount *account = NULL;
1381 GtkTextView *textview;
1382 GtkTextBuffer *textbuf;
1383 gboolean quote = FALSE;
1384 const gchar *qmark = NULL;
1385 const gchar *body_fmt = NULL;
1387 g_return_val_if_fail(msginfo != NULL, NULL);
1388 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1390 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1392 g_return_val_if_fail(account != NULL, NULL);
1394 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1396 compose->updating = TRUE;
1398 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1399 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1401 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1402 if (!compose->replyinfo)
1403 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1405 compose_extract_original_charset(compose);
1407 if (msginfo->folder && msginfo->folder->ret_rcpt)
1408 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1410 /* Set save folder */
1411 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1412 gchar *folderidentifier;
1414 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1415 folderidentifier = folder_item_get_identifier(msginfo->folder);
1416 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1417 g_free(folderidentifier);
1420 if (compose_parse_header(compose, msginfo) < 0) {
1421 compose->updating = FALSE;
1422 compose_destroy(compose);
1426 /* override from name according to folder properties */
1427 if (msginfo->folder && msginfo->folder->prefs &&
1428 msginfo->folder->prefs->reply_with_format &&
1429 msginfo->folder->prefs->reply_override_from_format &&
1430 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1435 /* decode \-escape sequences in the internal representation of the quote format */
1436 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1437 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1440 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1441 compose->gtkaspell);
1443 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1445 quote_fmt_scan_string(tmp);
1448 buf = quote_fmt_get_buffer();
1450 alertpanel_error(_("Message reply From format error."));
1452 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1453 quote_fmt_reset_vartable();
1458 textview = (GTK_TEXT_VIEW(compose->text));
1459 textbuf = gtk_text_view_get_buffer(textview);
1460 compose_create_tags(textview, compose);
1462 undo_block(compose->undostruct);
1464 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1467 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1468 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1469 /* use the reply format of folder (if enabled), or the account's one
1470 (if enabled) or fallback to the global reply format, which is always
1471 enabled (even if empty), and use the relevant quotemark */
1473 if (msginfo->folder && msginfo->folder->prefs &&
1474 msginfo->folder->prefs->reply_with_format) {
1475 qmark = msginfo->folder->prefs->reply_quotemark;
1476 body_fmt = msginfo->folder->prefs->reply_body_format;
1478 } else if (account->reply_with_format) {
1479 qmark = account->reply_quotemark;
1480 body_fmt = account->reply_body_format;
1483 qmark = prefs_common.quotemark;
1484 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1485 body_fmt = gettext(prefs_common.quotefmt);
1492 /* empty quotemark is not allowed */
1493 if (qmark == NULL || *qmark == '\0')
1495 compose_quote_fmt(compose, compose->replyinfo,
1496 body_fmt, qmark, body, FALSE, TRUE,
1497 _("Message reply format error at line %d."));
1498 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1499 quote_fmt_reset_vartable();
1502 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1503 compose_force_encryption(compose, account, FALSE);
1506 privacy_msginfo_get_signed_state(compose->replyinfo);
1507 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1508 compose_force_signing(compose, account);
1511 SIGNAL_BLOCK(textbuf);
1513 if (account->auto_sig)
1514 compose_insert_sig(compose, FALSE);
1516 compose_wrap_all(compose);
1518 SIGNAL_UNBLOCK(textbuf);
1520 gtk_widget_grab_focus(compose->text);
1522 undo_unblock(compose->undostruct);
1524 if (prefs_common.auto_exteditor)
1525 compose_exec_ext_editor(compose);
1527 compose->modified = FALSE;
1528 compose_set_title(compose);
1530 compose->updating = FALSE;
1531 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1532 SCROLL_TO_CURSOR(compose);
1534 if (compose->deferred_destroy) {
1535 compose_destroy(compose);
1542 #define INSERT_FW_HEADER(var, hdr) \
1543 if (msginfo->var && *msginfo->var) { \
1544 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1545 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1546 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1549 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1550 gboolean as_attach, const gchar *body,
1551 gboolean no_extedit,
1555 GtkTextView *textview;
1556 GtkTextBuffer *textbuf;
1559 g_return_val_if_fail(msginfo != NULL, NULL);
1560 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1563 !(account = compose_guess_forward_account_from_msginfo
1565 account = cur_account;
1567 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1569 compose->updating = TRUE;
1570 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1571 if (!compose->fwdinfo)
1572 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1574 compose_extract_original_charset(compose);
1576 if (msginfo->subject && *msginfo->subject) {
1577 gchar *buf, *buf2, *p;
1579 buf = p = g_strdup(msginfo->subject);
1580 p += subject_get_prefix_length(p);
1581 memmove(buf, p, strlen(p) + 1);
1583 buf2 = g_strdup_printf("Fw: %s", buf);
1584 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1590 /* override from name according to folder properties */
1591 if (msginfo->folder && msginfo->folder->prefs &&
1592 msginfo->folder->prefs->forward_with_format &&
1593 msginfo->folder->prefs->forward_override_from_format &&
1594 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1598 MsgInfo *full_msginfo = NULL;
1601 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1603 full_msginfo = procmsg_msginfo_copy(msginfo);
1605 /* decode \-escape sequences in the internal representation of the quote format */
1606 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1607 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1610 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1611 compose->gtkaspell);
1613 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1615 quote_fmt_scan_string(tmp);
1618 buf = quote_fmt_get_buffer();
1620 alertpanel_error(_("Message forward From format error."));
1622 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1623 quote_fmt_reset_vartable();
1626 procmsg_msginfo_free(full_msginfo);
1629 textview = GTK_TEXT_VIEW(compose->text);
1630 textbuf = gtk_text_view_get_buffer(textview);
1631 compose_create_tags(textview, compose);
1633 undo_block(compose->undostruct);
1637 msgfile = procmsg_get_message_file(msginfo);
1638 if (!is_file_exist(msgfile))
1639 g_warning("%s: file not exist\n", msgfile);
1641 compose_attach_append(compose, msgfile, msgfile,
1646 const gchar *qmark = NULL;
1647 const gchar *body_fmt = NULL;
1648 MsgInfo *full_msginfo;
1650 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1651 body_fmt = gettext(prefs_common.fw_quotefmt);
1655 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1657 full_msginfo = procmsg_msginfo_copy(msginfo);
1659 /* use the forward format of folder (if enabled), or the account's one
1660 (if enabled) or fallback to the global forward format, which is always
1661 enabled (even if empty), and use the relevant quotemark */
1662 if (msginfo->folder && msginfo->folder->prefs &&
1663 msginfo->folder->prefs->forward_with_format) {
1664 qmark = msginfo->folder->prefs->forward_quotemark;
1665 body_fmt = msginfo->folder->prefs->forward_body_format;
1667 } else if (account->forward_with_format) {
1668 qmark = account->forward_quotemark;
1669 body_fmt = account->forward_body_format;
1672 qmark = prefs_common.fw_quotemark;
1673 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1674 body_fmt = gettext(prefs_common.fw_quotefmt);
1679 /* empty quotemark is not allowed */
1680 if (qmark == NULL || *qmark == '\0')
1683 compose_quote_fmt(compose, full_msginfo,
1684 body_fmt, qmark, body, FALSE, TRUE,
1685 _("Message forward format error at line %d."));
1686 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1687 quote_fmt_reset_vartable();
1688 compose_attach_parts(compose, msginfo);
1690 procmsg_msginfo_free(full_msginfo);
1693 SIGNAL_BLOCK(textbuf);
1695 if (account->auto_sig)
1696 compose_insert_sig(compose, FALSE);
1698 compose_wrap_all(compose);
1700 SIGNAL_UNBLOCK(textbuf);
1702 gtk_text_buffer_get_start_iter(textbuf, &iter);
1703 gtk_text_buffer_place_cursor(textbuf, &iter);
1705 gtk_widget_grab_focus(compose->header_last->entry);
1707 if (!no_extedit && prefs_common.auto_exteditor)
1708 compose_exec_ext_editor(compose);
1711 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1712 gchar *folderidentifier;
1714 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1715 folderidentifier = folder_item_get_identifier(msginfo->folder);
1716 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1717 g_free(folderidentifier);
1720 undo_unblock(compose->undostruct);
1722 compose->modified = FALSE;
1723 compose_set_title(compose);
1725 compose->updating = FALSE;
1726 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1727 SCROLL_TO_CURSOR(compose);
1729 if (compose->deferred_destroy) {
1730 compose_destroy(compose);
1737 #undef INSERT_FW_HEADER
1739 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1742 GtkTextView *textview;
1743 GtkTextBuffer *textbuf;
1747 gboolean single_mail = TRUE;
1749 g_return_val_if_fail(msginfo_list != NULL, NULL);
1751 if (g_slist_length(msginfo_list) > 1)
1752 single_mail = FALSE;
1754 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1755 if (((MsgInfo *)msginfo->data)->folder == NULL)
1758 /* guess account from first selected message */
1760 !(account = compose_guess_forward_account_from_msginfo
1761 (msginfo_list->data)))
1762 account = cur_account;
1764 g_return_val_if_fail(account != NULL, NULL);
1766 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1767 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1768 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1771 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1773 compose->updating = TRUE;
1775 /* override from name according to folder properties */
1776 if (msginfo_list->data) {
1777 MsgInfo *msginfo = msginfo_list->data;
1779 if (msginfo->folder && msginfo->folder->prefs &&
1780 msginfo->folder->prefs->forward_with_format &&
1781 msginfo->folder->prefs->forward_override_from_format &&
1782 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1787 /* decode \-escape sequences in the internal representation of the quote format */
1788 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1789 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1792 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1793 compose->gtkaspell);
1795 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1797 quote_fmt_scan_string(tmp);
1800 buf = quote_fmt_get_buffer();
1802 alertpanel_error(_("Message forward From format error."));
1804 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1805 quote_fmt_reset_vartable();
1811 textview = GTK_TEXT_VIEW(compose->text);
1812 textbuf = gtk_text_view_get_buffer(textview);
1813 compose_create_tags(textview, compose);
1815 undo_block(compose->undostruct);
1816 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1817 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1819 if (!is_file_exist(msgfile))
1820 g_warning("%s: file not exist\n", msgfile);
1822 compose_attach_append(compose, msgfile, msgfile,
1828 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1829 if (info->subject && *info->subject) {
1830 gchar *buf, *buf2, *p;
1832 buf = p = g_strdup(info->subject);
1833 p += subject_get_prefix_length(p);
1834 memmove(buf, p, strlen(p) + 1);
1836 buf2 = g_strdup_printf("Fw: %s", buf);
1837 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1843 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1844 _("Fw: multiple emails"));
1847 SIGNAL_BLOCK(textbuf);
1849 if (account->auto_sig)
1850 compose_insert_sig(compose, FALSE);
1852 compose_wrap_all(compose);
1854 SIGNAL_UNBLOCK(textbuf);
1856 gtk_text_buffer_get_start_iter(textbuf, &iter);
1857 gtk_text_buffer_place_cursor(textbuf, &iter);
1859 gtk_widget_grab_focus(compose->header_last->entry);
1860 undo_unblock(compose->undostruct);
1861 compose->modified = FALSE;
1862 compose_set_title(compose);
1864 compose->updating = FALSE;
1865 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1866 SCROLL_TO_CURSOR(compose);
1868 if (compose->deferred_destroy) {
1869 compose_destroy(compose);
1876 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1878 GtkTextIter start = *iter;
1879 GtkTextIter end_iter;
1880 int start_pos = gtk_text_iter_get_offset(&start);
1882 if (!compose->account->sig_sep)
1885 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1886 start_pos+strlen(compose->account->sig_sep));
1888 /* check sig separator */
1889 str = gtk_text_iter_get_text(&start, &end_iter);
1890 if (!strcmp(str, compose->account->sig_sep)) {
1892 /* check end of line (\n) */
1893 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1894 start_pos+strlen(compose->account->sig_sep));
1895 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1896 start_pos+strlen(compose->account->sig_sep)+1);
1897 tmp = gtk_text_iter_get_text(&start, &end_iter);
1898 if (!strcmp(tmp,"\n")) {
1910 static void compose_colorize_signature(Compose *compose)
1912 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1914 GtkTextIter end_iter;
1915 gtk_text_buffer_get_start_iter(buffer, &iter);
1916 while (gtk_text_iter_forward_line(&iter))
1917 if (compose_is_sig_separator(compose, buffer, &iter)) {
1918 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1919 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1923 #define BLOCK_WRAP() { \
1924 prev_autowrap = compose->autowrap; \
1925 buffer = gtk_text_view_get_buffer( \
1926 GTK_TEXT_VIEW(compose->text)); \
1927 compose->autowrap = FALSE; \
1929 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1930 G_CALLBACK(compose_changed_cb), \
1932 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1933 G_CALLBACK(text_inserted), \
1936 #define UNBLOCK_WRAP() { \
1937 compose->autowrap = prev_autowrap; \
1938 if (compose->autowrap) { \
1939 gint old = compose->draft_timeout_tag; \
1940 compose->draft_timeout_tag = -2; \
1941 compose_wrap_all(compose); \
1942 compose->draft_timeout_tag = old; \
1945 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1946 G_CALLBACK(compose_changed_cb), \
1948 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1949 G_CALLBACK(text_inserted), \
1953 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
1955 Compose *compose = NULL;
1956 PrefsAccount *account = NULL;
1957 GtkTextView *textview;
1958 GtkTextBuffer *textbuf;
1962 gchar buf[BUFFSIZE];
1963 gboolean use_signing = FALSE;
1964 gboolean use_encryption = FALSE;
1965 gchar *privacy_system = NULL;
1966 int priority = PRIORITY_NORMAL;
1967 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
1968 gboolean autowrap = prefs_common.autowrap;
1969 gboolean autoindent = prefs_common.auto_indent;
1971 g_return_val_if_fail(msginfo != NULL, NULL);
1972 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1974 if (compose_put_existing_to_front(msginfo)) {
1978 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
1979 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
1980 gchar queueheader_buf[BUFFSIZE];
1983 /* Select Account from queue headers */
1984 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1985 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
1986 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
1987 account = account_find_from_id(id);
1989 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1990 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
1991 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
1992 account = account_find_from_id(id);
1994 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1995 sizeof(queueheader_buf), "NAID:")) {
1996 id = atoi(&queueheader_buf[strlen("NAID:")]);
1997 account = account_find_from_id(id);
1999 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2000 sizeof(queueheader_buf), "MAID:")) {
2001 id = atoi(&queueheader_buf[strlen("MAID:")]);
2002 account = account_find_from_id(id);
2004 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2005 sizeof(queueheader_buf), "S:")) {
2006 account = account_find_from_address(queueheader_buf, FALSE);
2008 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2009 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2010 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2011 use_signing = param;
2014 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2015 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2016 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2017 use_signing = param;
2020 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2021 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2022 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2023 use_encryption = param;
2025 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2026 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2027 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2028 use_encryption = param;
2030 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2031 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2032 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2035 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2036 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2037 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2040 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2041 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2042 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2044 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2045 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2046 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2048 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2049 sizeof(queueheader_buf), "X-Priority: ")) {
2050 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2053 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2054 sizeof(queueheader_buf), "RMID:")) {
2055 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2056 if (tokens[0] && tokens[1] && tokens[2]) {
2057 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2058 if (orig_item != NULL) {
2059 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2064 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2065 sizeof(queueheader_buf), "FMID:")) {
2066 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2067 if (tokens[0] && tokens[1] && tokens[2]) {
2068 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2069 if (orig_item != NULL) {
2070 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2076 account = msginfo->folder->folder->account;
2079 if (!account && prefs_common.reedit_account_autosel) {
2080 gchar from[BUFFSIZE];
2081 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2082 extract_address(from);
2083 account = account_find_from_address(from, FALSE);
2087 account = cur_account;
2089 g_return_val_if_fail(account != NULL, NULL);
2091 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2093 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2094 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2095 compose->autowrap = autowrap;
2096 compose->replyinfo = replyinfo;
2097 compose->fwdinfo = fwdinfo;
2099 compose->updating = TRUE;
2100 compose->priority = priority;
2102 if (privacy_system != NULL) {
2103 compose->privacy_system = privacy_system;
2104 compose_use_signing(compose, use_signing);
2105 compose_use_encryption(compose, use_encryption);
2106 compose_update_privacy_system_menu_item(compose, FALSE);
2108 activate_privacy_system(compose, account, FALSE);
2111 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2113 compose_extract_original_charset(compose);
2115 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2116 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2117 gchar queueheader_buf[BUFFSIZE];
2119 /* Set message save folder */
2120 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2123 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2124 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
2125 gtk_editable_insert_text(GTK_EDITABLE(compose->savemsg_entry), &queueheader_buf[4], strlen(&queueheader_buf[4]), &startpos);
2127 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2128 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2130 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2135 if (compose_parse_header(compose, msginfo) < 0) {
2136 compose->updating = FALSE;
2137 compose_destroy(compose);
2140 compose_reedit_set_entry(compose, msginfo);
2142 textview = GTK_TEXT_VIEW(compose->text);
2143 textbuf = gtk_text_view_get_buffer(textview);
2144 compose_create_tags(textview, compose);
2146 mark = gtk_text_buffer_get_insert(textbuf);
2147 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2149 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2150 G_CALLBACK(compose_changed_cb),
2153 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2154 fp = procmime_get_first_encrypted_text_content(msginfo);
2156 compose_force_encryption(compose, account, TRUE);
2159 fp = procmime_get_first_text_content(msginfo);
2162 g_warning("Can't get text part\n");
2166 gboolean prev_autowrap = compose->autowrap;
2167 GtkTextBuffer *buffer = textbuf;
2169 while (fgets(buf, sizeof(buf), fp) != NULL) {
2171 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2177 compose_attach_parts(compose, msginfo);
2179 compose_colorize_signature(compose);
2181 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2182 G_CALLBACK(compose_changed_cb),
2185 gtk_widget_grab_focus(compose->text);
2187 if (prefs_common.auto_exteditor) {
2188 compose_exec_ext_editor(compose);
2190 compose->modified = FALSE;
2191 compose_set_title(compose);
2193 compose->updating = FALSE;
2194 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2195 SCROLL_TO_CURSOR(compose);
2197 if (compose->deferred_destroy) {
2198 compose_destroy(compose);
2202 compose->sig_str = account_get_signature_str(compose->account);
2207 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2214 g_return_val_if_fail(msginfo != NULL, NULL);
2217 account = account_get_reply_account(msginfo,
2218 prefs_common.reply_account_autosel);
2219 g_return_val_if_fail(account != NULL, NULL);
2221 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2223 compose->updating = TRUE;
2225 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2226 compose->replyinfo = NULL;
2227 compose->fwdinfo = NULL;
2229 compose_show_first_last_header(compose, TRUE);
2231 gtk_widget_grab_focus(compose->header_last->entry);
2233 filename = procmsg_get_message_file(msginfo);
2235 if (filename == NULL) {
2236 compose->updating = FALSE;
2237 compose_destroy(compose);
2242 compose->redirect_filename = filename;
2244 /* Set save folder */
2245 item = msginfo->folder;
2246 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2247 gchar *folderidentifier;
2249 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2250 folderidentifier = folder_item_get_identifier(item);
2251 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
2252 g_free(folderidentifier);
2255 compose_attach_parts(compose, msginfo);
2257 if (msginfo->subject)
2258 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2260 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2262 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2263 _("Message redirect format error at line %d."));
2264 quote_fmt_reset_vartable();
2265 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2267 compose_colorize_signature(compose);
2270 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2271 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2272 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2274 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2275 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2276 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2277 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2278 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2279 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2280 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2281 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2283 if (compose->toolbar->draft_btn)
2284 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2285 if (compose->toolbar->insert_btn)
2286 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2287 if (compose->toolbar->attach_btn)
2288 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2289 if (compose->toolbar->sig_btn)
2290 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2291 if (compose->toolbar->exteditor_btn)
2292 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2293 if (compose->toolbar->linewrap_current_btn)
2294 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2295 if (compose->toolbar->linewrap_all_btn)
2296 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2298 compose->modified = FALSE;
2299 compose_set_title(compose);
2300 compose->updating = FALSE;
2301 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2302 SCROLL_TO_CURSOR(compose);
2304 if (compose->deferred_destroy) {
2305 compose_destroy(compose);
2312 GList *compose_get_compose_list(void)
2314 return compose_list;
2317 void compose_entry_append(Compose *compose, const gchar *address,
2318 ComposeEntryType type)
2320 const gchar *header;
2322 gboolean in_quote = FALSE;
2323 if (!address || *address == '\0') return;
2330 header = N_("Bcc:");
2332 case COMPOSE_REPLYTO:
2333 header = N_("Reply-To:");
2335 case COMPOSE_NEWSGROUPS:
2336 header = N_("Newsgroups:");
2338 case COMPOSE_FOLLOWUPTO:
2339 header = N_( "Followup-To:");
2346 header = prefs_common_translated_header_name(header);
2348 cur = begin = (gchar *)address;
2350 /* we separate the line by commas, but not if we're inside a quoted
2352 while (*cur != '\0') {
2354 in_quote = !in_quote;
2355 if (*cur == ',' && !in_quote) {
2356 gchar *tmp = g_strdup(begin);
2358 tmp[cur-begin]='\0';
2361 while (*tmp == ' ' || *tmp == '\t')
2363 compose_add_header_entry(compose, header, tmp);
2370 gchar *tmp = g_strdup(begin);
2372 tmp[cur-begin]='\0';
2375 while (*tmp == ' ' || *tmp == '\t')
2377 compose_add_header_entry(compose, header, tmp);
2382 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2384 static GdkColor yellow;
2385 static GdkColor black;
2386 static gboolean yellow_initialised = FALSE;
2390 if (!yellow_initialised) {
2391 gdk_color_parse("#f5f6be", &yellow);
2392 gdk_color_parse("#000000", &black);
2393 yellow_initialised = gdk_colormap_alloc_color(
2394 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2395 yellow_initialised &= gdk_colormap_alloc_color(
2396 gdk_colormap_get_system(), &black, FALSE, TRUE);
2399 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2400 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2401 if (gtk_entry_get_text(entry) &&
2402 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2403 if (yellow_initialised) {
2404 gtk_widget_modify_base(
2405 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2406 GTK_STATE_NORMAL, &yellow);
2407 gtk_widget_modify_text(
2408 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2409 GTK_STATE_NORMAL, &black);
2415 void compose_toolbar_cb(gint action, gpointer data)
2417 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2418 Compose *compose = (Compose*)toolbar_item->parent;
2420 g_return_if_fail(compose != NULL);
2424 compose_send_cb(NULL, compose);
2427 compose_send_later_cb(NULL, compose);
2430 compose_draft(compose, COMPOSE_QUIT_EDITING);
2433 compose_insert_file_cb(NULL, compose);
2436 compose_attach_cb(NULL, compose);
2439 compose_insert_sig(compose, FALSE);
2442 compose_ext_editor_cb(NULL, compose);
2444 case A_LINEWRAP_CURRENT:
2445 compose_beautify_paragraph(compose, NULL, TRUE);
2447 case A_LINEWRAP_ALL:
2448 compose_wrap_all_full(compose, TRUE);
2451 compose_address_cb(NULL, compose);
2454 case A_CHECK_SPELLING:
2455 compose_check_all(NULL, compose);
2463 static void compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2468 gchar *subject = NULL;
2472 gchar **attach = NULL;
2474 /* get mailto parts but skip from */
2475 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2478 compose_entry_append(compose, to, to_type);
2480 compose_entry_append(compose, cc, COMPOSE_CC);
2482 compose_entry_append(compose, bcc, COMPOSE_BCC);
2484 if (!g_utf8_validate (subject, -1, NULL)) {
2485 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2486 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2489 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2493 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2494 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2497 gboolean prev_autowrap = compose->autowrap;
2499 compose->autowrap = FALSE;
2501 mark = gtk_text_buffer_get_insert(buffer);
2502 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2504 if (!g_utf8_validate (body, -1, NULL)) {
2505 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2506 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2509 gtk_text_buffer_insert(buffer, &iter, body, -1);
2511 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2513 compose->autowrap = prev_autowrap;
2514 if (compose->autowrap)
2515 compose_wrap_all(compose);
2519 gint i = 0, att = 0;
2520 gchar *warn_files = NULL;
2521 while (attach[i] != NULL) {
2522 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2523 if (utf8_filename) {
2524 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2525 gchar *tmp = g_strdup_printf("%s%s\n",
2526 warn_files?warn_files:"",
2532 g_free(utf8_filename);
2534 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2539 alertpanel_notice(ngettext(
2540 "The following file has been attached: \n%s",
2541 "The following files have been attached: \n%s", att), warn_files);
2553 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2555 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2556 {"Cc:", NULL, TRUE},
2557 {"References:", NULL, FALSE},
2558 {"Bcc:", NULL, TRUE},
2559 {"Newsgroups:", NULL, TRUE},
2560 {"Followup-To:", NULL, TRUE},
2561 {"List-Post:", NULL, FALSE},
2562 {"X-Priority:", NULL, FALSE},
2563 {NULL, NULL, FALSE}};
2579 g_return_val_if_fail(msginfo != NULL, -1);
2581 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2582 procheader_get_header_fields(fp, hentry);
2585 if (hentry[H_REPLY_TO].body != NULL) {
2586 if (hentry[H_REPLY_TO].body[0] != '\0') {
2588 conv_unmime_header(hentry[H_REPLY_TO].body,
2591 g_free(hentry[H_REPLY_TO].body);
2592 hentry[H_REPLY_TO].body = NULL;
2594 if (hentry[H_CC].body != NULL) {
2595 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2596 g_free(hentry[H_CC].body);
2597 hentry[H_CC].body = NULL;
2599 if (hentry[H_REFERENCES].body != NULL) {
2600 if (compose->mode == COMPOSE_REEDIT)
2601 compose->references = hentry[H_REFERENCES].body;
2603 compose->references = compose_parse_references
2604 (hentry[H_REFERENCES].body, msginfo->msgid);
2605 g_free(hentry[H_REFERENCES].body);
2607 hentry[H_REFERENCES].body = NULL;
2609 if (hentry[H_BCC].body != NULL) {
2610 if (compose->mode == COMPOSE_REEDIT)
2612 conv_unmime_header(hentry[H_BCC].body, NULL);
2613 g_free(hentry[H_BCC].body);
2614 hentry[H_BCC].body = NULL;
2616 if (hentry[H_NEWSGROUPS].body != NULL) {
2617 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2618 hentry[H_NEWSGROUPS].body = NULL;
2620 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2621 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2622 compose->followup_to =
2623 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2626 g_free(hentry[H_FOLLOWUP_TO].body);
2627 hentry[H_FOLLOWUP_TO].body = NULL;
2629 if (hentry[H_LIST_POST].body != NULL) {
2632 extract_address(hentry[H_LIST_POST].body);
2633 if (hentry[H_LIST_POST].body[0] != '\0') {
2634 scan_mailto_url(hentry[H_LIST_POST].body,
2635 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2637 g_free(compose->ml_post);
2638 compose->ml_post = to;
2641 g_free(hentry[H_LIST_POST].body);
2642 hentry[H_LIST_POST].body = NULL;
2645 /* CLAWS - X-Priority */
2646 if (compose->mode == COMPOSE_REEDIT)
2647 if (hentry[H_X_PRIORITY].body != NULL) {
2650 priority = atoi(hentry[H_X_PRIORITY].body);
2651 g_free(hentry[H_X_PRIORITY].body);
2653 hentry[H_X_PRIORITY].body = NULL;
2655 if (priority < PRIORITY_HIGHEST ||
2656 priority > PRIORITY_LOWEST)
2657 priority = PRIORITY_NORMAL;
2659 compose->priority = priority;
2662 if (compose->mode == COMPOSE_REEDIT) {
2663 if (msginfo->inreplyto && *msginfo->inreplyto)
2664 compose->inreplyto = g_strdup(msginfo->inreplyto);
2668 if (msginfo->msgid && *msginfo->msgid)
2669 compose->inreplyto = g_strdup(msginfo->msgid);
2671 if (!compose->references) {
2672 if (msginfo->msgid && *msginfo->msgid) {
2673 if (msginfo->inreplyto && *msginfo->inreplyto)
2674 compose->references =
2675 g_strdup_printf("<%s>\n\t<%s>",
2679 compose->references =
2680 g_strconcat("<", msginfo->msgid, ">",
2682 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2683 compose->references =
2684 g_strconcat("<", msginfo->inreplyto, ">",
2692 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2694 GSList *ref_id_list, *cur;
2698 ref_id_list = references_list_append(NULL, ref);
2699 if (!ref_id_list) return NULL;
2700 if (msgid && *msgid)
2701 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2706 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2707 /* "<" + Message-ID + ">" + CR+LF+TAB */
2708 len += strlen((gchar *)cur->data) + 5;
2710 if (len > MAX_REFERENCES_LEN) {
2711 /* remove second message-ID */
2712 if (ref_id_list && ref_id_list->next &&
2713 ref_id_list->next->next) {
2714 g_free(ref_id_list->next->data);
2715 ref_id_list = g_slist_remove
2716 (ref_id_list, ref_id_list->next->data);
2718 slist_free_strings(ref_id_list);
2719 g_slist_free(ref_id_list);
2726 new_ref = g_string_new("");
2727 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2728 if (new_ref->len > 0)
2729 g_string_append(new_ref, "\n\t");
2730 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2733 slist_free_strings(ref_id_list);
2734 g_slist_free(ref_id_list);
2736 new_ref_str = new_ref->str;
2737 g_string_free(new_ref, FALSE);
2742 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2743 const gchar *fmt, const gchar *qmark,
2744 const gchar *body, gboolean rewrap,
2745 gboolean need_unescape,
2746 const gchar *err_msg)
2748 MsgInfo* dummyinfo = NULL;
2749 gchar *quote_str = NULL;
2751 gboolean prev_autowrap;
2752 const gchar *trimmed_body = body;
2753 gint cursor_pos = -1;
2754 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2755 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2760 SIGNAL_BLOCK(buffer);
2763 dummyinfo = compose_msginfo_new_from_compose(compose);
2764 msginfo = dummyinfo;
2767 if (qmark != NULL) {
2769 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2770 compose->gtkaspell);
2772 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2774 quote_fmt_scan_string(qmark);
2777 buf = quote_fmt_get_buffer();
2779 alertpanel_error(_("Quote mark format error."));
2781 Xstrdup_a(quote_str, buf, goto error)
2784 if (fmt && *fmt != '\0') {
2787 while (*trimmed_body == '\n')
2791 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2792 compose->gtkaspell);
2794 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2796 if (need_unescape) {
2799 /* decode \-escape sequences in the internal representation of the quote format */
2800 tmp = malloc(strlen(fmt)+1);
2801 pref_get_unescaped_pref(tmp, fmt);
2802 quote_fmt_scan_string(tmp);
2806 quote_fmt_scan_string(fmt);
2810 buf = quote_fmt_get_buffer();
2812 gint line = quote_fmt_get_line();
2813 alertpanel_error(err_msg, line);
2819 prev_autowrap = compose->autowrap;
2820 compose->autowrap = FALSE;
2822 mark = gtk_text_buffer_get_insert(buffer);
2823 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2824 if (g_utf8_validate(buf, -1, NULL)) {
2825 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2827 gchar *tmpout = NULL;
2828 tmpout = conv_codeset_strdup
2829 (buf, conv_get_locale_charset_str_no_utf8(),
2831 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2833 tmpout = g_malloc(strlen(buf)*2+1);
2834 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2836 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2840 cursor_pos = quote_fmt_get_cursor_pos();
2841 if (cursor_pos == -1)
2842 cursor_pos = gtk_text_iter_get_offset(&iter);
2843 compose->set_cursor_pos = cursor_pos;
2845 gtk_text_buffer_get_start_iter(buffer, &iter);
2846 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2847 gtk_text_buffer_place_cursor(buffer, &iter);
2849 compose->autowrap = prev_autowrap;
2850 if (compose->autowrap && rewrap)
2851 compose_wrap_all(compose);
2858 SIGNAL_UNBLOCK(buffer);
2860 procmsg_msginfo_free( dummyinfo );
2865 /* if ml_post is of type addr@host and from is of type
2866 * addr-anything@host, return TRUE
2868 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2870 gchar *left_ml = NULL;
2871 gchar *right_ml = NULL;
2872 gchar *left_from = NULL;
2873 gchar *right_from = NULL;
2874 gboolean result = FALSE;
2876 if (!ml_post || !from)
2879 left_ml = g_strdup(ml_post);
2880 if (strstr(left_ml, "@")) {
2881 right_ml = strstr(left_ml, "@")+1;
2882 *(strstr(left_ml, "@")) = '\0';
2885 left_from = g_strdup(from);
2886 if (strstr(left_from, "@")) {
2887 right_from = strstr(left_from, "@")+1;
2888 *(strstr(left_from, "@")) = '\0';
2891 if (left_ml && left_from && right_ml && right_from
2892 && !strncmp(left_from, left_ml, strlen(left_ml))
2893 && !strcmp(right_from, right_ml)) {
2902 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2904 gchar *my_addr1, *my_addr2;
2906 if (!addr1 || !addr2)
2909 Xstrdup_a(my_addr1, addr1, return FALSE);
2910 Xstrdup_a(my_addr2, addr2, return FALSE);
2912 extract_address(my_addr1);
2913 extract_address(my_addr2);
2915 return !strcasecmp(my_addr1, my_addr2);
2918 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2919 gboolean to_all, gboolean to_ml,
2921 gboolean followup_and_reply_to)
2923 GSList *cc_list = NULL;
2926 gchar *replyto = NULL;
2927 GHashTable *to_table;
2929 gboolean reply_to_ml = FALSE;
2930 gboolean default_reply_to = FALSE;
2932 g_return_if_fail(compose->account != NULL);
2933 g_return_if_fail(msginfo != NULL);
2935 reply_to_ml = to_ml && compose->ml_post;
2937 default_reply_to = msginfo->folder &&
2938 msginfo->folder->prefs->enable_default_reply_to;
2940 if (compose->account->protocol != A_NNTP) {
2941 if (reply_to_ml && !default_reply_to) {
2943 gboolean is_subscr = is_subscription(compose->ml_post,
2946 /* normal answer to ml post with a reply-to */
2947 compose_entry_append(compose,
2950 if (compose->replyto
2951 && !same_address(compose->ml_post, compose->replyto))
2952 compose_entry_append(compose,
2956 /* answer to subscription confirmation */
2957 if (compose->replyto)
2958 compose_entry_append(compose,
2961 else if (msginfo->from)
2962 compose_entry_append(compose,
2967 else if (!(to_all || to_sender) && default_reply_to) {
2968 compose_entry_append(compose,
2969 msginfo->folder->prefs->default_reply_to,
2971 compose_entry_mark_default_to(compose,
2972 msginfo->folder->prefs->default_reply_to);
2977 Xstrdup_a(tmp1, msginfo->from, return);
2978 extract_address(tmp1);
2979 if (to_all || to_sender ||
2980 !account_find_from_address(tmp1, FALSE))
2981 compose_entry_append(compose,
2982 (compose->replyto && !to_sender)
2983 ? compose->replyto :
2984 msginfo->from ? msginfo->from : "",
2986 else if (!to_all && !to_sender) {
2987 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2988 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
2989 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2990 if (compose->replyto) {
2991 compose_entry_append(compose,
2995 compose_entry_append(compose,
2996 msginfo->from ? msginfo->from : "",
3000 /* replying to own mail, use original recp */
3001 compose_entry_append(compose,
3002 msginfo->to ? msginfo->to : "",
3004 compose_entry_append(compose,
3005 msginfo->cc ? msginfo->cc : "",
3011 if (to_sender || (compose->followup_to &&
3012 !strncmp(compose->followup_to, "poster", 6)))
3013 compose_entry_append
3015 (compose->replyto ? compose->replyto :
3016 msginfo->from ? msginfo->from : ""),
3019 else if (followup_and_reply_to || to_all) {
3020 compose_entry_append
3022 (compose->replyto ? compose->replyto :
3023 msginfo->from ? msginfo->from : ""),
3026 compose_entry_append
3028 compose->followup_to ? compose->followup_to :
3029 compose->newsgroups ? compose->newsgroups : "",
3030 COMPOSE_NEWSGROUPS);
3033 compose_entry_append
3035 compose->followup_to ? compose->followup_to :
3036 compose->newsgroups ? compose->newsgroups : "",
3037 COMPOSE_NEWSGROUPS);
3040 if (msginfo->subject && *msginfo->subject) {
3044 buf = p = g_strdup(msginfo->subject);
3045 p += subject_get_prefix_length(p);
3046 memmove(buf, p, strlen(p) + 1);
3048 buf2 = g_strdup_printf("Re: %s", buf);
3049 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3054 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3056 if (to_ml && compose->ml_post) return;
3057 if (!to_all || compose->account->protocol == A_NNTP) return;
3059 if (compose->replyto) {
3060 Xstrdup_a(replyto, compose->replyto, return);
3061 extract_address(replyto);
3063 if (msginfo->from) {
3064 Xstrdup_a(from, msginfo->from, return);
3065 extract_address(from);
3068 if (replyto && from)
3069 cc_list = address_list_append_with_comments(cc_list, from);
3070 if (to_all && msginfo->folder &&
3071 msginfo->folder->prefs->enable_default_reply_to)
3072 cc_list = address_list_append_with_comments(cc_list,
3073 msginfo->folder->prefs->default_reply_to);
3074 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3075 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3077 to_table = g_hash_table_new(g_str_hash, g_str_equal);
3079 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
3080 if (compose->account) {
3081 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
3082 GINT_TO_POINTER(1));
3084 /* remove address on To: and that of current account */
3085 for (cur = cc_list; cur != NULL; ) {
3086 GSList *next = cur->next;
3089 addr = g_utf8_strdown(cur->data, -1);
3090 extract_address(addr);
3092 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3093 cc_list = g_slist_remove(cc_list, cur->data);
3095 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3099 hash_free_strings(to_table);
3100 g_hash_table_destroy(to_table);
3103 for (cur = cc_list; cur != NULL; cur = cur->next)
3104 compose_entry_append(compose, (gchar *)cur->data,
3106 slist_free_strings(cc_list);
3107 g_slist_free(cc_list);
3112 #define SET_ENTRY(entry, str) \
3115 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3118 #define SET_ADDRESS(type, str) \
3121 compose_entry_append(compose, str, type); \
3124 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3126 g_return_if_fail(msginfo != NULL);
3128 SET_ENTRY(subject_entry, msginfo->subject);
3129 SET_ENTRY(from_name, msginfo->from);
3130 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3131 SET_ADDRESS(COMPOSE_CC, compose->cc);
3132 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3133 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3134 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3135 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3137 compose_update_priority_menu_item(compose);
3138 compose_update_privacy_system_menu_item(compose, FALSE);
3139 compose_show_first_last_header(compose, TRUE);
3145 static void compose_insert_sig(Compose *compose, gboolean replace)
3147 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3148 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3150 GtkTextIter iter, iter_end;
3151 gint cur_pos, ins_pos;
3152 gboolean prev_autowrap;
3153 gboolean found = FALSE;
3154 gboolean exists = FALSE;
3156 g_return_if_fail(compose->account != NULL);
3160 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3161 G_CALLBACK(compose_changed_cb),
3164 mark = gtk_text_buffer_get_insert(buffer);
3165 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3166 cur_pos = gtk_text_iter_get_offset (&iter);
3169 gtk_text_buffer_get_end_iter(buffer, &iter);
3171 exists = (compose->sig_str != NULL);
3174 GtkTextIter first_iter, start_iter, end_iter;
3176 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3178 if (!exists || compose->sig_str[0] == '\0')
3181 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3182 compose->signature_tag);
3185 /* include previous \n\n */
3186 gtk_text_iter_backward_chars(&first_iter, 1);
3187 start_iter = first_iter;
3188 end_iter = first_iter;
3190 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3191 compose->signature_tag);
3192 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3193 compose->signature_tag);
3195 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3201 g_free(compose->sig_str);
3202 compose->sig_str = account_get_signature_str(compose->account);
3204 cur_pos = gtk_text_iter_get_offset(&iter);
3206 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3207 g_free(compose->sig_str);
3208 compose->sig_str = NULL;
3210 if (compose->sig_inserted == FALSE)
3211 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3212 compose->sig_inserted = TRUE;
3214 cur_pos = gtk_text_iter_get_offset(&iter);
3215 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3217 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3218 gtk_text_iter_forward_chars(&iter, 1);
3219 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3220 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3222 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3223 cur_pos = gtk_text_buffer_get_char_count (buffer);
3226 /* put the cursor where it should be
3227 * either where the quote_fmt says, either where it was */
3228 if (compose->set_cursor_pos < 0)
3229 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3231 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3232 compose->set_cursor_pos);
3234 compose->set_cursor_pos = -1;
3235 gtk_text_buffer_place_cursor(buffer, &iter);
3236 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3237 G_CALLBACK(compose_changed_cb),
3243 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3246 GtkTextBuffer *buffer;
3249 const gchar *cur_encoding;
3250 gchar buf[BUFFSIZE];
3253 gboolean prev_autowrap;
3254 gboolean badtxt = FALSE;
3256 g_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3258 if ((fp = g_fopen(file, "rb")) == NULL) {
3259 FILE_OP_ERROR(file, "fopen");
3260 return COMPOSE_INSERT_READ_ERROR;
3263 prev_autowrap = compose->autowrap;
3264 compose->autowrap = FALSE;
3266 text = GTK_TEXT_VIEW(compose->text);
3267 buffer = gtk_text_view_get_buffer(text);
3268 mark = gtk_text_buffer_get_insert(buffer);
3269 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3271 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3272 G_CALLBACK(text_inserted),
3275 cur_encoding = conv_get_locale_charset_str_no_utf8();
3277 while (fgets(buf, sizeof(buf), fp) != NULL) {
3280 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3281 str = g_strdup(buf);
3283 str = conv_codeset_strdup
3284 (buf, cur_encoding, CS_INTERNAL);
3287 /* strip <CR> if DOS/Windows file,
3288 replace <CR> with <LF> if Macintosh file. */
3291 if (len > 0 && str[len - 1] != '\n') {
3293 if (str[len] == '\r') str[len] = '\n';
3296 gtk_text_buffer_insert(buffer, &iter, str, -1);
3300 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3301 G_CALLBACK(text_inserted),
3303 compose->autowrap = prev_autowrap;
3304 if (compose->autowrap)
3305 compose_wrap_all(compose);
3310 return COMPOSE_INSERT_INVALID_CHARACTER;
3312 return COMPOSE_INSERT_SUCCESS;
3315 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3316 const gchar *filename,
3317 const gchar *content_type)
3325 GtkListStore *store;
3327 gboolean has_binary = FALSE;
3329 if (!is_file_exist(file)) {
3330 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3331 gboolean result = FALSE;
3332 if (file_from_uri && is_file_exist(file_from_uri)) {
3333 result = compose_attach_append(
3334 compose, file_from_uri,
3338 g_free(file_from_uri);
3341 alertpanel_error("File %s doesn't exist\n", filename);
3344 if ((size = get_file_size(file)) < 0) {
3345 alertpanel_error("Can't get file size of %s\n", filename);
3349 alertpanel_error(_("File %s is empty."), filename);
3352 if ((fp = g_fopen(file, "rb")) == NULL) {
3353 alertpanel_error(_("Can't read %s."), filename);
3358 ainfo = g_new0(AttachInfo, 1);
3359 auto_ainfo = g_auto_pointer_new_with_free
3360 (ainfo, (GFreeFunc) compose_attach_info_free);
3361 ainfo->file = g_strdup(file);
3364 ainfo->content_type = g_strdup(content_type);
3365 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3367 MsgFlags flags = {0, 0};
3369 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3370 ainfo->encoding = ENC_7BIT;
3372 ainfo->encoding = ENC_8BIT;
3374 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3375 if (msginfo && msginfo->subject)
3376 name = g_strdup(msginfo->subject);
3378 name = g_path_get_basename(filename ? filename : file);
3380 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3382 procmsg_msginfo_free(msginfo);
3384 if (!g_ascii_strncasecmp(content_type, "text", 4))
3385 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3387 ainfo->encoding = ENC_BASE64;
3388 name = g_path_get_basename(filename ? filename : file);
3389 ainfo->name = g_strdup(name);
3393 ainfo->content_type = procmime_get_mime_type(file);
3394 if (!ainfo->content_type) {
3395 ainfo->content_type =
3396 g_strdup("application/octet-stream");
3397 ainfo->encoding = ENC_BASE64;
3398 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3400 procmime_get_encoding_for_text_file(file, &has_binary);
3402 ainfo->encoding = ENC_BASE64;
3403 name = g_path_get_basename(filename ? filename : file);
3404 ainfo->name = g_strdup(name);
3408 if (ainfo->name != NULL
3409 && !strcmp(ainfo->name, ".")) {
3410 g_free(ainfo->name);
3414 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3415 g_free(ainfo->content_type);
3416 ainfo->content_type = g_strdup("application/octet-stream");
3419 ainfo->size = (goffset)size;
3420 size_text = to_human_readable((goffset)size);
3422 store = GTK_LIST_STORE(gtk_tree_view_get_model
3423 (GTK_TREE_VIEW(compose->attach_clist)));
3425 gtk_list_store_append(store, &iter);
3426 gtk_list_store_set(store, &iter,
3427 COL_MIMETYPE, ainfo->content_type,
3428 COL_SIZE, size_text,
3429 COL_NAME, ainfo->name,
3431 COL_AUTODATA, auto_ainfo,
3434 g_auto_pointer_free(auto_ainfo);
3435 compose_attach_update_label(compose);
3439 static void compose_use_signing(Compose *compose, gboolean use_signing)
3441 compose->use_signing = use_signing;
3442 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3445 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3447 compose->use_encryption = use_encryption;
3448 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3451 #define NEXT_PART_NOT_CHILD(info) \
3453 node = info->node; \
3454 while (node->children) \
3455 node = g_node_last_child(node); \
3456 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3459 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3463 MimeInfo *firsttext = NULL;
3464 MimeInfo *encrypted = NULL;
3467 const gchar *partname = NULL;
3469 mimeinfo = procmime_scan_message(msginfo);
3470 if (!mimeinfo) return;
3472 if (mimeinfo->node->children == NULL) {
3473 procmime_mimeinfo_free_all(mimeinfo);
3477 /* find first content part */
3478 child = (MimeInfo *) mimeinfo->node->children->data;
3479 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3480 child = (MimeInfo *)child->node->children->data;
3482 if (child->type == MIMETYPE_TEXT) {
3484 debug_print("First text part found\n");
3485 } else if (compose->mode == COMPOSE_REEDIT &&
3486 child->type == MIMETYPE_APPLICATION &&
3487 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3488 encrypted = (MimeInfo *)child->node->parent->data;
3491 child = (MimeInfo *) mimeinfo->node->children->data;
3492 while (child != NULL) {
3495 if (child == encrypted) {
3496 /* skip this part of tree */
3497 NEXT_PART_NOT_CHILD(child);
3501 if (child->type == MIMETYPE_MULTIPART) {
3502 /* get the actual content */
3503 child = procmime_mimeinfo_next(child);
3507 if (child == firsttext) {
3508 child = procmime_mimeinfo_next(child);
3512 outfile = procmime_get_tmp_file_name(child);
3513 if ((err = procmime_get_part(outfile, child)) < 0)
3514 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3516 gchar *content_type;
3518 content_type = procmime_get_content_type_str(child->type, child->subtype);
3520 /* if we meet a pgp signature, we don't attach it, but
3521 * we force signing. */
3522 if ((strcmp(content_type, "application/pgp-signature") &&
3523 strcmp(content_type, "application/pkcs7-signature") &&
3524 strcmp(content_type, "application/x-pkcs7-signature"))
3525 || compose->mode == COMPOSE_REDIRECT) {
3526 partname = procmime_mimeinfo_get_parameter(child, "filename");
3527 if (partname == NULL)
3528 partname = procmime_mimeinfo_get_parameter(child, "name");
3529 if (partname == NULL)
3531 compose_attach_append(compose, outfile,
3532 partname, content_type);
3534 compose_force_signing(compose, compose->account);
3536 g_free(content_type);
3539 NEXT_PART_NOT_CHILD(child);
3541 procmime_mimeinfo_free_all(mimeinfo);
3544 #undef NEXT_PART_NOT_CHILD
3549 WAIT_FOR_INDENT_CHAR,
3550 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3553 /* return indent length, we allow:
3554 indent characters followed by indent characters or spaces/tabs,
3555 alphabets and numbers immediately followed by indent characters,
3556 and the repeating sequences of the above
3557 If quote ends with multiple spaces, only the first one is included. */
3558 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3559 const GtkTextIter *start, gint *len)
3561 GtkTextIter iter = *start;
3565 IndentState state = WAIT_FOR_INDENT_CHAR;
3568 gint alnum_count = 0;
3569 gint space_count = 0;
3572 if (prefs_common.quote_chars == NULL) {
3576 while (!gtk_text_iter_ends_line(&iter)) {
3577 wc = gtk_text_iter_get_char(&iter);
3578 if (g_unichar_iswide(wc))
3580 clen = g_unichar_to_utf8(wc, ch);
3584 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3585 is_space = g_unichar_isspace(wc);
3587 if (state == WAIT_FOR_INDENT_CHAR) {
3588 if (!is_indent && !g_unichar_isalnum(wc))
3591 quote_len += alnum_count + space_count + 1;
3592 alnum_count = space_count = 0;
3593 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3596 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3597 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3601 else if (is_indent) {
3602 quote_len += alnum_count + space_count + 1;
3603 alnum_count = space_count = 0;
3606 state = WAIT_FOR_INDENT_CHAR;
3610 gtk_text_iter_forward_char(&iter);
3613 if (quote_len > 0 && space_count > 0)
3619 if (quote_len > 0) {
3621 gtk_text_iter_forward_chars(&iter, quote_len);
3622 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3628 /* return >0 if the line is itemized */
3629 static int compose_itemized_length(GtkTextBuffer *buffer,
3630 const GtkTextIter *start)
3632 GtkTextIter iter = *start;
3637 if (gtk_text_iter_ends_line(&iter))
3642 wc = gtk_text_iter_get_char(&iter);
3643 if (!g_unichar_isspace(wc))
3645 gtk_text_iter_forward_char(&iter);
3646 if (gtk_text_iter_ends_line(&iter))
3650 clen = g_unichar_to_utf8(wc, ch);
3654 if (!strchr("*-+", ch[0]))
3657 gtk_text_iter_forward_char(&iter);
3658 if (gtk_text_iter_ends_line(&iter))
3660 wc = gtk_text_iter_get_char(&iter);
3661 if (g_unichar_isspace(wc)) {
3667 /* return the string at the start of the itemization */
3668 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3669 const GtkTextIter *start)
3671 GtkTextIter iter = *start;
3674 GString *item_chars = g_string_new("");
3677 if (gtk_text_iter_ends_line(&iter))
3682 wc = gtk_text_iter_get_char(&iter);
3683 if (!g_unichar_isspace(wc))
3685 gtk_text_iter_forward_char(&iter);
3686 if (gtk_text_iter_ends_line(&iter))
3688 g_string_append_unichar(item_chars, wc);
3691 str = item_chars->str;
3692 g_string_free(item_chars, FALSE);
3696 /* return the number of spaces at a line's start */
3697 static int compose_left_offset_length(GtkTextBuffer *buffer,
3698 const GtkTextIter *start)
3700 GtkTextIter iter = *start;
3703 if (gtk_text_iter_ends_line(&iter))
3707 wc = gtk_text_iter_get_char(&iter);
3708 if (!g_unichar_isspace(wc))
3711 gtk_text_iter_forward_char(&iter);
3712 if (gtk_text_iter_ends_line(&iter))
3716 gtk_text_iter_forward_char(&iter);
3717 if (gtk_text_iter_ends_line(&iter))
3722 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3723 const GtkTextIter *start,
3724 GtkTextIter *break_pos,
3728 GtkTextIter iter = *start, line_end = *start;
3729 PangoLogAttr *attrs;
3736 gboolean can_break = FALSE;
3737 gboolean do_break = FALSE;
3738 gboolean was_white = FALSE;
3739 gboolean prev_dont_break = FALSE;
3741 gtk_text_iter_forward_to_line_end(&line_end);
3742 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3743 len = g_utf8_strlen(str, -1);
3747 g_warning("compose_get_line_break_pos: len = 0!\n");
3751 /* g_print("breaking line: %d: %s (len = %d)\n",
3752 gtk_text_iter_get_line(&iter), str, len); */
3754 attrs = g_new(PangoLogAttr, len + 1);
3756 pango_default_break(str, -1, NULL, attrs, len + 1);
3760 /* skip quote and leading spaces */
3761 for (i = 0; *p != '\0' && i < len; i++) {
3764 wc = g_utf8_get_char(p);
3765 if (i >= quote_len && !g_unichar_isspace(wc))
3767 if (g_unichar_iswide(wc))
3769 else if (*p == '\t')
3773 p = g_utf8_next_char(p);
3776 for (; *p != '\0' && i < len; i++) {
3777 PangoLogAttr *attr = attrs + i;
3781 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3784 was_white = attr->is_white;
3786 /* don't wrap URI */
3787 if ((uri_len = get_uri_len(p)) > 0) {
3789 if (pos > 0 && col > max_col) {
3799 wc = g_utf8_get_char(p);
3800 if (g_unichar_iswide(wc)) {
3802 if (prev_dont_break && can_break && attr->is_line_break)
3804 } else if (*p == '\t')
3808 if (pos > 0 && col > max_col) {
3813 if (*p == '-' || *p == '/')
3814 prev_dont_break = TRUE;
3816 prev_dont_break = FALSE;
3818 p = g_utf8_next_char(p);
3822 debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3827 *break_pos = *start;
3828 gtk_text_iter_set_line_offset(break_pos, pos);
3833 static gboolean compose_join_next_line(Compose *compose,
3834 GtkTextBuffer *buffer,
3836 const gchar *quote_str)
3838 GtkTextIter iter_ = *iter, cur, prev, next, end;
3839 PangoLogAttr attrs[3];
3841 gchar *next_quote_str;
3844 gboolean keep_cursor = FALSE;
3846 if (!gtk_text_iter_forward_line(&iter_) ||
3847 gtk_text_iter_ends_line(&iter_)) {
3850 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
3852 if ((quote_str || next_quote_str) &&
3853 strcmp2(quote_str, next_quote_str) != 0) {
3854 g_free(next_quote_str);
3857 g_free(next_quote_str);
3860 if (quote_len > 0) {
3861 gtk_text_iter_forward_chars(&end, quote_len);
3862 if (gtk_text_iter_ends_line(&end)) {
3867 /* don't join itemized lines */
3868 if (compose_itemized_length(buffer, &end) > 0) {
3872 /* don't join signature separator */
3873 if (compose_is_sig_separator(compose, buffer, &iter_)) {
3876 /* delete quote str */
3878 gtk_text_buffer_delete(buffer, &iter_, &end);
3880 /* don't join line breaks put by the user */
3882 gtk_text_iter_backward_char(&cur);
3883 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
3884 gtk_text_iter_forward_char(&cur);
3888 gtk_text_iter_forward_char(&cur);
3889 /* delete linebreak and extra spaces */
3890 while (gtk_text_iter_backward_char(&cur)) {
3891 wc1 = gtk_text_iter_get_char(&cur);
3892 if (!g_unichar_isspace(wc1))
3897 while (!gtk_text_iter_ends_line(&cur)) {
3898 wc1 = gtk_text_iter_get_char(&cur);
3899 if (!g_unichar_isspace(wc1))
3901 gtk_text_iter_forward_char(&cur);
3904 if (!gtk_text_iter_equal(&prev, &next)) {
3907 mark = gtk_text_buffer_get_insert(buffer);
3908 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
3909 if (gtk_text_iter_equal(&prev, &cur))
3911 gtk_text_buffer_delete(buffer, &prev, &next);
3915 /* insert space if required */
3916 gtk_text_iter_backward_char(&prev);
3917 wc1 = gtk_text_iter_get_char(&prev);
3918 wc2 = gtk_text_iter_get_char(&next);
3919 gtk_text_iter_forward_char(&next);
3920 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
3921 pango_default_break(str, -1, NULL, attrs, 3);
3922 if (!attrs[1].is_line_break ||
3923 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
3924 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
3926 gtk_text_iter_backward_char(&iter_);
3927 gtk_text_buffer_place_cursor(buffer, &iter_);
3936 #define ADD_TXT_POS(bp_, ep_, pti_) \
3937 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
3938 last = last->next; \
3939 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
3940 last->next = NULL; \
3942 g_warning("alloc error scanning URIs\n"); \
3945 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
3947 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3948 GtkTextBuffer *buffer;
3949 GtkTextIter iter, break_pos, end_of_line;
3950 gchar *quote_str = NULL;
3952 gboolean wrap_quote = prefs_common.linewrap_quote;
3953 gboolean prev_autowrap = compose->autowrap;
3954 gint startq_offset = -1, noq_offset = -1;
3955 gint uri_start = -1, uri_stop = -1;
3956 gint nouri_start = -1, nouri_stop = -1;
3957 gint num_blocks = 0;
3958 gint quotelevel = -1;
3959 gboolean modified = force;
3960 gboolean removed = FALSE;
3961 gboolean modified_before_remove = FALSE;
3963 gboolean start = TRUE;
3964 gint itemized_len = 0, rem_item_len = 0;
3965 gchar *itemized_chars = NULL;
3966 gboolean item_continuation = FALSE;
3971 if (compose->draft_timeout_tag == -2) {
3975 compose->autowrap = FALSE;
3977 buffer = gtk_text_view_get_buffer(text);
3978 undo_wrapping(compose->undostruct, TRUE);
3983 mark = gtk_text_buffer_get_insert(buffer);
3984 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3988 if (compose->draft_timeout_tag == -2) {
3989 if (gtk_text_iter_ends_line(&iter)) {
3990 while (gtk_text_iter_ends_line(&iter) &&
3991 gtk_text_iter_forward_line(&iter))
3994 while (gtk_text_iter_backward_line(&iter)) {
3995 if (gtk_text_iter_ends_line(&iter)) {
3996 gtk_text_iter_forward_line(&iter);
4002 /* move to line start */
4003 gtk_text_iter_set_line_offset(&iter, 0);
4006 itemized_len = compose_itemized_length(buffer, &iter);
4008 if (!itemized_len) {
4009 itemized_len = compose_left_offset_length(buffer, &iter);
4010 item_continuation = TRUE;
4014 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4016 /* go until paragraph end (empty line) */
4017 while (start || !gtk_text_iter_ends_line(&iter)) {
4018 gchar *scanpos = NULL;
4019 /* parse table - in order of priority */
4021 const gchar *needle; /* token */
4023 /* token search function */
4024 gchar *(*search) (const gchar *haystack,
4025 const gchar *needle);
4026 /* part parsing function */
4027 gboolean (*parse) (const gchar *start,
4028 const gchar *scanpos,
4032 /* part to URI function */
4033 gchar *(*build_uri) (const gchar *bp,
4037 static struct table parser[] = {
4038 {"http://", strcasestr, get_uri_part, make_uri_string},
4039 {"https://", strcasestr, get_uri_part, make_uri_string},
4040 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4041 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4042 {"www.", strcasestr, get_uri_part, make_http_string},
4043 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4044 {"@", strcasestr, get_email_part, make_email_string}
4046 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4047 gint last_index = PARSE_ELEMS;
4049 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4053 if (!prev_autowrap && num_blocks == 0) {
4055 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4056 G_CALLBACK(text_inserted),
4059 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4062 uri_start = uri_stop = -1;
4064 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4067 debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4068 if (startq_offset == -1)
4069 startq_offset = gtk_text_iter_get_offset(&iter);
4070 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4071 if (quotelevel > 2) {
4072 /* recycle colors */
4073 if (prefs_common.recycle_quote_colors)
4082 if (startq_offset == -1)
4083 noq_offset = gtk_text_iter_get_offset(&iter);
4087 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4090 if (gtk_text_iter_ends_line(&iter)) {
4092 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4093 prefs_common.linewrap_len,
4095 GtkTextIter prev, next, cur;
4096 if (prev_autowrap != FALSE || force) {
4097 compose->automatic_break = TRUE;
4099 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4100 compose->automatic_break = FALSE;
4101 if (itemized_len && compose->autoindent) {
4102 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4103 if (!item_continuation)
4104 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4106 } else if (quote_str && wrap_quote) {
4107 compose->automatic_break = TRUE;
4109 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4110 compose->automatic_break = FALSE;
4111 if (itemized_len && compose->autoindent) {
4112 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4113 if (!item_continuation)
4114 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4118 /* remove trailing spaces */
4120 rem_item_len = itemized_len;
4121 while (compose->autoindent && rem_item_len-- > 0)
4122 gtk_text_iter_backward_char(&cur);
4123 gtk_text_iter_backward_char(&cur);
4126 while (!gtk_text_iter_starts_line(&cur)) {
4129 gtk_text_iter_backward_char(&cur);
4130 wc = gtk_text_iter_get_char(&cur);
4131 if (!g_unichar_isspace(wc))
4135 if (!gtk_text_iter_equal(&prev, &next)) {
4136 gtk_text_buffer_delete(buffer, &prev, &next);
4138 gtk_text_iter_forward_char(&break_pos);
4142 gtk_text_buffer_insert(buffer, &break_pos,
4146 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4148 /* move iter to current line start */
4149 gtk_text_iter_set_line_offset(&iter, 0);
4156 /* move iter to next line start */
4162 if (!prev_autowrap && num_blocks > 0) {
4164 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4165 G_CALLBACK(text_inserted),
4169 while (!gtk_text_iter_ends_line(&end_of_line)) {
4170 gtk_text_iter_forward_char(&end_of_line);
4172 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4174 nouri_start = gtk_text_iter_get_offset(&iter);
4175 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4177 walk_pos = gtk_text_iter_get_offset(&iter);
4178 /* FIXME: this looks phony. scanning for anything in the parse table */
4179 for (n = 0; n < PARSE_ELEMS; n++) {
4182 tmp = parser[n].search(walk, parser[n].needle);
4184 if (scanpos == NULL || tmp < scanpos) {
4193 /* check if URI can be parsed */
4194 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4195 (const gchar **)&ep, FALSE)
4196 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4200 strlen(parser[last_index].needle);
4203 uri_start = walk_pos + (bp - o_walk);
4204 uri_stop = walk_pos + (ep - o_walk);
4208 gtk_text_iter_forward_line(&iter);
4211 if (startq_offset != -1) {
4212 GtkTextIter startquote, endquote;
4213 gtk_text_buffer_get_iter_at_offset(
4214 buffer, &startquote, startq_offset);
4217 switch (quotelevel) {
4219 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4220 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4221 gtk_text_buffer_apply_tag_by_name(
4222 buffer, "quote0", &startquote, &endquote);
4223 gtk_text_buffer_remove_tag_by_name(
4224 buffer, "quote1", &startquote, &endquote);
4225 gtk_text_buffer_remove_tag_by_name(
4226 buffer, "quote2", &startquote, &endquote);
4231 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4232 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4233 gtk_text_buffer_apply_tag_by_name(
4234 buffer, "quote1", &startquote, &endquote);
4235 gtk_text_buffer_remove_tag_by_name(
4236 buffer, "quote0", &startquote, &endquote);
4237 gtk_text_buffer_remove_tag_by_name(
4238 buffer, "quote2", &startquote, &endquote);
4243 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4244 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4245 gtk_text_buffer_apply_tag_by_name(
4246 buffer, "quote2", &startquote, &endquote);
4247 gtk_text_buffer_remove_tag_by_name(
4248 buffer, "quote0", &startquote, &endquote);
4249 gtk_text_buffer_remove_tag_by_name(
4250 buffer, "quote1", &startquote, &endquote);
4256 } else if (noq_offset != -1) {
4257 GtkTextIter startnoquote, endnoquote;
4258 gtk_text_buffer_get_iter_at_offset(
4259 buffer, &startnoquote, noq_offset);
4262 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4263 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4264 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4265 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4266 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4267 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4268 gtk_text_buffer_remove_tag_by_name(
4269 buffer, "quote0", &startnoquote, &endnoquote);
4270 gtk_text_buffer_remove_tag_by_name(
4271 buffer, "quote1", &startnoquote, &endnoquote);
4272 gtk_text_buffer_remove_tag_by_name(
4273 buffer, "quote2", &startnoquote, &endnoquote);
4279 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4280 GtkTextIter nouri_start_iter, nouri_end_iter;
4281 gtk_text_buffer_get_iter_at_offset(
4282 buffer, &nouri_start_iter, nouri_start);
4283 gtk_text_buffer_get_iter_at_offset(
4284 buffer, &nouri_end_iter, nouri_stop);
4285 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4286 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4287 gtk_text_buffer_remove_tag_by_name(
4288 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4289 modified_before_remove = modified;
4294 if (uri_start >= 0 && uri_stop > 0) {
4295 GtkTextIter uri_start_iter, uri_end_iter, back;
4296 gtk_text_buffer_get_iter_at_offset(
4297 buffer, &uri_start_iter, uri_start);
4298 gtk_text_buffer_get_iter_at_offset(
4299 buffer, &uri_end_iter, uri_stop);
4300 back = uri_end_iter;
4301 gtk_text_iter_backward_char(&back);
4302 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4303 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4304 gtk_text_buffer_apply_tag_by_name(
4305 buffer, "link", &uri_start_iter, &uri_end_iter);
4307 if (removed && !modified_before_remove) {
4313 debug_print("not modified, out after %d lines\n", lines);
4317 debug_print("modified, out after %d lines\n", lines);
4319 g_free(itemized_chars);
4322 undo_wrapping(compose->undostruct, FALSE);
4323 compose->autowrap = prev_autowrap;
4328 void compose_action_cb(void *data)
4330 Compose *compose = (Compose *)data;
4331 compose_wrap_all(compose);
4334 static void compose_wrap_all(Compose *compose)
4336 compose_wrap_all_full(compose, FALSE);
4339 static void compose_wrap_all_full(Compose *compose, gboolean force)
4341 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4342 GtkTextBuffer *buffer;
4344 gboolean modified = TRUE;
4346 buffer = gtk_text_view_get_buffer(text);
4348 gtk_text_buffer_get_start_iter(buffer, &iter);
4349 while (!gtk_text_iter_is_end(&iter) && modified)
4350 modified = compose_beautify_paragraph(compose, &iter, force);
4354 static void compose_set_title(Compose *compose)
4360 edited = compose->modified ? _(" [Edited]") : "";
4362 subject = gtk_editable_get_chars(
4363 GTK_EDITABLE(compose->subject_entry), 0, -1);
4365 #ifndef GENERIC_UMPC
4366 if (subject && strlen(subject))
4367 str = g_strdup_printf(_("%s - Compose message%s"),
4370 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4372 str = g_strdup(_("Compose message"));
4375 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4381 * compose_current_mail_account:
4383 * Find a current mail account (the currently selected account, or the
4384 * default account, if a news account is currently selected). If a
4385 * mail account cannot be found, display an error message.
4387 * Return value: Mail account, or NULL if not found.
4389 static PrefsAccount *
4390 compose_current_mail_account(void)
4394 if (cur_account && cur_account->protocol != A_NNTP)
4397 ac = account_get_default();
4398 if (!ac || ac->protocol == A_NNTP) {
4399 alertpanel_error(_("Account for sending mail is not specified.\n"
4400 "Please select a mail account before sending."));
4407 #define QUOTE_IF_REQUIRED(out, str) \
4409 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4413 len = strlen(str) + 3; \
4414 if ((__tmp = alloca(len)) == NULL) { \
4415 g_warning("can't allocate memory\n"); \
4416 g_string_free(header, TRUE); \
4419 g_snprintf(__tmp, len, "\"%s\"", str); \
4424 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4425 g_warning("can't allocate memory\n"); \
4426 g_string_free(header, TRUE); \
4429 strcpy(__tmp, str); \
4435 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4437 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4441 len = strlen(str) + 3; \
4442 if ((__tmp = alloca(len)) == NULL) { \
4443 g_warning("can't allocate memory\n"); \
4446 g_snprintf(__tmp, len, "\"%s\"", str); \
4451 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4452 g_warning("can't allocate memory\n"); \
4455 strcpy(__tmp, str); \
4461 static void compose_select_account(Compose *compose, PrefsAccount *account,
4466 g_return_if_fail(account != NULL);
4468 compose->account = account;
4470 if (account->name && *account->name) {
4472 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4473 from = g_strdup_printf("%s <%s>",
4474 buf, account->address);
4475 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4477 from = g_strdup_printf("<%s>",
4479 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4484 compose_set_title(compose);
4486 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4487 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4489 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4490 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4491 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4493 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4495 activate_privacy_system(compose, account, FALSE);
4497 if (!init && compose->mode != COMPOSE_REDIRECT) {
4498 undo_block(compose->undostruct);
4499 compose_insert_sig(compose, TRUE);
4500 undo_unblock(compose->undostruct);
4504 /* use account's dict info if set */
4505 if (compose->gtkaspell) {
4506 if (account->enable_default_dictionary)
4507 gtkaspell_change_dict(compose->gtkaspell,
4508 account->default_dictionary, FALSE);
4509 if (account->enable_default_alt_dictionary)
4510 gtkaspell_change_alt_dict(compose->gtkaspell,
4511 account->default_alt_dictionary);
4512 if (account->enable_default_dictionary
4513 || account->enable_default_alt_dictionary)
4514 compose_spell_menu_changed(compose);
4519 gboolean compose_check_for_valid_recipient(Compose *compose) {
4520 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4521 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4522 gboolean recipient_found = FALSE;
4526 /* free to and newsgroup list */
4527 slist_free_strings(compose->to_list);
4528 g_slist_free(compose->to_list);
4529 compose->to_list = NULL;
4531 slist_free_strings(compose->newsgroup_list);
4532 g_slist_free(compose->newsgroup_list);
4533 compose->newsgroup_list = NULL;
4535 /* search header entries for to and newsgroup entries */
4536 for (list = compose->header_list; list; list = list->next) {
4539 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4540 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4543 if (entry[0] != '\0') {
4544 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4545 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4546 compose->to_list = address_list_append(compose->to_list, entry);
4547 recipient_found = TRUE;
4550 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4551 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4552 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4553 recipient_found = TRUE;
4560 return recipient_found;
4563 static gboolean compose_check_for_set_recipients(Compose *compose)
4565 if (compose->account->set_autocc && compose->account->auto_cc) {
4566 gboolean found_other = FALSE;
4568 /* search header entries for to and newsgroup entries */
4569 for (list = compose->header_list; list; list = list->next) {
4572 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4573 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4576 if (strcmp(entry, compose->account->auto_cc)
4577 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4587 if (compose->batch) {
4588 gtk_widget_show_all(compose->window);
4590 aval = alertpanel(_("Send"),
4591 _("The only recipient is the default CC address. Send anyway?"),
4592 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4593 if (aval != G_ALERTALTERNATE)
4597 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4598 gboolean found_other = FALSE;
4600 /* search header entries for to and newsgroup entries */
4601 for (list = compose->header_list; list; list = list->next) {
4604 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4605 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4608 if (strcmp(entry, compose->account->auto_bcc)
4609 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4619 if (compose->batch) {
4620 gtk_widget_show_all(compose->window);
4622 aval = alertpanel(_("Send"),
4623 _("The only recipient is the default BCC address. Send anyway?"),
4624 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4625 if (aval != G_ALERTALTERNATE)
4632 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4636 if (compose_check_for_valid_recipient(compose) == FALSE) {
4637 if (compose->batch) {
4638 gtk_widget_show_all(compose->window);
4640 alertpanel_error(_("Recipient is not specified."));
4644 if (compose_check_for_set_recipients(compose) == FALSE) {
4648 if (!compose->batch) {
4649 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4650 if (*str == '\0' && check_everything == TRUE &&
4651 compose->mode != COMPOSE_REDIRECT) {
4653 gchar *button_label;
4656 if (compose->sending)
4657 button_label = _("+_Send");
4659 button_label = _("+_Queue");
4660 message = g_strdup_printf(_("Subject is empty. %s"),
4661 compose->sending?_("Send it anyway?"):
4662 _("Queue it anyway?"));
4664 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4665 GTK_STOCK_CANCEL, button_label, NULL);
4667 if (aval != G_ALERTALTERNATE)
4672 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4678 gint compose_send(Compose *compose)
4681 FolderItem *folder = NULL;
4683 gchar *msgpath = NULL;
4684 gboolean discard_window = FALSE;
4685 gchar *errstr = NULL;
4686 gchar *tmsgid = NULL;
4687 MainWindow *mainwin = mainwindow_get_mainwindow();
4688 gboolean queued_removed = FALSE;
4690 if (prefs_common.send_dialog_invisible
4691 || compose->batch == TRUE)
4692 discard_window = TRUE;
4694 compose_allow_user_actions (compose, FALSE);
4695 compose->sending = TRUE;
4697 if (compose_check_entries(compose, TRUE) == FALSE) {
4698 if (compose->batch) {
4699 gtk_widget_show_all(compose->window);
4705 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4708 if (compose->batch) {
4709 gtk_widget_show_all(compose->window);
4712 alertpanel_error(_("Could not queue message for sending:\n\n"
4713 "Charset conversion failed."));
4714 } else if (val == -5) {
4715 alertpanel_error(_("Could not queue message for sending:\n\n"
4716 "Couldn't get recipient encryption key."));
4717 } else if (val == -6) {
4719 } else if (val == -3) {
4720 if (privacy_peek_error())
4721 alertpanel_error(_("Could not queue message for sending:\n\n"
4722 "Signature failed: %s"), privacy_get_error());
4723 } else if (val == -2 && errno != 0) {
4724 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4726 alertpanel_error(_("Could not queue message for sending."));
4731 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4732 if (discard_window) {
4733 compose->sending = FALSE;
4734 compose_close(compose);
4735 /* No more compose access in the normal codepath
4736 * after this point! */
4741 alertpanel_error(_("The message was queued but could not be "
4742 "sent.\nUse \"Send queued messages\" from "
4743 "the main window to retry."));
4744 if (!discard_window) {
4751 if (msgpath == NULL) {
4752 msgpath = folder_item_fetch_msg(folder, msgnum);
4753 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4756 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4757 claws_unlink(msgpath);
4760 if (!discard_window) {
4762 if (!queued_removed)
4763 folder_item_remove_msg(folder, msgnum);
4764 folder_item_scan(folder);
4766 /* make sure we delete that */
4767 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4769 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4770 folder_item_remove_msg(folder, tmp->msgnum);
4771 procmsg_msginfo_free(tmp);
4778 if (!queued_removed)
4779 folder_item_remove_msg(folder, msgnum);
4780 folder_item_scan(folder);
4782 /* make sure we delete that */
4783 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4785 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4786 folder_item_remove_msg(folder, tmp->msgnum);
4787 procmsg_msginfo_free(tmp);
4790 if (!discard_window) {
4791 compose->sending = FALSE;
4792 compose_allow_user_actions (compose, TRUE);
4793 compose_close(compose);
4797 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4798 "the main window to retry."), errstr);
4801 alertpanel_error_log(_("The message was queued but could not be "
4802 "sent.\nUse \"Send queued messages\" from "
4803 "the main window to retry."));
4805 if (!discard_window) {
4814 toolbar_main_set_sensitive(mainwin);
4815 main_window_set_menu_sensitive(mainwin);
4821 compose_allow_user_actions (compose, TRUE);
4822 compose->sending = FALSE;
4823 compose->modified = TRUE;
4824 toolbar_main_set_sensitive(mainwin);
4825 main_window_set_menu_sensitive(mainwin);
4830 static gboolean compose_use_attach(Compose *compose)
4832 GtkTreeModel *model = gtk_tree_view_get_model
4833 (GTK_TREE_VIEW(compose->attach_clist));
4834 return gtk_tree_model_iter_n_children(model, NULL) > 0;
4837 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
4840 gchar buf[BUFFSIZE];
4842 gboolean first_to_address;
4843 gboolean first_cc_address;
4845 ComposeHeaderEntry *headerentry;
4846 const gchar *headerentryname;
4847 const gchar *cc_hdr;
4848 const gchar *to_hdr;
4849 gboolean err = FALSE;
4851 debug_print("Writing redirect header\n");
4853 cc_hdr = prefs_common_translated_header_name("Cc:");
4854 to_hdr = prefs_common_translated_header_name("To:");
4856 first_to_address = TRUE;
4857 for (list = compose->header_list; list; list = list->next) {
4858 headerentry = ((ComposeHeaderEntry *)list->data);
4859 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4861 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
4862 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4863 Xstrdup_a(str, entstr, return -1);
4865 if (str[0] != '\0') {
4866 compose_convert_header
4867 (compose, buf, sizeof(buf), str,
4868 strlen("Resent-To") + 2, TRUE);
4870 if (first_to_address) {
4871 err |= (fprintf(fp, "Resent-To: ") < 0);
4872 first_to_address = FALSE;
4874 err |= (fprintf(fp, ",") < 0);
4876 err |= (fprintf(fp, "%s", buf) < 0);
4880 if (!first_to_address) {
4881 err |= (fprintf(fp, "\n") < 0);
4884 first_cc_address = TRUE;
4885 for (list = compose->header_list; list; list = list->next) {
4886 headerentry = ((ComposeHeaderEntry *)list->data);
4887 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4889 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
4890 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4891 Xstrdup_a(str, strg, return -1);
4893 if (str[0] != '\0') {
4894 compose_convert_header
4895 (compose, buf, sizeof(buf), str,
4896 strlen("Resent-Cc") + 2, TRUE);
4898 if (first_cc_address) {
4899 err |= (fprintf(fp, "Resent-Cc: ") < 0);
4900 first_cc_address = FALSE;
4902 err |= (fprintf(fp, ",") < 0);
4904 err |= (fprintf(fp, "%s", buf) < 0);
4908 if (!first_cc_address) {
4909 err |= (fprintf(fp, "\n") < 0);
4912 return (err ? -1:0);
4915 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
4917 gchar buf[BUFFSIZE];
4919 const gchar *entstr;
4920 /* struct utsname utsbuf; */
4921 gboolean err = FALSE;
4923 g_return_val_if_fail(fp != NULL, -1);
4924 g_return_val_if_fail(compose->account != NULL, -1);
4925 g_return_val_if_fail(compose->account->address != NULL, -1);
4928 get_rfc822_date(buf, sizeof(buf));
4929 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
4932 if (compose->account->name && *compose->account->name) {
4933 compose_convert_header
4934 (compose, buf, sizeof(buf), compose->account->name,
4935 strlen("From: "), TRUE);
4936 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
4937 buf, compose->account->address) < 0);
4939 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
4942 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4943 if (*entstr != '\0') {
4944 Xstrdup_a(str, entstr, return -1);
4947 compose_convert_header(compose, buf, sizeof(buf), str,
4948 strlen("Subject: "), FALSE);
4949 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
4953 /* Resent-Message-ID */
4954 if (compose->account->set_domain && compose->account->domain) {
4955 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
4956 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
4957 g_snprintf(buf, sizeof(buf), "%s",
4958 strchr(compose->account->address, '@') ?
4959 strchr(compose->account->address, '@')+1 :
4960 compose->account->address);
4962 g_snprintf(buf, sizeof(buf), "%s", "");
4965 if (compose->account->gen_msgid) {
4966 generate_msgid(buf, sizeof(buf));
4967 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
4968 compose->msgid = g_strdup(buf);
4970 compose->msgid = NULL;
4973 if (compose_redirect_write_headers_from_headerlist(compose, fp))
4976 /* separator between header and body */
4977 err |= (fputs("\n", fp) == EOF);
4979 return (err ? -1:0);
4982 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
4986 gchar buf[BUFFSIZE];
4988 gboolean skip = FALSE;
4989 gboolean err = FALSE;
4990 gchar *not_included[]={
4991 "Return-Path:", "Delivered-To:", "Received:",
4992 "Subject:", "X-UIDL:", "AF:",
4993 "NF:", "PS:", "SRH:",
4994 "SFN:", "DSR:", "MID:",
4995 "CFG:", "PT:", "S:",
4996 "RQ:", "SSV:", "NSV:",
4997 "SSH:", "R:", "MAID:",
4998 "NAID:", "RMID:", "FMID:",
4999 "SCF:", "RRCPT:", "NG:",
5000 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5001 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5002 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5003 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5004 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5007 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5008 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5012 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5014 for (i = 0; not_included[i] != NULL; i++) {
5015 if (g_ascii_strncasecmp(buf, not_included[i],
5016 strlen(not_included[i])) == 0) {
5023 if (fputs(buf, fdest) == -1)
5026 if (!prefs_common.redirect_keep_from) {
5027 if (g_ascii_strncasecmp(buf, "From:",
5028 strlen("From:")) == 0) {
5029 err |= (fputs(" (by way of ", fdest) == EOF);
5030 if (compose->account->name
5031 && *compose->account->name) {
5032 compose_convert_header
5033 (compose, buf, sizeof(buf),
5034 compose->account->name,
5037 err |= (fprintf(fdest, "%s <%s>",
5039 compose->account->address) < 0);
5041 err |= (fprintf(fdest, "%s",
5042 compose->account->address) < 0);
5043 err |= (fputs(")", fdest) == EOF);
5047 if (fputs("\n", fdest) == -1)
5054 if (compose_redirect_write_headers(compose, fdest))
5057 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5058 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5071 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5073 GtkTextBuffer *buffer;
5074 GtkTextIter start, end;
5077 const gchar *out_codeset;
5078 EncodingType encoding = ENC_UNKNOWN;
5079 MimeInfo *mimemsg, *mimetext;
5081 const gchar *src_codeset = CS_INTERNAL;
5083 if (action == COMPOSE_WRITE_FOR_SEND)
5084 attach_parts = TRUE;
5086 /* create message MimeInfo */
5087 mimemsg = procmime_mimeinfo_new();
5088 mimemsg->type = MIMETYPE_MESSAGE;
5089 mimemsg->subtype = g_strdup("rfc822");
5090 mimemsg->content = MIMECONTENT_MEM;
5091 mimemsg->tmp = TRUE; /* must free content later */
5092 mimemsg->data.mem = compose_get_header(compose);
5094 /* Create text part MimeInfo */
5095 /* get all composed text */
5096 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5097 gtk_text_buffer_get_start_iter(buffer, &start);
5098 gtk_text_buffer_get_end_iter(buffer, &end);
5099 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5101 out_codeset = conv_get_charset_str(compose->out_encoding);
5103 if (!out_codeset && is_ascii_str(chars)) {
5104 out_codeset = CS_US_ASCII;
5105 } else if (prefs_common.outgoing_fallback_to_ascii &&
5106 is_ascii_str(chars)) {
5107 out_codeset = CS_US_ASCII;
5108 encoding = ENC_7BIT;
5112 gchar *test_conv_global_out = NULL;
5113 gchar *test_conv_reply = NULL;
5115 /* automatic mode. be automatic. */
5116 codeconv_set_strict(TRUE);
5118 out_codeset = conv_get_outgoing_charset_str();
5120 debug_print("trying to convert to %s\n", out_codeset);
5121 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5124 if (!test_conv_global_out && compose->orig_charset
5125 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5126 out_codeset = compose->orig_charset;
5127 debug_print("failure; trying to convert to %s\n", out_codeset);
5128 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5131 if (!test_conv_global_out && !test_conv_reply) {
5133 out_codeset = CS_INTERNAL;
5134 debug_print("failure; finally using %s\n", out_codeset);
5136 g_free(test_conv_global_out);
5137 g_free(test_conv_reply);
5138 codeconv_set_strict(FALSE);
5141 if (encoding == ENC_UNKNOWN) {
5142 if (prefs_common.encoding_method == CTE_BASE64)
5143 encoding = ENC_BASE64;
5144 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5145 encoding = ENC_QUOTED_PRINTABLE;
5146 else if (prefs_common.encoding_method == CTE_8BIT)
5147 encoding = ENC_8BIT;
5149 encoding = procmime_get_encoding_for_charset(out_codeset);
5152 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5153 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5155 if (action == COMPOSE_WRITE_FOR_SEND) {
5156 codeconv_set_strict(TRUE);
5157 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5158 codeconv_set_strict(FALSE);
5164 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5165 "to the specified %s charset.\n"
5166 "Send it as %s?"), out_codeset, src_codeset);
5167 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5168 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5171 if (aval != G_ALERTALTERNATE) {
5176 out_codeset = src_codeset;
5182 out_codeset = src_codeset;
5187 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5188 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5189 strstr(buf, "\nFrom ") != NULL) {
5190 encoding = ENC_QUOTED_PRINTABLE;
5194 mimetext = procmime_mimeinfo_new();
5195 mimetext->content = MIMECONTENT_MEM;
5196 mimetext->tmp = TRUE; /* must free content later */
5197 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5198 * and free the data, which we need later. */
5199 mimetext->data.mem = g_strdup(buf);
5200 mimetext->type = MIMETYPE_TEXT;
5201 mimetext->subtype = g_strdup("plain");
5202 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5203 g_strdup(out_codeset));
5205 /* protect trailing spaces when signing message */
5206 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5207 privacy_system_can_sign(compose->privacy_system)) {
5208 encoding = ENC_QUOTED_PRINTABLE;
5211 debug_print("main text: %zd bytes encoded as %s in %d\n",
5212 strlen(buf), out_codeset, encoding);
5214 /* check for line length limit */
5215 if (action == COMPOSE_WRITE_FOR_SEND &&
5216 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5217 check_line_length(buf, 1000, &line) < 0) {
5221 msg = g_strdup_printf
5222 (_("Line %d exceeds the line length limit (998 bytes).\n"
5223 "The contents of the message might be broken on the way to the delivery.\n"
5225 "Send it anyway?"), line + 1);
5226 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5228 if (aval != G_ALERTALTERNATE) {
5234 if (encoding != ENC_UNKNOWN)
5235 procmime_encode_content(mimetext, encoding);
5237 /* append attachment parts */
5238 if (compose_use_attach(compose) && attach_parts) {
5239 MimeInfo *mimempart;
5240 gchar *boundary = NULL;
5241 mimempart = procmime_mimeinfo_new();
5242 mimempart->content = MIMECONTENT_EMPTY;
5243 mimempart->type = MIMETYPE_MULTIPART;
5244 mimempart->subtype = g_strdup("mixed");
5248 boundary = generate_mime_boundary(NULL);
5249 } while (strstr(buf, boundary) != NULL);
5251 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5254 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5256 g_node_append(mimempart->node, mimetext->node);
5257 g_node_append(mimemsg->node, mimempart->node);
5259 compose_add_attachments(compose, mimempart);
5261 g_node_append(mimemsg->node, mimetext->node);
5265 /* sign message if sending */
5266 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5267 privacy_system_can_sign(compose->privacy_system))
5268 if (!privacy_sign(compose->privacy_system, mimemsg, compose->account))
5271 procmime_write_mimeinfo(mimemsg, fp);
5273 procmime_mimeinfo_free_all(mimemsg);
5278 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5280 GtkTextBuffer *buffer;
5281 GtkTextIter start, end;
5286 if ((fp = g_fopen(file, "wb")) == NULL) {
5287 FILE_OP_ERROR(file, "fopen");
5291 /* chmod for security */
5292 if (change_file_mode_rw(fp, file) < 0) {
5293 FILE_OP_ERROR(file, "chmod");
5294 g_warning("can't change file mode\n");
5297 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5298 gtk_text_buffer_get_start_iter(buffer, &start);
5299 gtk_text_buffer_get_end_iter(buffer, &end);
5300 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5302 chars = conv_codeset_strdup
5303 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5306 if (!chars) return -1;
5309 len = strlen(chars);
5310 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5311 FILE_OP_ERROR(file, "fwrite");
5320 if (fclose(fp) == EOF) {
5321 FILE_OP_ERROR(file, "fclose");
5328 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5331 MsgInfo *msginfo = compose->targetinfo;
5333 g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5334 if (!msginfo) return -1;
5336 if (!force && MSG_IS_LOCKED(msginfo->flags))
5339 item = msginfo->folder;
5340 g_return_val_if_fail(item != NULL, -1);
5342 if (procmsg_msg_exist(msginfo) &&
5343 (folder_has_parent_of_type(item, F_QUEUE) ||
5344 folder_has_parent_of_type(item, F_DRAFT)
5345 || msginfo == compose->autosaved_draft)) {
5346 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5347 g_warning("can't remove the old message\n");
5350 debug_print("removed reedit target %d\n", msginfo->msgnum);
5357 static void compose_remove_draft(Compose *compose)
5360 MsgInfo *msginfo = compose->targetinfo;
5361 drafts = account_get_special_folder(compose->account, F_DRAFT);
5363 if (procmsg_msg_exist(msginfo)) {
5364 folder_item_remove_msg(drafts, msginfo->msgnum);
5369 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5370 gboolean remove_reedit_target)
5372 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5375 static gboolean compose_warn_encryption(Compose *compose)
5377 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5378 AlertValue val = G_ALERTALTERNATE;
5380 if (warning == NULL)
5383 val = alertpanel_full(_("Encryption warning"), warning,
5384 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5385 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5386 if (val & G_ALERTDISABLE) {
5387 val &= ~G_ALERTDISABLE;
5388 if (val == G_ALERTALTERNATE)
5389 privacy_inhibit_encrypt_warning(compose->privacy_system,
5393 if (val == G_ALERTALTERNATE) {
5400 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5401 gchar **msgpath, gboolean check_subject,
5402 gboolean remove_reedit_target)
5409 static gboolean lock = FALSE;
5410 PrefsAccount *mailac = NULL, *newsac = NULL;
5411 gboolean err = FALSE;
5413 debug_print("queueing message...\n");
5414 g_return_val_if_fail(compose->account != NULL, -1);
5418 if (compose_check_entries(compose, check_subject) == FALSE) {
5420 if (compose->batch) {
5421 gtk_widget_show_all(compose->window);
5426 if (!compose->to_list && !compose->newsgroup_list) {
5427 g_warning("can't get recipient list.");
5432 if (compose->to_list) {
5433 if (compose->account->protocol != A_NNTP)
5434 mailac = compose->account;
5435 else if (cur_account && cur_account->protocol != A_NNTP)
5436 mailac = cur_account;
5437 else if (!(mailac = compose_current_mail_account())) {
5439 alertpanel_error(_("No account for sending mails available!"));
5444 if (compose->newsgroup_list) {
5445 if (compose->account->protocol == A_NNTP)
5446 newsac = compose->account;
5447 else if (!newsac->protocol != A_NNTP) {
5449 alertpanel_error(_("No account for posting news available!"));
5454 /* write queue header */
5455 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5456 G_DIR_SEPARATOR, compose, (guint) rand());
5457 debug_print("queuing to %s\n", tmp);
5458 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5459 FILE_OP_ERROR(tmp, "fopen");
5465 if (change_file_mode_rw(fp, tmp) < 0) {
5466 FILE_OP_ERROR(tmp, "chmod");
5467 g_warning("can't change file mode\n");
5470 /* queueing variables */
5471 err |= (fprintf(fp, "AF:\n") < 0);
5472 err |= (fprintf(fp, "NF:0\n") < 0);
5473 err |= (fprintf(fp, "PS:10\n") < 0);
5474 err |= (fprintf(fp, "SRH:1\n") < 0);
5475 err |= (fprintf(fp, "SFN:\n") < 0);
5476 err |= (fprintf(fp, "DSR:\n") < 0);
5478 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5480 err |= (fprintf(fp, "MID:\n") < 0);
5481 err |= (fprintf(fp, "CFG:\n") < 0);
5482 err |= (fprintf(fp, "PT:0\n") < 0);
5483 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5484 err |= (fprintf(fp, "RQ:\n") < 0);
5486 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5488 err |= (fprintf(fp, "SSV:\n") < 0);
5490 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5492 err |= (fprintf(fp, "NSV:\n") < 0);
5493 err |= (fprintf(fp, "SSH:\n") < 0);
5494 /* write recepient list */
5495 if (compose->to_list) {
5496 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5497 for (cur = compose->to_list->next; cur != NULL;
5499 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5500 err |= (fprintf(fp, "\n") < 0);
5502 /* write newsgroup list */
5503 if (compose->newsgroup_list) {
5504 err |= (fprintf(fp, "NG:") < 0);
5505 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5506 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5507 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5508 err |= (fprintf(fp, "\n") < 0);
5510 /* Sylpheed account IDs */
5512 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5514 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5517 if (compose->privacy_system != NULL) {
5518 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5519 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5520 if (compose->use_encryption) {
5522 if (!compose_warn_encryption(compose)) {
5529 if (mailac && mailac->encrypt_to_self) {
5530 GSList *tmp_list = g_slist_copy(compose->to_list);
5531 tmp_list = g_slist_append(tmp_list, compose->account->address);
5532 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5533 g_slist_free(tmp_list);
5535 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5537 if (encdata != NULL) {
5538 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5539 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5540 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5542 } /* else we finally dont want to encrypt */
5544 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5545 /* and if encdata was null, it means there's been a problem in
5557 /* Save copy folder */
5558 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5559 gchar *savefolderid;
5561 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
5562 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5563 g_free(savefolderid);
5565 /* Save copy folder */
5566 if (compose->return_receipt) {
5567 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5569 /* Message-ID of message replying to */
5570 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5573 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5574 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5577 /* Message-ID of message forwarding to */
5578 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5581 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5582 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5586 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5587 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5589 /* end of headers */
5590 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5592 if (compose->redirect_filename != NULL) {
5593 if (compose_redirect_write_to_file(compose, fp) < 0) {
5602 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5607 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5611 g_warning("failed to write queue message\n");
5618 if (fclose(fp) == EOF) {
5619 FILE_OP_ERROR(tmp, "fclose");
5626 if (item && *item) {
5629 queue = account_get_special_folder(compose->account, F_QUEUE);
5632 g_warning("can't find queue folder\n");
5638 folder_item_scan(queue);
5639 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5640 g_warning("can't queue the message\n");
5647 if (msgpath == NULL) {
5653 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5654 compose_remove_reedit_target(compose, FALSE);
5657 if ((msgnum != NULL) && (item != NULL)) {
5665 static void compose_add_attachments(Compose *compose, MimeInfo *parent)
5668 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5670 struct stat statbuf;
5671 gchar *type, *subtype;
5672 GtkTreeModel *model;
5675 model = gtk_tree_view_get_model(tree_view);
5677 if (!gtk_tree_model_get_iter_first(model, &iter))
5680 gtk_tree_model_get(model, &iter,
5684 mimepart = procmime_mimeinfo_new();
5685 mimepart->content = MIMECONTENT_FILE;
5686 mimepart->data.filename = g_strdup(ainfo->file);
5687 mimepart->tmp = FALSE; /* or we destroy our attachment */
5688 mimepart->offset = 0;
5690 g_stat(ainfo->file, &statbuf);
5691 mimepart->length = statbuf.st_size;
5693 type = g_strdup(ainfo->content_type);
5695 if (!strchr(type, '/')) {
5697 type = g_strdup("application/octet-stream");
5700 subtype = strchr(type, '/') + 1;
5701 *(subtype - 1) = '\0';
5702 mimepart->type = procmime_get_media_type(type);
5703 mimepart->subtype = g_strdup(subtype);
5706 if (mimepart->type == MIMETYPE_MESSAGE &&
5707 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5708 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5711 if (mimepart->type == MIMETYPE_APPLICATION &&
5712 !strcmp2(mimepart->subtype, "octet-stream"))
5713 g_hash_table_insert(mimepart->typeparameters,
5714 g_strdup("name"), g_strdup(ainfo->name));
5715 g_hash_table_insert(mimepart->dispositionparameters,
5716 g_strdup("filename"), g_strdup(ainfo->name));
5717 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5721 if (compose->use_signing) {
5722 if (ainfo->encoding == ENC_7BIT)
5723 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5724 else if (ainfo->encoding == ENC_8BIT)
5725 ainfo->encoding = ENC_BASE64;
5728 procmime_encode_content(mimepart, ainfo->encoding);
5730 g_node_append(parent->node, mimepart->node);
5731 } while (gtk_tree_model_iter_next(model, &iter));
5734 #define IS_IN_CUSTOM_HEADER(header) \
5735 (compose->account->add_customhdr && \
5736 custom_header_find(compose->account->customhdr_list, header) != NULL)
5738 static void compose_add_headerfield_from_headerlist(Compose *compose,
5740 const gchar *fieldname,
5741 const gchar *seperator)
5743 gchar *str, *fieldname_w_colon;
5744 gboolean add_field = FALSE;
5746 ComposeHeaderEntry *headerentry;
5747 const gchar *headerentryname;
5748 const gchar *trans_fieldname;
5751 if (IS_IN_CUSTOM_HEADER(fieldname))
5754 debug_print("Adding %s-fields\n", fieldname);
5756 fieldstr = g_string_sized_new(64);
5758 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5759 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5761 for (list = compose->header_list; list; list = list->next) {
5762 headerentry = ((ComposeHeaderEntry *)list->data);
5763 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5765 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5766 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5768 if (str[0] != '\0') {
5770 g_string_append(fieldstr, seperator);
5771 g_string_append(fieldstr, str);
5780 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5781 compose_convert_header
5782 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
5783 strlen(fieldname) + 2, TRUE);
5784 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5788 g_free(fieldname_w_colon);
5789 g_string_free(fieldstr, TRUE);
5794 static gchar *compose_get_header(Compose *compose)
5796 gchar buf[BUFFSIZE];
5797 const gchar *entry_str;
5801 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5803 gchar *from_name = NULL, *from_address = NULL;
5806 g_return_val_if_fail(compose->account != NULL, NULL);
5807 g_return_val_if_fail(compose->account->address != NULL, NULL);
5809 header = g_string_sized_new(64);
5812 get_rfc822_date(buf, sizeof(buf));
5813 g_string_append_printf(header, "Date: %s\n", buf);
5817 if (compose->account->name && *compose->account->name) {
5819 QUOTE_IF_REQUIRED(buf, compose->account->name);
5820 tmp = g_strdup_printf("%s <%s>",
5821 buf, compose->account->address);
5823 tmp = g_strdup_printf("%s",
5824 compose->account->address);
5826 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5827 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5829 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5830 from_address = g_strdup(compose->account->address);
5832 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5833 /* extract name and address */
5834 if (strstr(spec, " <") && strstr(spec, ">")) {
5835 from_address = g_strdup(strrchr(spec, '<')+1);
5836 *(strrchr(from_address, '>')) = '\0';
5837 from_name = g_strdup(spec);
5838 *(strrchr(from_name, '<')) = '\0';
5841 from_address = g_strdup(spec);
5848 if (from_name && *from_name) {
5849 compose_convert_header
5850 (compose, buf, sizeof(buf), from_name,
5851 strlen("From: "), TRUE);
5852 QUOTE_IF_REQUIRED(name, buf);
5854 g_string_append_printf(header, "From: %s <%s>\n",
5855 name, from_address);
5857 g_string_append_printf(header, "From: %s\n", from_address);
5860 g_free(from_address);
5863 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
5866 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
5869 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
5872 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
5875 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
5877 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
5880 compose_convert_header(compose, buf, sizeof(buf), str,
5881 strlen("Subject: "), FALSE);
5882 g_string_append_printf(header, "Subject: %s\n", buf);
5888 if (compose->account->set_domain && compose->account->domain) {
5889 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5890 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5891 g_snprintf(buf, sizeof(buf), "%s",
5892 strchr(compose->account->address, '@') ?
5893 strchr(compose->account->address, '@')+1 :
5894 compose->account->address);
5896 g_snprintf(buf, sizeof(buf), "%s", "");
5899 if (compose->account->gen_msgid) {
5900 generate_msgid(buf, sizeof(buf));
5901 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
5902 compose->msgid = g_strdup(buf);
5904 compose->msgid = NULL;
5907 if (compose->remove_references == FALSE) {
5909 if (compose->inreplyto && compose->to_list)
5910 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
5913 if (compose->references)
5914 g_string_append_printf(header, "References: %s\n", compose->references);
5918 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
5921 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
5924 if (compose->account->organization &&
5925 strlen(compose->account->organization) &&
5926 !IS_IN_CUSTOM_HEADER("Organization")) {
5927 compose_convert_header(compose, buf, sizeof(buf),
5928 compose->account->organization,
5929 strlen("Organization: "), FALSE);
5930 g_string_append_printf(header, "Organization: %s\n", buf);
5933 /* Program version and system info */
5934 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
5935 !compose->newsgroup_list) {
5936 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
5938 gtk_major_version, gtk_minor_version, gtk_micro_version,
5941 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
5942 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
5944 gtk_major_version, gtk_minor_version, gtk_micro_version,
5948 /* custom headers */
5949 if (compose->account->add_customhdr) {
5952 for (cur = compose->account->customhdr_list; cur != NULL;
5954 CustomHeader *chdr = (CustomHeader *)cur->data;
5956 if (custom_header_is_allowed(chdr->name)) {
5957 compose_convert_header
5958 (compose, buf, sizeof(buf),
5959 chdr->value ? chdr->value : "",
5960 strlen(chdr->name) + 2, FALSE);
5961 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
5966 /* Automatic Faces and X-Faces */
5967 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
5968 g_string_append_printf(header, "X-Face: %s\n", buf);
5970 else if (get_default_xface (buf, sizeof(buf)) == 0) {
5971 g_string_append_printf(header, "X-Face: %s\n", buf);
5973 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
5974 g_string_append_printf(header, "Face: %s\n", buf);
5976 else if (get_default_face (buf, sizeof(buf)) == 0) {
5977 g_string_append_printf(header, "Face: %s\n", buf);
5981 switch (compose->priority) {
5982 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
5983 "X-Priority: 1 (Highest)\n");
5985 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
5986 "X-Priority: 2 (High)\n");
5988 case PRIORITY_NORMAL: break;
5989 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
5990 "X-Priority: 4 (Low)\n");
5992 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
5993 "X-Priority: 5 (Lowest)\n");
5995 default: debug_print("compose: priority unknown : %d\n",
5999 /* Request Return Receipt */
6000 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6001 if (compose->return_receipt) {
6002 if (compose->account->name
6003 && *compose->account->name) {
6004 compose_convert_header(compose, buf, sizeof(buf),
6005 compose->account->name,
6006 strlen("Disposition-Notification-To: "),
6008 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6010 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6014 /* get special headers */
6015 for (list = compose->header_list; list; list = list->next) {
6016 ComposeHeaderEntry *headerentry;
6019 gchar *headername_wcolon;
6020 const gchar *headername_trans;
6023 gboolean standard_header = FALSE;
6025 headerentry = ((ComposeHeaderEntry *)list->data);
6027 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6029 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6034 if (!strstr(tmp, ":")) {
6035 headername_wcolon = g_strconcat(tmp, ":", NULL);
6036 headername = g_strdup(tmp);
6038 headername_wcolon = g_strdup(tmp);
6039 headername = g_strdup(strtok(tmp, ":"));
6043 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6044 Xstrdup_a(headervalue, entry_str, return NULL);
6045 subst_char(headervalue, '\r', ' ');
6046 subst_char(headervalue, '\n', ' ');
6047 string = std_headers;
6048 while (*string != NULL) {
6049 headername_trans = prefs_common_translated_header_name(*string);
6050 if (!strcmp(headername_trans, headername_wcolon))
6051 standard_header = TRUE;
6054 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6055 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6058 g_free(headername_wcolon);
6062 g_string_free(header, FALSE);
6067 #undef IS_IN_CUSTOM_HEADER
6069 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6070 gint header_len, gboolean addr_field)
6072 gchar *tmpstr = NULL;
6073 const gchar *out_codeset = NULL;
6075 g_return_if_fail(src != NULL);
6076 g_return_if_fail(dest != NULL);
6078 if (len < 1) return;
6080 tmpstr = g_strdup(src);
6082 subst_char(tmpstr, '\n', ' ');
6083 subst_char(tmpstr, '\r', ' ');
6086 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6087 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6088 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6093 codeconv_set_strict(TRUE);
6094 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6095 conv_get_charset_str(compose->out_encoding));
6096 codeconv_set_strict(FALSE);
6098 if (!dest || *dest == '\0') {
6099 gchar *test_conv_global_out = NULL;
6100 gchar *test_conv_reply = NULL;
6102 /* automatic mode. be automatic. */
6103 codeconv_set_strict(TRUE);
6105 out_codeset = conv_get_outgoing_charset_str();
6107 debug_print("trying to convert to %s\n", out_codeset);
6108 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6111 if (!test_conv_global_out && compose->orig_charset
6112 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6113 out_codeset = compose->orig_charset;
6114 debug_print("failure; trying to convert to %s\n", out_codeset);
6115 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6118 if (!test_conv_global_out && !test_conv_reply) {
6120 out_codeset = CS_INTERNAL;
6121 debug_print("finally using %s\n", out_codeset);
6123 g_free(test_conv_global_out);
6124 g_free(test_conv_reply);
6125 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6127 codeconv_set_strict(FALSE);
6132 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6136 g_return_if_fail(user_data != NULL);
6138 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6139 g_strstrip(address);
6140 if (*address != '\0') {
6141 gchar *name = procheader_get_fromname(address);
6142 extract_address(address);
6143 addressbook_add_contact(name, address, NULL, NULL);
6148 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6150 GtkWidget *menuitem;
6153 g_return_if_fail(menu != NULL);
6154 g_return_if_fail(GTK_IS_MENU_SHELL(menu));
6156 menuitem = gtk_separator_menu_item_new();
6157 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6158 gtk_widget_show(menuitem);
6160 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6161 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6163 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6164 g_strstrip(address);
6165 if (*address == '\0') {
6166 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6169 g_signal_connect(G_OBJECT(menuitem), "activate",
6170 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6171 gtk_widget_show(menuitem);
6174 static void compose_create_header_entry(Compose *compose)
6176 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6181 const gchar *header = NULL;
6182 ComposeHeaderEntry *headerentry;
6183 gboolean standard_header = FALSE;
6184 #if !(GTK_CHECK_VERSION(2,12,0))
6185 GtkTooltips *tips = compose->tooltips;
6188 headerentry = g_new0(ComposeHeaderEntry, 1);
6191 combo = gtk_combo_box_entry_new_text();
6193 while(*string != NULL) {
6194 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6195 (gchar*)prefs_common_translated_header_name(*string));
6198 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6199 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6200 G_CALLBACK(compose_grab_focus_cb), compose);
6201 gtk_widget_show(combo);
6202 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6203 compose->header_nextrow, compose->header_nextrow+1,
6204 GTK_SHRINK, GTK_FILL, 0, 0);
6205 if (compose->header_last) {
6206 const gchar *last_header_entry = gtk_entry_get_text(
6207 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6209 while (*string != NULL) {
6210 if (!strcmp(*string, last_header_entry))
6211 standard_header = TRUE;
6214 if (standard_header)
6215 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6217 if (!compose->header_last || !standard_header) {
6218 switch(compose->account->protocol) {
6220 header = prefs_common_translated_header_name("Newsgroups:");
6223 header = prefs_common_translated_header_name("To:");
6228 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6230 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6231 G_CALLBACK(compose_grab_focus_cb), compose);
6234 entry = gtk_entry_new();
6235 gtk_widget_show(entry);
6236 CLAWS_SET_TIP(entry,
6237 _("Use <tab> to autocomplete from addressbook"));
6238 gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2,
6239 compose->header_nextrow, compose->header_nextrow+1,
6240 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6242 g_signal_connect(G_OBJECT(entry), "key-press-event",
6243 G_CALLBACK(compose_headerentry_key_press_event_cb),
6245 g_signal_connect(G_OBJECT(entry), "changed",
6246 G_CALLBACK(compose_headerentry_changed_cb),
6248 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6249 G_CALLBACK(compose_grab_focus_cb), compose);
6252 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6253 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6254 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6255 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6256 G_CALLBACK(compose_header_drag_received_cb),
6258 g_signal_connect(G_OBJECT(entry), "drag-drop",
6259 G_CALLBACK(compose_drag_drop),
6261 g_signal_connect(G_OBJECT(entry), "populate-popup",
6262 G_CALLBACK(compose_entry_popup_extend),
6265 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6267 headerentry->compose = compose;
6268 headerentry->combo = combo;
6269 headerentry->entry = entry;
6270 headerentry->headernum = compose->header_nextrow;
6272 compose->header_nextrow++;
6273 compose->header_last = headerentry;
6274 compose->header_list =
6275 g_slist_append(compose->header_list,
6279 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text)
6281 ComposeHeaderEntry *last_header;
6283 last_header = compose->header_last;
6285 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((last_header->combo)))), header);
6286 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6289 static void compose_remove_header_entries(Compose *compose)
6292 for (list = compose->header_list; list; list = list->next) {
6293 ComposeHeaderEntry *headerentry =
6294 (ComposeHeaderEntry *)list->data;
6295 gtk_widget_destroy(headerentry->combo);
6296 gtk_widget_destroy(headerentry->entry);
6297 g_free(headerentry);
6299 compose->header_last = NULL;
6300 g_slist_free(compose->header_list);
6301 compose->header_list = NULL;
6302 compose->header_nextrow = 1;
6303 compose_create_header_entry(compose);
6306 static GtkWidget *compose_create_header(Compose *compose)
6308 GtkWidget *from_optmenu_hbox;
6309 GtkWidget *header_scrolledwin;
6310 GtkWidget *header_table;
6314 /* header labels and entries */
6315 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6316 gtk_widget_show(header_scrolledwin);
6317 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6319 header_table = gtk_table_new(2, 2, FALSE);
6320 gtk_widget_show(header_table);
6321 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6322 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6323 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6326 /* option menu for selecting accounts */
6327 from_optmenu_hbox = compose_account_option_menu_create(compose);
6328 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6329 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6332 compose->header_table = header_table;
6333 compose->header_list = NULL;
6334 compose->header_nextrow = count;
6336 compose_create_header_entry(compose);
6338 compose->table = NULL;
6340 return header_scrolledwin ;
6343 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6345 Compose *compose = (Compose *)data;
6346 GdkEventButton event;
6349 event.time = gtk_get_current_event_time();
6351 return attach_button_pressed(compose->attach_clist, &event, compose);
6354 static GtkWidget *compose_create_attach(Compose *compose)
6356 GtkWidget *attach_scrwin;
6357 GtkWidget *attach_clist;
6359 GtkListStore *store;
6360 GtkCellRenderer *renderer;
6361 GtkTreeViewColumn *column;
6362 GtkTreeSelection *selection;
6364 /* attachment list */
6365 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6366 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6367 GTK_POLICY_AUTOMATIC,
6368 GTK_POLICY_AUTOMATIC);
6369 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6371 store = gtk_list_store_new(N_ATTACH_COLS,
6376 G_TYPE_AUTO_POINTER,
6378 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6379 (GTK_TREE_MODEL(store)));
6380 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6381 g_object_unref(store);
6383 renderer = gtk_cell_renderer_text_new();
6384 column = gtk_tree_view_column_new_with_attributes
6385 (_("Mime type"), renderer, "text",
6386 COL_MIMETYPE, NULL);
6387 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6389 renderer = gtk_cell_renderer_text_new();
6390 column = gtk_tree_view_column_new_with_attributes
6391 (_("Size"), renderer, "text",
6393 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6395 renderer = gtk_cell_renderer_text_new();
6396 column = gtk_tree_view_column_new_with_attributes
6397 (_("Name"), renderer, "text",
6399 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6401 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6402 prefs_common.use_stripes_everywhere);
6403 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6404 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6406 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6407 G_CALLBACK(attach_selected), compose);
6408 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6409 G_CALLBACK(attach_button_pressed), compose);
6411 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6412 G_CALLBACK(popup_attach_button_pressed), compose);
6414 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6415 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6416 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6417 G_CALLBACK(popup_attach_button_pressed), compose);
6419 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6420 G_CALLBACK(attach_key_pressed), compose);
6423 gtk_drag_dest_set(attach_clist,
6424 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6425 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6426 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6427 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6428 G_CALLBACK(compose_attach_drag_received_cb),
6430 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6431 G_CALLBACK(compose_drag_drop),
6434 compose->attach_scrwin = attach_scrwin;
6435 compose->attach_clist = attach_clist;
6437 return attach_scrwin;
6440 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6441 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6443 static GtkWidget *compose_create_others(Compose *compose)
6446 GtkWidget *savemsg_checkbtn;
6447 GtkWidget *savemsg_entry;
6448 GtkWidget *savemsg_select;
6451 gchar *folderidentifier;
6453 /* Table for settings */
6454 table = gtk_table_new(3, 1, FALSE);
6455 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6456 gtk_widget_show(table);
6457 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6460 /* Save Message to folder */
6461 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6462 gtk_widget_show(savemsg_checkbtn);
6463 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6464 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6465 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6467 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6468 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6470 savemsg_entry = gtk_entry_new();
6471 gtk_widget_show(savemsg_entry);
6472 gtk_table_attach_defaults(GTK_TABLE(table), savemsg_entry, 1, 2, rowcount, rowcount + 1);
6473 gtk_editable_set_editable(GTK_EDITABLE(savemsg_entry), prefs_common.savemsg);
6474 g_signal_connect_after(G_OBJECT(savemsg_entry), "grab_focus",
6475 G_CALLBACK(compose_grab_focus_cb), compose);
6476 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6477 folderidentifier = folder_item_get_identifier(account_get_special_folder
6478 (compose->account, F_OUTBOX));
6479 gtk_entry_set_text(GTK_ENTRY(savemsg_entry), folderidentifier);
6480 g_free(folderidentifier);
6483 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6484 gtk_widget_show(savemsg_select);
6485 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6486 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6487 G_CALLBACK(compose_savemsg_select_cb),
6492 compose->savemsg_checkbtn = savemsg_checkbtn;
6493 compose->savemsg_entry = savemsg_entry;
6498 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6500 gtk_editable_set_editable(GTK_EDITABLE(compose->savemsg_entry),
6501 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6504 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6509 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6512 path = folder_item_get_identifier(dest);
6514 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), path);
6518 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6519 GdkAtom clip, GtkTextIter *insert_place);
6522 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6526 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6528 if (event->button == 3) {
6530 GtkTextIter sel_start, sel_end;
6531 gboolean stuff_selected;
6533 /* move the cursor to allow GtkAspell to check the word
6534 * under the mouse */
6535 if (event->x && event->y) {
6536 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6537 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6539 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6542 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6543 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6546 stuff_selected = gtk_text_buffer_get_selection_bounds(
6548 &sel_start, &sel_end);
6550 gtk_text_buffer_place_cursor (buffer, &iter);
6551 /* reselect stuff */
6553 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6554 gtk_text_buffer_select_range(buffer,
6555 &sel_start, &sel_end);
6557 return FALSE; /* pass the event so that the right-click goes through */
6560 if (event->button == 2) {
6565 /* get the middle-click position to paste at the correct place */
6566 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6567 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6569 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6572 entry_paste_clipboard(compose, text,
6573 prefs_common.linewrap_pastes,
6574 GDK_SELECTION_PRIMARY, &iter);
6582 static void compose_spell_menu_changed(void *data)
6584 Compose *compose = (Compose *)data;
6586 GtkWidget *menuitem;
6587 GtkWidget *parent_item;
6588 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6591 if (compose->gtkaspell == NULL)
6594 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6595 "/Menu/Spelling/Options");
6597 /* setting the submenu removes /Spelling/Options from the factory
6598 * so we need to save it */
6600 if (parent_item == NULL) {
6601 parent_item = compose->aspell_options_menu;
6602 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6604 compose->aspell_options_menu = parent_item;
6606 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6608 spell_menu = g_slist_reverse(spell_menu);
6609 for (items = spell_menu;
6610 items; items = items->next) {
6611 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6612 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6613 gtk_widget_show(GTK_WIDGET(menuitem));
6615 g_slist_free(spell_menu);
6617 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6622 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6624 Compose *compose = (Compose *)data;
6625 GdkEventButton event;
6628 event.time = gtk_get_current_event_time();
6632 return text_clicked(compose->text, &event, compose);
6635 static gboolean compose_force_window_origin = TRUE;
6636 static Compose *compose_create(PrefsAccount *account,
6645 GtkWidget *handlebox;
6647 GtkWidget *notebook;
6649 GtkWidget *attach_hbox;
6650 GtkWidget *attach_lab1;
6651 GtkWidget *attach_lab2;
6656 GtkWidget *subject_hbox;
6657 GtkWidget *subject_frame;
6658 GtkWidget *subject_entry;
6662 GtkWidget *edit_vbox;
6663 GtkWidget *ruler_hbox;
6665 GtkWidget *scrolledwin;
6667 GtkTextBuffer *buffer;
6668 GtkClipboard *clipboard;
6671 UndoMain *undostruct;
6673 gchar *titles[N_ATTACH_COLS];
6674 GtkWidget *popupmenu;
6675 GtkWidget *tmpl_menu;
6676 GtkActionGroup *action_group = NULL;
6679 GtkAspell * gtkaspell = NULL;
6682 static GdkGeometry geometry;
6684 g_return_val_if_fail(account != NULL, NULL);
6686 debug_print("Creating compose window...\n");
6687 compose = g_new0(Compose, 1);
6689 titles[COL_MIMETYPE] = _("MIME type");
6690 titles[COL_SIZE] = _("Size");
6691 titles[COL_NAME] = _("Name");
6693 compose->batch = batch;
6694 compose->account = account;
6695 compose->folder = folder;
6697 compose->mutex = g_mutex_new();
6698 compose->set_cursor_pos = -1;
6700 #if !(GTK_CHECK_VERSION(2,12,0))
6701 compose->tooltips = tips;
6704 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6706 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6707 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6709 if (!geometry.max_width) {
6710 geometry.max_width = gdk_screen_width();
6711 geometry.max_height = gdk_screen_height();
6714 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6715 &geometry, GDK_HINT_MAX_SIZE);
6716 if (!geometry.min_width) {
6717 geometry.min_width = 600;
6718 geometry.min_height = 480;
6720 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6721 &geometry, GDK_HINT_MIN_SIZE);
6723 #ifndef GENERIC_UMPC
6724 if (compose_force_window_origin)
6725 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
6726 prefs_common.compose_y);
6728 g_signal_connect(G_OBJECT(window), "delete_event",
6729 G_CALLBACK(compose_delete_cb), compose);
6730 MANAGE_WINDOW_SIGNALS_CONNECT(window);
6731 gtk_widget_realize(window);
6733 gtkut_widget_set_composer_icon(window);
6735 vbox = gtk_vbox_new(FALSE, 0);
6736 gtk_container_add(GTK_CONTAINER(window), vbox);
6738 compose->ui_manager = gtk_ui_manager_new();
6739 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
6740 G_N_ELEMENTS(compose_entries), (gpointer)compose);
6741 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
6742 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
6743 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
6744 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
6745 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
6746 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
6747 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
6748 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
6751 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
6753 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
6756 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
6757 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
6759 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
6761 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
6762 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
6763 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
6766 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
6767 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
6768 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6769 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
6770 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
6771 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
6772 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6773 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
6774 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6775 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
6778 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
6779 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
6780 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6782 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
6783 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
6784 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
6786 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
6787 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
6788 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
6789 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
6791 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
6793 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
6794 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
6795 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
6796 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
6797 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
6798 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
6799 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
6800 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
6801 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
6802 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
6803 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
6804 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
6805 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
6806 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
6807 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
6809 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6811 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
6812 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
6813 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
6814 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
6815 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
6817 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6819 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
6823 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
6824 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
6825 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
6826 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
6827 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
6828 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
6832 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
6833 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
6834 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
6835 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
6836 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
6838 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6839 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
6840 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6841 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
6842 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
6845 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6846 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
6847 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
6848 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
6849 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
6850 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
6851 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
6853 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6854 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
6855 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6856 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
6857 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6859 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
6861 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
6862 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6863 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
6864 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
6865 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6867 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
6868 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)
6869 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)
6870 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
6872 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
6874 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
6875 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)
6876 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)
6878 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
6880 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
6881 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)
6882 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
6884 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
6885 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)
6886 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
6888 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
6890 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
6891 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)
6892 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
6893 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
6894 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
6896 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
6897 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)
6898 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)
6899 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
6900 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
6902 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
6903 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
6904 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
6905 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
6906 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
6908 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
6909 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
6910 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)
6912 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
6913 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
6914 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
6918 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
6919 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
6920 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
6921 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6922 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
6923 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6926 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
6928 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
6929 gtk_widget_show_all(menubar);
6931 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
6933 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
6935 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
6938 if (prefs_common.toolbar_detachable) {
6939 handlebox = gtk_handle_box_new();
6941 handlebox = gtk_hbox_new(FALSE, 0);
6943 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
6945 gtk_widget_realize(handlebox);
6947 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
6950 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
6954 vbox2 = gtk_vbox_new(FALSE, 2);
6955 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
6956 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
6959 notebook = gtk_notebook_new();
6960 gtk_widget_set_size_request(notebook, -1, 130);
6961 gtk_widget_show(notebook);
6963 /* header labels and entries */
6964 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6965 compose_create_header(compose),
6966 gtk_label_new_with_mnemonic(_("Hea_der")));
6967 /* attachment list */
6968 attach_hbox = gtk_hbox_new(FALSE, 0);
6969 gtk_widget_show(attach_hbox);
6971 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
6972 gtk_widget_show(attach_lab1);
6973 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
6975 attach_lab2 = gtk_label_new("");
6976 gtk_widget_show(attach_lab2);
6977 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
6979 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6980 compose_create_attach(compose),
6983 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6984 compose_create_others(compose),
6985 gtk_label_new_with_mnemonic(_("Othe_rs")));
6988 subject_hbox = gtk_hbox_new(FALSE, 0);
6989 gtk_widget_show(subject_hbox);
6991 subject_frame = gtk_frame_new(NULL);
6992 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
6993 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
6994 gtk_widget_show(subject_frame);
6996 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
6997 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
6998 gtk_widget_show(subject);
7000 label = gtk_label_new(_("Subject:"));
7001 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7002 gtk_widget_show(label);
7004 subject_entry = gtk_entry_new();
7005 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7006 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7007 G_CALLBACK(compose_grab_focus_cb), compose);
7008 gtk_widget_show(subject_entry);
7009 compose->subject_entry = subject_entry;
7010 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7012 edit_vbox = gtk_vbox_new(FALSE, 0);
7014 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7017 ruler_hbox = gtk_hbox_new(FALSE, 0);
7018 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7020 ruler = gtk_shruler_new();
7021 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7022 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7026 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7027 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7028 GTK_POLICY_AUTOMATIC,
7029 GTK_POLICY_AUTOMATIC);
7030 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7032 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7033 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7035 text = gtk_text_view_new();
7036 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7037 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7038 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7039 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7040 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7042 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7044 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7045 G_CALLBACK(compose_edit_size_alloc),
7047 g_signal_connect(G_OBJECT(buffer), "changed",
7048 G_CALLBACK(compose_changed_cb), compose);
7049 g_signal_connect(G_OBJECT(text), "grab_focus",
7050 G_CALLBACK(compose_grab_focus_cb), compose);
7051 g_signal_connect(G_OBJECT(buffer), "insert_text",
7052 G_CALLBACK(text_inserted), compose);
7053 g_signal_connect(G_OBJECT(text), "button_press_event",
7054 G_CALLBACK(text_clicked), compose);
7056 g_signal_connect(G_OBJECT(text), "popup-menu",
7057 G_CALLBACK(compose_popup_menu), compose);
7059 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7060 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7061 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7062 G_CALLBACK(compose_popup_menu), compose);
7064 g_signal_connect(G_OBJECT(subject_entry), "changed",
7065 G_CALLBACK(compose_changed_cb), compose);
7068 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7069 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7070 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7071 g_signal_connect(G_OBJECT(text), "drag_data_received",
7072 G_CALLBACK(compose_insert_drag_received_cb),
7074 g_signal_connect(G_OBJECT(text), "drag-drop",
7075 G_CALLBACK(compose_drag_drop),
7077 gtk_widget_show_all(vbox);
7079 /* pane between attach clist and text */
7080 paned = gtk_vpaned_new();
7081 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7083 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7084 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7086 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7088 gtk_paned_add1(GTK_PANED(paned), notebook);
7089 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7090 gtk_widget_show_all(paned);
7093 if (prefs_common.textfont) {
7094 PangoFontDescription *font_desc;
7096 font_desc = pango_font_description_from_string
7097 (prefs_common.textfont);
7099 gtk_widget_modify_font(text, font_desc);
7100 pango_font_description_free(font_desc);
7104 gtk_action_group_add_actions(action_group, compose_popup_entries,
7105 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7106 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7107 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7108 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7109 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7110 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7111 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7113 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7115 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7116 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7117 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7119 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7121 undostruct = undo_init(text);
7122 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7125 address_completion_start(window);
7127 compose->window = window;
7128 compose->vbox = vbox;
7129 compose->menubar = menubar;
7130 compose->handlebox = handlebox;
7132 compose->vbox2 = vbox2;
7134 compose->paned = paned;
7136 compose->attach_label = attach_lab2;
7138 compose->notebook = notebook;
7139 compose->edit_vbox = edit_vbox;
7140 compose->ruler_hbox = ruler_hbox;
7141 compose->ruler = ruler;
7142 compose->scrolledwin = scrolledwin;
7143 compose->text = text;
7145 compose->focused_editable = NULL;
7147 compose->popupmenu = popupmenu;
7149 compose->tmpl_menu = tmpl_menu;
7151 compose->mode = mode;
7152 compose->rmode = mode;
7154 compose->targetinfo = NULL;
7155 compose->replyinfo = NULL;
7156 compose->fwdinfo = NULL;
7158 compose->replyto = NULL;
7160 compose->bcc = NULL;
7161 compose->followup_to = NULL;
7163 compose->ml_post = NULL;
7165 compose->inreplyto = NULL;
7166 compose->references = NULL;
7167 compose->msgid = NULL;
7168 compose->boundary = NULL;
7170 compose->autowrap = prefs_common.autowrap;
7171 compose->autoindent = prefs_common.auto_indent;
7172 compose->use_signing = FALSE;
7173 compose->use_encryption = FALSE;
7174 compose->privacy_system = NULL;
7176 compose->modified = FALSE;
7178 compose->return_receipt = FALSE;
7180 compose->to_list = NULL;
7181 compose->newsgroup_list = NULL;
7183 compose->undostruct = undostruct;
7185 compose->sig_str = NULL;
7187 compose->exteditor_file = NULL;
7188 compose->exteditor_pid = -1;
7189 compose->exteditor_tag = -1;
7190 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7193 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7194 if (mode != COMPOSE_REDIRECT) {
7195 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7196 strcmp(prefs_common.dictionary, "")) {
7197 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7198 prefs_common.alt_dictionary,
7199 conv_get_locale_charset_str(),
7200 prefs_common.misspelled_col,
7201 prefs_common.check_while_typing,
7202 prefs_common.recheck_when_changing_dict,
7203 prefs_common.use_alternate,
7204 prefs_common.use_both_dicts,
7205 GTK_TEXT_VIEW(text),
7206 GTK_WINDOW(compose->window),
7207 compose_spell_menu_changed,
7210 alertpanel_error(_("Spell checker could not "
7212 gtkaspell_checkers_strerror());
7213 gtkaspell_checkers_reset_error();
7215 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7219 compose->gtkaspell = gtkaspell;
7220 compose_spell_menu_changed(compose);
7223 compose_select_account(compose, account, TRUE);
7225 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7226 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7228 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7229 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7231 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7232 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7234 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7235 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7237 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7238 if (account->protocol != A_NNTP)
7239 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7240 prefs_common_translated_header_name("To:"));
7242 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7243 prefs_common_translated_header_name("Newsgroups:"));
7245 addressbook_set_target_compose(compose);
7247 if (mode != COMPOSE_REDIRECT)
7248 compose_set_template_menu(compose);
7250 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7253 compose_list = g_list_append(compose_list, compose);
7255 if (!prefs_common.show_ruler)
7256 gtk_widget_hide(ruler_hbox);
7258 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7261 compose->priority = PRIORITY_NORMAL;
7262 compose_update_priority_menu_item(compose);
7264 compose_set_out_encoding(compose);
7267 compose_update_actions_menu(compose);
7269 /* Privacy Systems menu */
7270 compose_update_privacy_systems_menu(compose);
7272 activate_privacy_system(compose, account, TRUE);
7273 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7275 gtk_widget_realize(window);
7277 gtk_widget_show(window);
7279 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7280 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7287 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7292 GtkWidget *optmenubox;
7295 GtkWidget *from_name = NULL;
7296 #if !(GTK_CHECK_VERSION(2,12,0))
7297 GtkTooltips *tips = compose->tooltips;
7300 gint num = 0, def_menu = 0;
7302 accounts = account_get_list();
7303 g_return_val_if_fail(accounts != NULL, NULL);
7305 optmenubox = gtk_event_box_new();
7306 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7307 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7309 hbox = gtk_hbox_new(FALSE, 6);
7310 from_name = gtk_entry_new();
7312 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7313 G_CALLBACK(compose_grab_focus_cb), compose);
7315 for (; accounts != NULL; accounts = accounts->next, num++) {
7316 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7317 gchar *name, *from = NULL;
7319 if (ac == compose->account) def_menu = num;
7321 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7324 if (ac == compose->account) {
7325 if (ac->name && *ac->name) {
7327 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7328 from = g_strdup_printf("%s <%s>",
7330 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7332 from = g_strdup_printf("%s",
7334 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7337 COMBOBOX_ADD(menu, name, ac->account_id);
7342 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7344 g_signal_connect(G_OBJECT(optmenu), "changed",
7345 G_CALLBACK(account_activated),
7347 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7348 G_CALLBACK(compose_entry_popup_extend),
7351 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7352 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7354 CLAWS_SET_TIP(optmenubox,
7355 _("Account to use for this email"));
7356 CLAWS_SET_TIP(from_name,
7357 _("Sender address to be used"));
7359 compose->from_name = from_name;
7364 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7366 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7367 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7368 Compose *compose = (Compose *) data;
7370 compose->priority = value;
7374 static void compose_reply_change_mode(Compose *compose,
7377 gboolean was_modified = compose->modified;
7379 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7381 g_return_if_fail(compose->replyinfo != NULL);
7383 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7385 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7387 if (action == COMPOSE_REPLY_TO_ALL)
7389 if (action == COMPOSE_REPLY_TO_SENDER)
7391 if (action == COMPOSE_REPLY_TO_LIST)
7394 compose_remove_header_entries(compose);
7395 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7396 if (compose->account->set_autocc && compose->account->auto_cc)
7397 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7399 if (compose->account->set_autobcc && compose->account->auto_bcc)
7400 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7402 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7403 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7404 compose_show_first_last_header(compose, TRUE);
7405 compose->modified = was_modified;
7406 compose_set_title(compose);
7409 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7411 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7412 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7413 Compose *compose = (Compose *) data;
7416 compose_reply_change_mode(compose, value);
7419 static void compose_update_priority_menu_item(Compose * compose)
7421 GtkWidget *menuitem = NULL;
7422 switch (compose->priority) {
7423 case PRIORITY_HIGHEST:
7424 menuitem = gtk_ui_manager_get_widget
7425 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7428 menuitem = gtk_ui_manager_get_widget
7429 (compose->ui_manager, "/Menu/Options/Priority/High");
7431 case PRIORITY_NORMAL:
7432 menuitem = gtk_ui_manager_get_widget
7433 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7436 menuitem = gtk_ui_manager_get_widget
7437 (compose->ui_manager, "/Menu/Options/Priority/Low");
7439 case PRIORITY_LOWEST:
7440 menuitem = gtk_ui_manager_get_widget
7441 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7444 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7447 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7449 Compose *compose = (Compose *) data;
7451 gboolean can_sign = FALSE, can_encrypt = FALSE;
7453 g_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7455 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7458 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7459 g_free(compose->privacy_system);
7460 compose->privacy_system = NULL;
7461 if (systemid != NULL) {
7462 compose->privacy_system = g_strdup(systemid);
7464 can_sign = privacy_system_can_sign(systemid);
7465 can_encrypt = privacy_system_can_encrypt(systemid);
7468 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7470 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7471 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7474 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7476 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7477 GtkWidget *menuitem = NULL;
7479 gboolean can_sign = FALSE, can_encrypt = FALSE;
7480 gboolean found = FALSE;
7482 if (compose->privacy_system != NULL) {
7484 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7485 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7486 g_return_if_fail(menuitem != NULL);
7488 amenu = GTK_MENU_SHELL(menuitem)->children;
7490 while (amenu != NULL) {
7491 GList *alist = amenu->next;
7493 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7494 if (systemid != NULL) {
7495 if (strcmp(systemid, compose->privacy_system) == 0 &&
7496 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7497 menuitem = GTK_WIDGET(amenu->data);
7499 can_sign = privacy_system_can_sign(systemid);
7500 can_encrypt = privacy_system_can_encrypt(systemid);
7504 } else if (strlen(compose->privacy_system) == 0 &&
7505 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7506 menuitem = GTK_WIDGET(amenu->data);
7509 can_encrypt = FALSE;
7516 if (menuitem != NULL)
7517 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7519 if (warn && !found && strlen(compose->privacy_system)) {
7520 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7521 "will not be able to sign or encrypt this message."),
7522 compose->privacy_system);
7526 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7527 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7530 static void compose_set_out_encoding(Compose *compose)
7532 CharSet out_encoding;
7533 const gchar *branch = NULL;
7534 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7536 switch(out_encoding) {
7537 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7538 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7539 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7540 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7541 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7542 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7543 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7544 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7545 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7546 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7547 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7548 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7549 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7550 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7551 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7552 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7553 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7554 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7555 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7556 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7557 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7558 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7559 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7560 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7561 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7562 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7563 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7564 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7565 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7566 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7567 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7568 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7570 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7573 static void compose_set_template_menu(Compose *compose)
7575 GSList *tmpl_list, *cur;
7579 tmpl_list = template_get_config();
7581 menu = gtk_menu_new();
7583 gtk_menu_set_accel_group (GTK_MENU (menu),
7584 gtk_ui_manager_get_accel_group(compose->ui_manager));
7585 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7586 Template *tmpl = (Template *)cur->data;
7587 gchar *accel_path = NULL;
7588 item = gtk_menu_item_new_with_label(tmpl->name);
7589 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7590 g_signal_connect(G_OBJECT(item), "activate",
7591 G_CALLBACK(compose_template_activate_cb),
7593 g_object_set_data(G_OBJECT(item), "template", tmpl);
7594 gtk_widget_show(item);
7595 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7596 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7600 gtk_widget_show(menu);
7601 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7604 void compose_update_actions_menu(Compose *compose)
7606 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7609 static void compose_update_privacy_systems_menu(Compose *compose)
7611 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7612 GSList *systems, *cur;
7614 GtkWidget *system_none;
7616 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7617 GtkWidget *privacy_menu = gtk_menu_new();
7619 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7620 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7622 g_signal_connect(G_OBJECT(system_none), "activate",
7623 G_CALLBACK(compose_set_privacy_system_cb), compose);
7625 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7626 gtk_widget_show(system_none);
7628 systems = privacy_get_system_ids();
7629 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7630 gchar *systemid = cur->data;
7632 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7633 widget = gtk_radio_menu_item_new_with_label(group,
7634 privacy_system_get_name(systemid));
7635 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7636 g_strdup(systemid), g_free);
7637 g_signal_connect(G_OBJECT(widget), "activate",
7638 G_CALLBACK(compose_set_privacy_system_cb), compose);
7640 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7641 gtk_widget_show(widget);
7644 g_slist_free(systems);
7645 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7646 gtk_widget_show_all(privacy_menu);
7647 gtk_widget_show_all(privacy_menuitem);
7650 void compose_reflect_prefs_all(void)
7655 for (cur = compose_list; cur != NULL; cur = cur->next) {
7656 compose = (Compose *)cur->data;
7657 compose_set_template_menu(compose);
7661 void compose_reflect_prefs_pixmap_theme(void)
7666 for (cur = compose_list; cur != NULL; cur = cur->next) {
7667 compose = (Compose *)cur->data;
7668 toolbar_update(TOOLBAR_COMPOSE, compose);
7672 static const gchar *compose_quote_char_from_context(Compose *compose)
7674 const gchar *qmark = NULL;
7676 g_return_val_if_fail(compose != NULL, NULL);
7678 switch (compose->mode) {
7679 /* use forward-specific quote char */
7680 case COMPOSE_FORWARD:
7681 case COMPOSE_FORWARD_AS_ATTACH:
7682 case COMPOSE_FORWARD_INLINE:
7683 if (compose->folder && compose->folder->prefs &&
7684 compose->folder->prefs->forward_with_format)
7685 qmark = compose->folder->prefs->forward_quotemark;
7686 else if (compose->account->forward_with_format)
7687 qmark = compose->account->forward_quotemark;
7689 qmark = prefs_common.fw_quotemark;
7692 /* use reply-specific quote char in all other modes */
7694 if (compose->folder && compose->folder->prefs &&
7695 compose->folder->prefs->reply_with_format)
7696 qmark = compose->folder->prefs->reply_quotemark;
7697 else if (compose->account->reply_with_format)
7698 qmark = compose->account->reply_quotemark;
7700 qmark = prefs_common.quotemark;
7704 if (qmark == NULL || *qmark == '\0')
7710 static void compose_template_apply(Compose *compose, Template *tmpl,
7714 GtkTextBuffer *buffer;
7718 gchar *parsed_str = NULL;
7719 gint cursor_pos = 0;
7720 const gchar *err_msg = _("Template body format error at line %d.");
7723 /* process the body */
7725 text = GTK_TEXT_VIEW(compose->text);
7726 buffer = gtk_text_view_get_buffer(text);
7729 qmark = compose_quote_char_from_context(compose);
7731 if (compose->replyinfo != NULL) {
7734 gtk_text_buffer_set_text(buffer, "", -1);
7735 mark = gtk_text_buffer_get_insert(buffer);
7736 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7738 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7739 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7741 } else if (compose->fwdinfo != NULL) {
7744 gtk_text_buffer_set_text(buffer, "", -1);
7745 mark = gtk_text_buffer_get_insert(buffer);
7746 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7748 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7749 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7752 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7754 GtkTextIter start, end;
7757 gtk_text_buffer_get_start_iter(buffer, &start);
7758 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7759 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7761 /* clear the buffer now */
7763 gtk_text_buffer_set_text(buffer, "", -1);
7765 parsed_str = compose_quote_fmt(compose, dummyinfo,
7766 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7767 procmsg_msginfo_free( dummyinfo );
7773 gtk_text_buffer_set_text(buffer, "", -1);
7774 mark = gtk_text_buffer_get_insert(buffer);
7775 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7778 if (replace && parsed_str && compose->account->auto_sig)
7779 compose_insert_sig(compose, FALSE);
7781 if (replace && parsed_str) {
7782 gtk_text_buffer_get_start_iter(buffer, &iter);
7783 gtk_text_buffer_place_cursor(buffer, &iter);
7787 cursor_pos = quote_fmt_get_cursor_pos();
7788 compose->set_cursor_pos = cursor_pos;
7789 if (cursor_pos == -1)
7791 gtk_text_buffer_get_start_iter(buffer, &iter);
7792 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7793 gtk_text_buffer_place_cursor(buffer, &iter);
7796 /* process the other fields */
7798 compose_template_apply_fields(compose, tmpl);
7799 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7800 quote_fmt_reset_vartable();
7801 compose_changed_cb(NULL, compose);
7804 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7806 MsgInfo* dummyinfo = NULL;
7807 MsgInfo *msginfo = NULL;
7810 if (compose->replyinfo != NULL)
7811 msginfo = compose->replyinfo;
7812 else if (compose->fwdinfo != NULL)
7813 msginfo = compose->fwdinfo;
7815 dummyinfo = compose_msginfo_new_from_compose(compose);
7816 msginfo = dummyinfo;
7819 if (tmpl->from && *tmpl->from != '\0') {
7821 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7822 compose->gtkaspell);
7824 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7826 quote_fmt_scan_string(tmpl->from);
7829 buf = quote_fmt_get_buffer();
7831 alertpanel_error(_("Template From format error."));
7833 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
7837 if (tmpl->to && *tmpl->to != '\0') {
7839 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7840 compose->gtkaspell);
7842 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7844 quote_fmt_scan_string(tmpl->to);
7847 buf = quote_fmt_get_buffer();
7849 alertpanel_error(_("Template To format error."));
7851 compose_entry_append(compose, buf, COMPOSE_TO);
7855 if (tmpl->cc && *tmpl->cc != '\0') {
7857 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7858 compose->gtkaspell);
7860 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7862 quote_fmt_scan_string(tmpl->cc);
7865 buf = quote_fmt_get_buffer();
7867 alertpanel_error(_("Template Cc format error."));
7869 compose_entry_append(compose, buf, COMPOSE_CC);
7873 if (tmpl->bcc && *tmpl->bcc != '\0') {
7875 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7876 compose->gtkaspell);
7878 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7880 quote_fmt_scan_string(tmpl->bcc);
7883 buf = quote_fmt_get_buffer();
7885 alertpanel_error(_("Template Bcc format error."));
7887 compose_entry_append(compose, buf, COMPOSE_BCC);
7891 /* process the subject */
7892 if (tmpl->subject && *tmpl->subject != '\0') {
7894 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7895 compose->gtkaspell);
7897 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7899 quote_fmt_scan_string(tmpl->subject);
7902 buf = quote_fmt_get_buffer();
7904 alertpanel_error(_("Template subject format error."));
7906 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
7910 procmsg_msginfo_free( dummyinfo );
7913 static void compose_destroy(Compose *compose)
7915 GtkTextBuffer *buffer;
7916 GtkClipboard *clipboard;
7918 compose_list = g_list_remove(compose_list, compose);
7920 if (compose->updating) {
7921 debug_print("danger, not destroying anything now\n");
7922 compose->deferred_destroy = TRUE;
7925 /* NOTE: address_completion_end() does nothing with the window
7926 * however this may change. */
7927 address_completion_end(compose->window);
7929 slist_free_strings(compose->to_list);
7930 g_slist_free(compose->to_list);
7931 slist_free_strings(compose->newsgroup_list);
7932 g_slist_free(compose->newsgroup_list);
7933 slist_free_strings(compose->header_list);
7934 g_slist_free(compose->header_list);
7936 procmsg_msginfo_free(compose->targetinfo);
7937 procmsg_msginfo_free(compose->replyinfo);
7938 procmsg_msginfo_free(compose->fwdinfo);
7940 g_free(compose->replyto);
7941 g_free(compose->cc);
7942 g_free(compose->bcc);
7943 g_free(compose->newsgroups);
7944 g_free(compose->followup_to);
7946 g_free(compose->ml_post);
7948 g_free(compose->inreplyto);
7949 g_free(compose->references);
7950 g_free(compose->msgid);
7951 g_free(compose->boundary);
7953 g_free(compose->redirect_filename);
7954 if (compose->undostruct)
7955 undo_destroy(compose->undostruct);
7957 g_free(compose->sig_str);
7959 g_free(compose->exteditor_file);
7961 g_free(compose->orig_charset);
7963 g_free(compose->privacy_system);
7965 if (addressbook_get_target_compose() == compose)
7966 addressbook_set_target_compose(NULL);
7969 if (compose->gtkaspell) {
7970 gtkaspell_delete(compose->gtkaspell);
7971 compose->gtkaspell = NULL;
7975 if (!compose->batch) {
7976 prefs_common.compose_width = compose->scrolledwin->allocation.width;
7977 prefs_common.compose_height = compose->window->allocation.height;
7980 if (!gtk_widget_get_parent(compose->paned))
7981 gtk_widget_destroy(compose->paned);
7982 gtk_widget_destroy(compose->popupmenu);
7984 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
7985 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7986 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
7988 gtk_widget_destroy(compose->window);
7989 toolbar_destroy(compose->toolbar);
7990 g_free(compose->toolbar);
7991 g_mutex_free(compose->mutex);
7995 static void compose_attach_info_free(AttachInfo *ainfo)
7997 g_free(ainfo->file);
7998 g_free(ainfo->content_type);
7999 g_free(ainfo->name);
8003 static void compose_attach_update_label(Compose *compose)
8008 GtkTreeModel *model;
8013 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8014 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8015 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8019 while(gtk_tree_model_iter_next(model, &iter))
8022 text = g_strdup_printf("(%d)", i);
8023 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8027 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8029 Compose *compose = (Compose *)data;
8030 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8031 GtkTreeSelection *selection;
8033 GtkTreeModel *model;
8035 selection = gtk_tree_view_get_selection(tree_view);
8036 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8041 for (cur = sel; cur != NULL; cur = cur->next) {
8042 GtkTreePath *path = cur->data;
8043 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8046 gtk_tree_path_free(path);
8049 for (cur = sel; cur != NULL; cur = cur->next) {
8050 GtkTreeRowReference *ref = cur->data;
8051 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8054 if (gtk_tree_model_get_iter(model, &iter, path))
8055 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8057 gtk_tree_path_free(path);
8058 gtk_tree_row_reference_free(ref);
8062 compose_attach_update_label(compose);
8065 static struct _AttachProperty
8068 GtkWidget *mimetype_entry;
8069 GtkWidget *encoding_optmenu;
8070 GtkWidget *path_entry;
8071 GtkWidget *filename_entry;
8073 GtkWidget *cancel_btn;
8076 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8078 gtk_tree_path_free((GtkTreePath *)ptr);
8081 static void compose_attach_property(GtkAction *action, gpointer data)
8083 Compose *compose = (Compose *)data;
8084 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8086 GtkComboBox *optmenu;
8087 GtkTreeSelection *selection;
8089 GtkTreeModel *model;
8092 static gboolean cancelled;
8094 /* only if one selected */
8095 selection = gtk_tree_view_get_selection(tree_view);
8096 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8099 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8103 path = (GtkTreePath *) sel->data;
8104 gtk_tree_model_get_iter(model, &iter, path);
8105 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8108 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8114 if (!attach_prop.window)
8115 compose_attach_property_create(&cancelled);
8116 gtk_widget_grab_focus(attach_prop.ok_btn);
8117 gtk_widget_show(attach_prop.window);
8118 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8120 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8121 if (ainfo->encoding == ENC_UNKNOWN)
8122 combobox_select_by_data(optmenu, ENC_BASE64);
8124 combobox_select_by_data(optmenu, ainfo->encoding);
8126 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8127 ainfo->content_type ? ainfo->content_type : "");
8128 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8129 ainfo->file ? ainfo->file : "");
8130 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8131 ainfo->name ? ainfo->name : "");
8134 const gchar *entry_text;
8136 gchar *cnttype = NULL;
8143 gtk_widget_hide(attach_prop.window);
8148 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8149 if (*entry_text != '\0') {
8152 text = g_strstrip(g_strdup(entry_text));
8153 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8154 cnttype = g_strdup(text);
8157 alertpanel_error(_("Invalid MIME type."));
8163 ainfo->encoding = combobox_get_active_data(optmenu);
8165 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8166 if (*entry_text != '\0') {
8167 if (is_file_exist(entry_text) &&
8168 (size = get_file_size(entry_text)) > 0)
8169 file = g_strdup(entry_text);
8172 (_("File doesn't exist or is empty."));
8178 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8179 if (*entry_text != '\0') {
8180 g_free(ainfo->name);
8181 ainfo->name = g_strdup(entry_text);
8185 g_free(ainfo->content_type);
8186 ainfo->content_type = cnttype;
8189 g_free(ainfo->file);
8193 ainfo->size = (goffset)size;
8195 /* update tree store */
8196 text = to_human_readable(ainfo->size);
8197 gtk_tree_model_get_iter(model, &iter, path);
8198 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8199 COL_MIMETYPE, ainfo->content_type,
8201 COL_NAME, ainfo->name,
8207 gtk_tree_path_free(path);
8210 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8212 label = gtk_label_new(str); \
8213 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8214 GTK_FILL, 0, 0, 0); \
8215 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8217 entry = gtk_entry_new(); \
8218 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8219 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8222 static void compose_attach_property_create(gboolean *cancelled)
8228 GtkWidget *mimetype_entry;
8231 GtkListStore *optmenu_menu;
8232 GtkWidget *path_entry;
8233 GtkWidget *filename_entry;
8236 GtkWidget *cancel_btn;
8237 GList *mime_type_list, *strlist;
8240 debug_print("Creating attach_property window...\n");
8242 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8243 gtk_widget_set_size_request(window, 480, -1);
8244 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8245 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8246 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8247 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8248 g_signal_connect(G_OBJECT(window), "delete_event",
8249 G_CALLBACK(attach_property_delete_event),
8251 g_signal_connect(G_OBJECT(window), "key_press_event",
8252 G_CALLBACK(attach_property_key_pressed),
8255 vbox = gtk_vbox_new(FALSE, 8);
8256 gtk_container_add(GTK_CONTAINER(window), vbox);
8258 table = gtk_table_new(4, 2, FALSE);
8259 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8260 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8261 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8263 label = gtk_label_new(_("MIME type"));
8264 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8266 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8267 mimetype_entry = gtk_combo_box_entry_new_text();
8268 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8269 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8271 /* stuff with list */
8272 mime_type_list = procmime_get_mime_type_list();
8274 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8275 MimeType *type = (MimeType *) mime_type_list->data;
8278 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8280 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8283 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8284 (GCompareFunc)strcmp2);
8287 for (mime_type_list = strlist; mime_type_list != NULL;
8288 mime_type_list = mime_type_list->next) {
8289 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8290 g_free(mime_type_list->data);
8292 g_list_free(strlist);
8293 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8294 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8296 label = gtk_label_new(_("Encoding"));
8297 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8299 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8301 hbox = gtk_hbox_new(FALSE, 0);
8302 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8303 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8305 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8306 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8308 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8309 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8310 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8311 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8312 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8314 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8316 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8317 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8319 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8320 &ok_btn, GTK_STOCK_OK,
8322 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8323 gtk_widget_grab_default(ok_btn);
8325 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8326 G_CALLBACK(attach_property_ok),
8328 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8329 G_CALLBACK(attach_property_cancel),
8332 gtk_widget_show_all(vbox);
8334 attach_prop.window = window;
8335 attach_prop.mimetype_entry = mimetype_entry;
8336 attach_prop.encoding_optmenu = optmenu;
8337 attach_prop.path_entry = path_entry;
8338 attach_prop.filename_entry = filename_entry;
8339 attach_prop.ok_btn = ok_btn;
8340 attach_prop.cancel_btn = cancel_btn;
8343 #undef SET_LABEL_AND_ENTRY
8345 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8351 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8357 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8358 gboolean *cancelled)
8366 static gboolean attach_property_key_pressed(GtkWidget *widget,
8368 gboolean *cancelled)
8370 if (event && event->keyval == GDK_Escape) {
8374 if (event && event->keyval == GDK_Return) {
8381 static void compose_exec_ext_editor(Compose *compose)
8388 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8389 G_DIR_SEPARATOR, compose);
8391 if (pipe(pipe_fds) < 0) {
8397 if ((pid = fork()) < 0) {
8404 /* close the write side of the pipe */
8407 compose->exteditor_file = g_strdup(tmp);
8408 compose->exteditor_pid = pid;
8410 compose_set_ext_editor_sensitive(compose, FALSE);
8413 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8415 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8417 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8421 } else { /* process-monitoring process */
8427 /* close the read side of the pipe */
8430 if (compose_write_body_to_file(compose, tmp) < 0) {
8431 fd_write_all(pipe_fds[1], "2\n", 2);
8435 pid_ed = compose_exec_ext_editor_real(tmp);
8437 fd_write_all(pipe_fds[1], "1\n", 2);
8441 /* wait until editor is terminated */
8442 waitpid(pid_ed, NULL, 0);
8444 fd_write_all(pipe_fds[1], "0\n", 2);
8451 #endif /* G_OS_UNIX */
8455 static gint compose_exec_ext_editor_real(const gchar *file)
8462 g_return_val_if_fail(file != NULL, -1);
8464 if ((pid = fork()) < 0) {
8469 if (pid != 0) return pid;
8471 /* grandchild process */
8473 if (setpgid(0, getppid()))
8476 if (prefs_common_get_ext_editor_cmd() &&
8477 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8478 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8479 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8481 if (prefs_common_get_ext_editor_cmd())
8482 g_warning("External editor command-line is invalid: '%s'\n",
8483 prefs_common_get_ext_editor_cmd());
8484 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8487 cmdline = strsplit_with_quote(buf, " ", 1024);
8488 execvp(cmdline[0], cmdline);
8491 g_strfreev(cmdline);
8496 static gboolean compose_ext_editor_kill(Compose *compose)
8498 pid_t pgid = compose->exteditor_pid * -1;
8501 ret = kill(pgid, 0);
8503 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8507 msg = g_strdup_printf
8508 (_("The external editor is still working.\n"
8509 "Force terminating the process?\n"
8510 "process group id: %d"), -pgid);
8511 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8512 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8516 if (val == G_ALERTALTERNATE) {
8517 g_source_remove(compose->exteditor_tag);
8518 g_io_channel_shutdown(compose->exteditor_ch,
8520 g_io_channel_unref(compose->exteditor_ch);
8522 if (kill(pgid, SIGTERM) < 0) perror("kill");
8523 waitpid(compose->exteditor_pid, NULL, 0);
8525 g_warning("Terminated process group id: %d", -pgid);
8526 g_warning("Temporary file: %s",
8527 compose->exteditor_file);
8529 compose_set_ext_editor_sensitive(compose, TRUE);
8531 g_free(compose->exteditor_file);
8532 compose->exteditor_file = NULL;
8533 compose->exteditor_pid = -1;
8534 compose->exteditor_ch = NULL;
8535 compose->exteditor_tag = -1;
8543 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8547 Compose *compose = (Compose *)data;
8550 debug_print(_("Compose: input from monitoring process\n"));
8552 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8554 g_io_channel_shutdown(source, FALSE, NULL);
8555 g_io_channel_unref(source);
8557 waitpid(compose->exteditor_pid, NULL, 0);
8559 if (buf[0] == '0') { /* success */
8560 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8561 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8563 gtk_text_buffer_set_text(buffer, "", -1);
8564 compose_insert_file(compose, compose->exteditor_file);
8565 compose_changed_cb(NULL, compose);
8566 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8568 if (claws_unlink(compose->exteditor_file) < 0)
8569 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8570 } else if (buf[0] == '1') { /* failed */
8571 g_warning("Couldn't exec external editor\n");
8572 if (claws_unlink(compose->exteditor_file) < 0)
8573 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8574 } else if (buf[0] == '2') {
8575 g_warning("Couldn't write to file\n");
8576 } else if (buf[0] == '3') {
8577 g_warning("Pipe read failed\n");
8580 compose_set_ext_editor_sensitive(compose, TRUE);
8582 g_free(compose->exteditor_file);
8583 compose->exteditor_file = NULL;
8584 compose->exteditor_pid = -1;
8585 compose->exteditor_ch = NULL;
8586 compose->exteditor_tag = -1;
8591 static void compose_set_ext_editor_sensitive(Compose *compose,
8594 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8595 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8596 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8597 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8598 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8599 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8600 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8602 gtk_widget_set_sensitive(compose->text, sensitive);
8603 if (compose->toolbar->send_btn)
8604 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8605 if (compose->toolbar->sendl_btn)
8606 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8607 if (compose->toolbar->draft_btn)
8608 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8609 if (compose->toolbar->insert_btn)
8610 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8611 if (compose->toolbar->sig_btn)
8612 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8613 if (compose->toolbar->exteditor_btn)
8614 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8615 if (compose->toolbar->linewrap_current_btn)
8616 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8617 if (compose->toolbar->linewrap_all_btn)
8618 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8620 #endif /* G_OS_UNIX */
8623 * compose_undo_state_changed:
8625 * Change the sensivity of the menuentries undo and redo
8627 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8628 gint redo_state, gpointer data)
8630 Compose *compose = (Compose *)data;
8632 switch (undo_state) {
8633 case UNDO_STATE_TRUE:
8634 if (!undostruct->undo_state) {
8635 undostruct->undo_state = TRUE;
8636 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8639 case UNDO_STATE_FALSE:
8640 if (undostruct->undo_state) {
8641 undostruct->undo_state = FALSE;
8642 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8645 case UNDO_STATE_UNCHANGED:
8647 case UNDO_STATE_REFRESH:
8648 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8651 g_warning("Undo state not recognized");
8655 switch (redo_state) {
8656 case UNDO_STATE_TRUE:
8657 if (!undostruct->redo_state) {
8658 undostruct->redo_state = TRUE;
8659 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8662 case UNDO_STATE_FALSE:
8663 if (undostruct->redo_state) {
8664 undostruct->redo_state = FALSE;
8665 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8668 case UNDO_STATE_UNCHANGED:
8670 case UNDO_STATE_REFRESH:
8671 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8674 g_warning("Redo state not recognized");
8679 /* callback functions */
8681 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8682 * includes "non-client" (windows-izm) in calculation, so this calculation
8683 * may not be accurate.
8685 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8686 GtkAllocation *allocation,
8687 GtkSHRuler *shruler)
8689 if (prefs_common.show_ruler) {
8690 gint char_width = 0, char_height = 0;
8691 gint line_width_in_chars;
8693 gtkut_get_font_size(GTK_WIDGET(widget),
8694 &char_width, &char_height);
8695 line_width_in_chars =
8696 (allocation->width - allocation->x) / char_width;
8698 /* got the maximum */
8699 gtk_ruler_set_range(GTK_RULER(shruler),
8700 0.0, line_width_in_chars, 0,
8701 /*line_width_in_chars*/ char_width);
8707 static void account_activated(GtkComboBox *optmenu, gpointer data)
8709 Compose *compose = (Compose *)data;
8712 gchar *folderidentifier;
8713 gint account_id = 0;
8717 /* Get ID of active account in the combo box */
8718 menu = gtk_combo_box_get_model(optmenu);
8719 gtk_combo_box_get_active_iter(optmenu, &iter);
8720 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8722 ac = account_find_from_id(account_id);
8723 g_return_if_fail(ac != NULL);
8725 if (ac != compose->account)
8726 compose_select_account(compose, ac, FALSE);
8728 /* Set message save folder */
8729 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8730 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8732 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8733 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8735 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8736 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8737 folderidentifier = folder_item_get_identifier(account_get_special_folder
8738 (compose->account, F_OUTBOX));
8739 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
8740 g_free(folderidentifier);
8744 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8745 GtkTreeViewColumn *column, Compose *compose)
8747 compose_attach_property(NULL, compose);
8750 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8753 Compose *compose = (Compose *)data;
8754 GtkTreeSelection *attach_selection;
8755 gint attach_nr_selected;
8757 if (!event) return FALSE;
8759 if (event->button == 3) {
8760 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8761 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8763 if (attach_nr_selected > 0)
8765 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8766 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8768 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8769 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8772 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8773 NULL, NULL, event->button, event->time);
8780 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8783 Compose *compose = (Compose *)data;
8785 if (!event) return FALSE;
8787 switch (event->keyval) {
8789 compose_attach_remove_selected(NULL, compose);
8795 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8797 toolbar_comp_set_sensitive(compose, allow);
8798 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8799 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8801 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8803 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8804 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8805 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8807 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8811 static void compose_send_cb(GtkAction *action, gpointer data)
8813 Compose *compose = (Compose *)data;
8815 if (prefs_common.work_offline &&
8816 !inc_offline_should_override(TRUE,
8817 _("Claws Mail needs network access in order "
8818 "to send this email.")))
8821 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8822 g_source_remove(compose->draft_timeout_tag);
8823 compose->draft_timeout_tag = -1;
8826 compose_send(compose);
8829 static void compose_send_later_cb(GtkAction *action, gpointer data)
8831 Compose *compose = (Compose *)data;
8835 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8839 compose_close(compose);
8840 } else if (val == -1) {
8841 alertpanel_error(_("Could not queue message."));
8842 } else if (val == -2) {
8843 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8844 } else if (val == -3) {
8845 if (privacy_peek_error())
8846 alertpanel_error(_("Could not queue message for sending:\n\n"
8847 "Signature failed: %s"), privacy_get_error());
8848 } else if (val == -4) {
8849 alertpanel_error(_("Could not queue message for sending:\n\n"
8850 "Charset conversion failed."));
8851 } else if (val == -5) {
8852 alertpanel_error(_("Could not queue message for sending:\n\n"
8853 "Couldn't get recipient encryption key."));
8854 } else if (val == -6) {
8857 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
8860 #define DRAFTED_AT_EXIT "drafted_at_exit"
8861 static void compose_register_draft(MsgInfo *info)
8863 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8864 DRAFTED_AT_EXIT, NULL);
8865 FILE *fp = g_fopen(filepath, "ab");
8868 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
8876 gboolean compose_draft (gpointer data, guint action)
8878 Compose *compose = (Compose *)data;
8882 MsgFlags flag = {0, 0};
8883 static gboolean lock = FALSE;
8884 MsgInfo *newmsginfo;
8886 gboolean target_locked = FALSE;
8887 gboolean err = FALSE;
8889 if (lock) return FALSE;
8891 if (compose->sending)
8894 draft = account_get_special_folder(compose->account, F_DRAFT);
8895 g_return_val_if_fail(draft != NULL, FALSE);
8897 if (!g_mutex_trylock(compose->mutex)) {
8898 /* we don't want to lock the mutex once it's available,
8899 * because as the only other part of compose.c locking
8900 * it is compose_close - which means once unlocked,
8901 * the compose struct will be freed */
8902 debug_print("couldn't lock mutex, probably sending\n");
8908 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
8909 G_DIR_SEPARATOR, compose);
8910 if ((fp = g_fopen(tmp, "wb")) == NULL) {
8911 FILE_OP_ERROR(tmp, "fopen");
8915 /* chmod for security */
8916 if (change_file_mode_rw(fp, tmp) < 0) {
8917 FILE_OP_ERROR(tmp, "chmod");
8918 g_warning("can't change file mode\n");
8921 /* Save draft infos */
8922 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
8923 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
8925 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
8926 gchar *savefolderid;
8928 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8929 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
8930 g_free(savefolderid);
8932 if (compose->return_receipt) {
8933 err |= (fprintf(fp, "RRCPT:1\n") < 0);
8935 if (compose->privacy_system) {
8936 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
8937 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
8938 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
8941 /* Message-ID of message replying to */
8942 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
8945 folderid = folder_item_get_identifier(compose->replyinfo->folder);
8946 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
8949 /* Message-ID of message forwarding to */
8950 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
8953 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
8954 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
8958 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
8959 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
8961 /* end of headers */
8962 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
8969 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
8973 if (fclose(fp) == EOF) {
8977 if (compose->targetinfo) {
8978 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
8979 flag.perm_flags = target_locked?MSG_LOCKED:0;
8981 flag.tmp_flags = MSG_DRAFT;
8983 folder_item_scan(draft);
8984 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
8985 MsgInfo *tmpinfo = NULL;
8986 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
8987 if (compose->msgid) {
8988 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
8991 msgnum = tmpinfo->msgnum;
8992 procmsg_msginfo_free(tmpinfo);
8993 debug_print("got draft msgnum %d from scanning\n", msgnum);
8995 debug_print("didn't get draft msgnum after scanning\n");
8998 debug_print("got draft msgnum %d from adding\n", msgnum);
9004 if (action != COMPOSE_AUTO_SAVE) {
9005 if (action != COMPOSE_DRAFT_FOR_EXIT)
9006 alertpanel_error(_("Could not save draft."));
9009 gtkut_window_popup(compose->window);
9010 val = alertpanel_full(_("Could not save draft"),
9011 _("Could not save draft.\n"
9012 "Do you want to cancel exit or discard this email?"),
9013 _("_Cancel exit"), _("_Discard email"), NULL,
9014 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9015 if (val == G_ALERTALTERNATE) {
9017 g_mutex_unlock(compose->mutex); /* must be done before closing */
9018 compose_close(compose);
9022 g_mutex_unlock(compose->mutex); /* must be done before closing */
9031 if (compose->mode == COMPOSE_REEDIT) {
9032 compose_remove_reedit_target(compose, TRUE);
9035 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9038 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9040 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9042 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9043 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9044 procmsg_msginfo_set_flags(newmsginfo, 0,
9045 MSG_HAS_ATTACHMENT);
9047 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9048 compose_register_draft(newmsginfo);
9050 procmsg_msginfo_free(newmsginfo);
9053 folder_item_scan(draft);
9055 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9057 g_mutex_unlock(compose->mutex); /* must be done before closing */
9058 compose_close(compose);
9064 path = folder_item_fetch_msg(draft, msgnum);
9066 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9069 if (g_stat(path, &s) < 0) {
9070 FILE_OP_ERROR(path, "stat");
9076 procmsg_msginfo_free(compose->targetinfo);
9077 compose->targetinfo = procmsg_msginfo_new();
9078 compose->targetinfo->msgnum = msgnum;
9079 compose->targetinfo->size = (goffset)s.st_size;
9080 compose->targetinfo->mtime = s.st_mtime;
9081 compose->targetinfo->folder = draft;
9083 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9084 compose->mode = COMPOSE_REEDIT;
9086 if (action == COMPOSE_AUTO_SAVE) {
9087 compose->autosaved_draft = compose->targetinfo;
9089 compose->modified = FALSE;
9090 compose_set_title(compose);
9094 g_mutex_unlock(compose->mutex);
9098 void compose_clear_exit_drafts(void)
9100 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9101 DRAFTED_AT_EXIT, NULL);
9102 if (is_file_exist(filepath))
9103 claws_unlink(filepath);
9108 void compose_reopen_exit_drafts(void)
9110 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9111 DRAFTED_AT_EXIT, NULL);
9112 FILE *fp = g_fopen(filepath, "rb");
9116 while (fgets(buf, sizeof(buf), fp)) {
9117 gchar **parts = g_strsplit(buf, "\t", 2);
9118 const gchar *folder = parts[0];
9119 int msgnum = parts[1] ? atoi(parts[1]):-1;
9121 if (folder && *folder && msgnum > -1) {
9122 FolderItem *item = folder_find_item_from_identifier(folder);
9123 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9125 compose_reedit(info, FALSE);
9132 compose_clear_exit_drafts();
9135 static void compose_save_cb(GtkAction *action, gpointer data)
9137 compose_draft(data, COMPOSE_KEEP_EDITING);
9140 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9142 if (compose && file_list) {
9145 for ( tmp = file_list; tmp; tmp = tmp->next) {
9146 gchar *file = (gchar *) tmp->data;
9147 gchar *utf8_filename = conv_filename_to_utf8(file);
9148 compose_attach_append(compose, file, utf8_filename, NULL);
9149 compose_changed_cb(NULL, compose);
9154 g_free(utf8_filename);
9159 static void compose_attach_cb(GtkAction *action, gpointer data)
9161 Compose *compose = (Compose *)data;
9164 if (compose->redirect_filename != NULL)
9167 file_list = filesel_select_multiple_files_open(_("Select file"));
9170 compose_attach_from_list(compose, file_list, TRUE);
9171 g_list_free(file_list);
9175 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9177 Compose *compose = (Compose *)data;
9180 file_list = filesel_select_multiple_files_open(_("Select file"));
9185 for ( tmp = file_list; tmp; tmp = tmp->next) {
9186 gchar *file = (gchar *) tmp->data;
9187 gchar *filedup = g_strdup(file);
9188 gchar *shortfile = g_path_get_basename(filedup);
9189 ComposeInsertResult res;
9191 res = compose_insert_file(compose, file);
9192 if (res == COMPOSE_INSERT_READ_ERROR) {
9193 alertpanel_error(_("File '%s' could not be read."), shortfile);
9194 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9195 alertpanel_error(_("File '%s' contained invalid characters\n"
9196 "for the current encoding, insertion may be incorrect."), shortfile);
9202 g_list_free(file_list);
9206 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9208 Compose *compose = (Compose *)data;
9210 compose_insert_sig(compose, FALSE);
9213 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9217 Compose *compose = (Compose *)data;
9219 gtkut_widget_get_uposition(widget, &x, &y);
9220 if (!compose->batch) {
9221 prefs_common.compose_x = x;
9222 prefs_common.compose_y = y;
9224 if (compose->sending || compose->updating)
9226 compose_close_cb(NULL, compose);
9230 void compose_close_toolbar(Compose *compose)
9232 compose_close_cb(NULL, compose);
9235 static void compose_close_cb(GtkAction *action, gpointer data)
9237 Compose *compose = (Compose *)data;
9241 if (compose->exteditor_tag != -1) {
9242 if (!compose_ext_editor_kill(compose))
9247 if (compose->modified) {
9248 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9249 if (!g_mutex_trylock(compose->mutex)) {
9250 /* we don't want to lock the mutex once it's available,
9251 * because as the only other part of compose.c locking
9252 * it is compose_close - which means once unlocked,
9253 * the compose struct will be freed */
9254 debug_print("couldn't lock mutex, probably sending\n");
9258 val = alertpanel(_("Discard message"),
9259 _("This message has been modified. Discard it?"),
9260 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9262 val = alertpanel(_("Save changes"),
9263 _("This message has been modified. Save the latest changes?"),
9264 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9266 g_mutex_unlock(compose->mutex);
9268 case G_ALERTDEFAULT:
9269 if (prefs_common.autosave && !reedit)
9270 compose_remove_draft(compose);
9272 case G_ALERTALTERNATE:
9273 compose_draft(data, COMPOSE_QUIT_EDITING);
9280 compose_close(compose);
9283 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9285 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9286 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9287 Compose *compose = (Compose *) data;
9290 compose->out_encoding = (CharSet)value;
9293 static void compose_address_cb(GtkAction *action, gpointer data)
9295 Compose *compose = (Compose *)data;
9297 addressbook_open(compose);
9300 static void about_show_cb(GtkAction *action, gpointer data)
9305 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9307 Compose *compose = (Compose *)data;
9312 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9313 g_return_if_fail(tmpl != NULL);
9315 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9317 val = alertpanel(_("Apply template"), msg,
9318 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9321 if (val == G_ALERTDEFAULT)
9322 compose_template_apply(compose, tmpl, TRUE);
9323 else if (val == G_ALERTALTERNATE)
9324 compose_template_apply(compose, tmpl, FALSE);
9327 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9329 Compose *compose = (Compose *)data;
9331 compose_exec_ext_editor(compose);
9334 static void compose_undo_cb(GtkAction *action, gpointer data)
9336 Compose *compose = (Compose *)data;
9337 gboolean prev_autowrap = compose->autowrap;
9339 compose->autowrap = FALSE;
9340 undo_undo(compose->undostruct);
9341 compose->autowrap = prev_autowrap;
9344 static void compose_redo_cb(GtkAction *action, gpointer data)
9346 Compose *compose = (Compose *)data;
9347 gboolean prev_autowrap = compose->autowrap;
9349 compose->autowrap = FALSE;
9350 undo_redo(compose->undostruct);
9351 compose->autowrap = prev_autowrap;
9354 static void entry_cut_clipboard(GtkWidget *entry)
9356 if (GTK_IS_EDITABLE(entry))
9357 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9358 else if (GTK_IS_TEXT_VIEW(entry))
9359 gtk_text_buffer_cut_clipboard(
9360 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9361 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9365 static void entry_copy_clipboard(GtkWidget *entry)
9367 if (GTK_IS_EDITABLE(entry))
9368 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9369 else if (GTK_IS_TEXT_VIEW(entry))
9370 gtk_text_buffer_copy_clipboard(
9371 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9372 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9375 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9376 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9378 if (GTK_IS_TEXT_VIEW(entry)) {
9379 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9380 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9381 GtkTextIter start_iter, end_iter;
9383 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9385 if (contents == NULL)
9388 /* we shouldn't delete the selection when middle-click-pasting, or we
9389 * can't mid-click-paste our own selection */
9390 if (clip != GDK_SELECTION_PRIMARY) {
9391 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9392 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9395 if (insert_place == NULL) {
9396 /* if insert_place isn't specified, insert at the cursor.
9397 * used for Ctrl-V pasting */
9398 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9399 start = gtk_text_iter_get_offset(&start_iter);
9400 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9402 /* if insert_place is specified, paste here.
9403 * used for mid-click-pasting */
9404 start = gtk_text_iter_get_offset(insert_place);
9405 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9406 if (prefs_common.primary_paste_unselects)
9407 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9411 /* paste unwrapped: mark the paste so it's not wrapped later */
9412 end = start + strlen(contents);
9413 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9414 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9415 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9416 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9417 /* rewrap paragraph now (after a mid-click-paste) */
9418 mark_start = gtk_text_buffer_get_insert(buffer);
9419 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9420 gtk_text_iter_backward_char(&start_iter);
9421 compose_beautify_paragraph(compose, &start_iter, TRUE);
9423 } else if (GTK_IS_EDITABLE(entry))
9424 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9426 compose->modified = TRUE;
9429 static void entry_allsel(GtkWidget *entry)
9431 if (GTK_IS_EDITABLE(entry))
9432 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9433 else if (GTK_IS_TEXT_VIEW(entry)) {
9434 GtkTextIter startiter, enditer;
9435 GtkTextBuffer *textbuf;
9437 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9438 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9439 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9441 gtk_text_buffer_move_mark_by_name(textbuf,
9442 "selection_bound", &startiter);
9443 gtk_text_buffer_move_mark_by_name(textbuf,
9444 "insert", &enditer);
9448 static void compose_cut_cb(GtkAction *action, gpointer data)
9450 Compose *compose = (Compose *)data;
9451 if (compose->focused_editable
9452 #ifndef GENERIC_UMPC
9453 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9456 entry_cut_clipboard(compose->focused_editable);
9459 static void compose_copy_cb(GtkAction *action, gpointer data)
9461 Compose *compose = (Compose *)data;
9462 if (compose->focused_editable
9463 #ifndef GENERIC_UMPC
9464 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9467 entry_copy_clipboard(compose->focused_editable);
9470 static void compose_paste_cb(GtkAction *action, gpointer data)
9472 Compose *compose = (Compose *)data;
9474 GtkTextBuffer *buffer;
9476 if (compose->focused_editable &&
9477 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9478 entry_paste_clipboard(compose, compose->focused_editable,
9479 prefs_common.linewrap_pastes,
9480 GDK_SELECTION_CLIPBOARD, NULL);
9484 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9486 Compose *compose = (Compose *)data;
9487 gint wrap_quote = prefs_common.linewrap_quote;
9488 if (compose->focused_editable
9489 #ifndef GENERIC_UMPC
9490 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9493 /* let text_insert() (called directly or at a later time
9494 * after the gtk_editable_paste_clipboard) know that
9495 * text is to be inserted as a quotation. implemented
9496 * by using a simple refcount... */
9497 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9498 G_OBJECT(compose->focused_editable),
9499 "paste_as_quotation"));
9500 g_object_set_data(G_OBJECT(compose->focused_editable),
9501 "paste_as_quotation",
9502 GINT_TO_POINTER(paste_as_quotation + 1));
9503 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9504 entry_paste_clipboard(compose, compose->focused_editable,
9505 prefs_common.linewrap_pastes,
9506 GDK_SELECTION_CLIPBOARD, NULL);
9507 prefs_common.linewrap_quote = wrap_quote;
9511 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9513 Compose *compose = (Compose *)data;
9515 GtkTextBuffer *buffer;
9517 if (compose->focused_editable
9518 #ifndef GENERIC_UMPC
9519 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9522 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9523 GDK_SELECTION_CLIPBOARD, NULL);
9527 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9529 Compose *compose = (Compose *)data;
9531 GtkTextBuffer *buffer;
9533 if (compose->focused_editable
9534 #ifndef GENERIC_UMPC
9535 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9538 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9539 GDK_SELECTION_CLIPBOARD, NULL);
9543 static void compose_allsel_cb(GtkAction *action, gpointer data)
9545 Compose *compose = (Compose *)data;
9546 if (compose->focused_editable
9547 #ifndef GENERIC_UMPC
9548 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9551 entry_allsel(compose->focused_editable);
9554 static void textview_move_beginning_of_line (GtkTextView *text)
9556 GtkTextBuffer *buffer;
9560 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9562 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9563 mark = gtk_text_buffer_get_insert(buffer);
9564 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9565 gtk_text_iter_set_line_offset(&ins, 0);
9566 gtk_text_buffer_place_cursor(buffer, &ins);
9569 static void textview_move_forward_character (GtkTextView *text)
9571 GtkTextBuffer *buffer;
9575 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9577 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9578 mark = gtk_text_buffer_get_insert(buffer);
9579 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9580 if (gtk_text_iter_forward_cursor_position(&ins))
9581 gtk_text_buffer_place_cursor(buffer, &ins);
9584 static void textview_move_backward_character (GtkTextView *text)
9586 GtkTextBuffer *buffer;
9590 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9592 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9593 mark = gtk_text_buffer_get_insert(buffer);
9594 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9595 if (gtk_text_iter_backward_cursor_position(&ins))
9596 gtk_text_buffer_place_cursor(buffer, &ins);
9599 static void textview_move_forward_word (GtkTextView *text)
9601 GtkTextBuffer *buffer;
9606 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9608 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9609 mark = gtk_text_buffer_get_insert(buffer);
9610 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9611 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9612 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9613 gtk_text_iter_backward_word_start(&ins);
9614 gtk_text_buffer_place_cursor(buffer, &ins);
9618 static void textview_move_backward_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_backward_word_starts(&ins, 1))
9632 gtk_text_buffer_place_cursor(buffer, &ins);
9635 static void textview_move_end_of_line (GtkTextView *text)
9637 GtkTextBuffer *buffer;
9641 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9643 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9644 mark = gtk_text_buffer_get_insert(buffer);
9645 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9646 if (gtk_text_iter_forward_to_line_end(&ins))
9647 gtk_text_buffer_place_cursor(buffer, &ins);
9650 static void textview_move_next_line (GtkTextView *text)
9652 GtkTextBuffer *buffer;
9657 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9659 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9660 mark = gtk_text_buffer_get_insert(buffer);
9661 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9662 offset = gtk_text_iter_get_line_offset(&ins);
9663 if (gtk_text_iter_forward_line(&ins)) {
9664 gtk_text_iter_set_line_offset(&ins, offset);
9665 gtk_text_buffer_place_cursor(buffer, &ins);
9669 static void textview_move_previous_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_backward_line(&ins)) {
9683 gtk_text_iter_set_line_offset(&ins, offset);
9684 gtk_text_buffer_place_cursor(buffer, &ins);
9688 static void textview_delete_forward_character (GtkTextView *text)
9690 GtkTextBuffer *buffer;
9692 GtkTextIter ins, end_iter;
9694 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9696 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9697 mark = gtk_text_buffer_get_insert(buffer);
9698 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9700 if (gtk_text_iter_forward_char(&end_iter)) {
9701 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9705 static void textview_delete_backward_character (GtkTextView *text)
9707 GtkTextBuffer *buffer;
9709 GtkTextIter ins, end_iter;
9711 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9713 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9714 mark = gtk_text_buffer_get_insert(buffer);
9715 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9717 if (gtk_text_iter_backward_char(&end_iter)) {
9718 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9722 static void textview_delete_forward_word (GtkTextView *text)
9724 GtkTextBuffer *buffer;
9726 GtkTextIter ins, end_iter;
9728 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9730 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9731 mark = gtk_text_buffer_get_insert(buffer);
9732 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9734 if (gtk_text_iter_forward_word_end(&end_iter)) {
9735 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9739 static void textview_delete_backward_word (GtkTextView *text)
9741 GtkTextBuffer *buffer;
9743 GtkTextIter ins, end_iter;
9745 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9747 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9748 mark = gtk_text_buffer_get_insert(buffer);
9749 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9751 if (gtk_text_iter_backward_word_start(&end_iter)) {
9752 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9756 static void textview_delete_line (GtkTextView *text)
9758 GtkTextBuffer *buffer;
9760 GtkTextIter ins, start_iter, end_iter;
9762 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9764 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9765 mark = gtk_text_buffer_get_insert(buffer);
9766 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9769 gtk_text_iter_set_line_offset(&start_iter, 0);
9772 if (gtk_text_iter_ends_line(&end_iter)){
9773 if (!gtk_text_iter_forward_char(&end_iter))
9774 gtk_text_iter_backward_char(&start_iter);
9777 gtk_text_iter_forward_to_line_end(&end_iter);
9778 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9781 static void textview_delete_to_line_end (GtkTextView *text)
9783 GtkTextBuffer *buffer;
9785 GtkTextIter ins, end_iter;
9787 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9789 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9790 mark = gtk_text_buffer_get_insert(buffer);
9791 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9793 if (gtk_text_iter_ends_line(&end_iter))
9794 gtk_text_iter_forward_char(&end_iter);
9796 gtk_text_iter_forward_to_line_end(&end_iter);
9797 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9800 #define DO_ACTION(name, act) { \
9801 if(!strcmp(name, a_name)) { \
9805 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
9807 const gchar *a_name = gtk_action_get_name(action);
9808 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
9809 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
9810 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
9811 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
9812 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
9813 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
9814 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
9815 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
9816 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
9817 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
9818 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
9819 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
9820 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
9821 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
9825 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
9827 Compose *compose = (Compose *)data;
9828 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9829 ComposeCallAdvancedAction action = -1;
9831 action = compose_call_advanced_action_from_path(gaction);
9834 void (*do_action) (GtkTextView *text);
9835 } action_table[] = {
9836 {textview_move_beginning_of_line},
9837 {textview_move_forward_character},
9838 {textview_move_backward_character},
9839 {textview_move_forward_word},
9840 {textview_move_backward_word},
9841 {textview_move_end_of_line},
9842 {textview_move_next_line},
9843 {textview_move_previous_line},
9844 {textview_delete_forward_character},
9845 {textview_delete_backward_character},
9846 {textview_delete_forward_word},
9847 {textview_delete_backward_word},
9848 {textview_delete_line},
9849 {textview_delete_to_line_end}
9852 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
9854 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
9855 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
9856 if (action_table[action].do_action)
9857 action_table[action].do_action(text);
9859 g_warning("Not implemented yet.");
9863 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
9867 if (GTK_IS_EDITABLE(widget)) {
9868 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
9869 gtk_editable_set_position(GTK_EDITABLE(widget),
9872 if (widget->parent && widget->parent->parent
9873 && widget->parent->parent->parent) {
9874 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
9875 gint y = widget->allocation.y;
9876 gint height = widget->allocation.height;
9877 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
9878 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
9880 if (y < (int)shown->value) {
9881 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
9883 if (y + height > (int)shown->value + (int)shown->page_size) {
9884 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
9885 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9886 y + height - (int)shown->page_size - 1);
9888 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9889 (int)shown->upper - (int)shown->page_size - 1);
9896 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
9897 compose->focused_editable = widget;
9900 if (GTK_IS_TEXT_VIEW(widget)
9901 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
9902 g_object_ref(compose->notebook);
9903 g_object_ref(compose->edit_vbox);
9904 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9905 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9906 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
9907 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
9908 g_object_unref(compose->notebook);
9909 g_object_unref(compose->edit_vbox);
9910 g_signal_handlers_block_by_func(G_OBJECT(widget),
9911 G_CALLBACK(compose_grab_focus_cb),
9913 gtk_widget_grab_focus(widget);
9914 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9915 G_CALLBACK(compose_grab_focus_cb),
9917 } else if (!GTK_IS_TEXT_VIEW(widget)
9918 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
9919 g_object_ref(compose->notebook);
9920 g_object_ref(compose->edit_vbox);
9921 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9922 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9923 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
9924 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
9925 g_object_unref(compose->notebook);
9926 g_object_unref(compose->edit_vbox);
9927 g_signal_handlers_block_by_func(G_OBJECT(widget),
9928 G_CALLBACK(compose_grab_focus_cb),
9930 gtk_widget_grab_focus(widget);
9931 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9932 G_CALLBACK(compose_grab_focus_cb),
9938 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
9940 compose->modified = TRUE;
9941 #ifndef GENERIC_UMPC
9942 compose_set_title(compose);
9946 static void compose_wrap_cb(GtkAction *action, gpointer data)
9948 Compose *compose = (Compose *)data;
9949 compose_beautify_paragraph(compose, NULL, TRUE);
9952 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
9954 Compose *compose = (Compose *)data;
9955 compose_wrap_all_full(compose, TRUE);
9958 static void compose_find_cb(GtkAction *action, gpointer data)
9960 Compose *compose = (Compose *)data;
9962 message_search_compose(compose);
9965 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
9968 Compose *compose = (Compose *)data;
9969 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9970 if (compose->autowrap)
9971 compose_wrap_all_full(compose, TRUE);
9972 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9975 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
9978 Compose *compose = (Compose *)data;
9979 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9982 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
9984 Compose *compose = (Compose *)data;
9986 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9989 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
9991 Compose *compose = (Compose *)data;
9993 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9996 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
9998 g_free(compose->privacy_system);
10000 compose->privacy_system = g_strdup(account->default_privacy_system);
10001 compose_update_privacy_system_menu_item(compose, warn);
10004 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10006 Compose *compose = (Compose *)data;
10008 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10009 gtk_widget_show(compose->ruler_hbox);
10010 prefs_common.show_ruler = TRUE;
10012 gtk_widget_hide(compose->ruler_hbox);
10013 gtk_widget_queue_resize(compose->edit_vbox);
10014 prefs_common.show_ruler = FALSE;
10018 static void compose_attach_drag_received_cb (GtkWidget *widget,
10019 GdkDragContext *context,
10022 GtkSelectionData *data,
10025 gpointer user_data)
10027 Compose *compose = (Compose *)user_data;
10030 if (gdk_atom_name(data->type) &&
10031 !strcmp(gdk_atom_name(data->type), "text/uri-list")
10032 && gtk_drag_get_source_widget(context) !=
10033 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10034 list = uri_list_extract_filenames((const gchar *)data->data);
10035 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10036 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10037 compose_attach_append
10038 (compose, (const gchar *)tmp->data,
10039 utf8_filename, NULL);
10040 g_free(utf8_filename);
10042 if (list) compose_changed_cb(NULL, compose);
10043 list_free_strings(list);
10045 } else if (gtk_drag_get_source_widget(context)
10046 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10047 /* comes from our summaryview */
10048 SummaryView * summaryview = NULL;
10049 GSList * list = NULL, *cur = NULL;
10051 if (mainwindow_get_mainwindow())
10052 summaryview = mainwindow_get_mainwindow()->summaryview;
10055 list = summary_get_selected_msg_list(summaryview);
10057 for (cur = list; cur; cur = cur->next) {
10058 MsgInfo *msginfo = (MsgInfo *)cur->data;
10059 gchar *file = NULL;
10061 file = procmsg_get_message_file_full(msginfo,
10064 compose_attach_append(compose, (const gchar *)file,
10065 (const gchar *)file, "message/rfc822");
10069 g_slist_free(list);
10073 static gboolean compose_drag_drop(GtkWidget *widget,
10074 GdkDragContext *drag_context,
10076 guint time, gpointer user_data)
10078 /* not handling this signal makes compose_insert_drag_received_cb
10083 static void compose_insert_drag_received_cb (GtkWidget *widget,
10084 GdkDragContext *drag_context,
10087 GtkSelectionData *data,
10090 gpointer user_data)
10092 Compose *compose = (Compose *)user_data;
10095 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10097 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10098 AlertValue val = G_ALERTDEFAULT;
10100 list = uri_list_extract_filenames((const gchar *)data->data);
10102 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10103 /* Assume a list of no files, and data has ://, is a remote link */
10104 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10105 gchar *tmpfile = get_tmp_file();
10106 str_write_to_file(tmpdata, tmpfile);
10108 compose_insert_file(compose, tmpfile);
10109 claws_unlink(tmpfile);
10111 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10112 compose_beautify_paragraph(compose, NULL, TRUE);
10115 switch (prefs_common.compose_dnd_mode) {
10116 case COMPOSE_DND_ASK:
10117 val = alertpanel_full(_("Insert or attach?"),
10118 _("Do you want to insert the contents of the file(s) "
10119 "into the message body, or attach it to the email?"),
10120 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10121 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10123 case COMPOSE_DND_INSERT:
10124 val = G_ALERTALTERNATE;
10126 case COMPOSE_DND_ATTACH:
10127 val = G_ALERTOTHER;
10130 /* unexpected case */
10131 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10134 if (val & G_ALERTDISABLE) {
10135 val &= ~G_ALERTDISABLE;
10136 /* remember what action to perform by default, only if we don't click Cancel */
10137 if (val == G_ALERTALTERNATE)
10138 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10139 else if (val == G_ALERTOTHER)
10140 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10143 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10144 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10145 list_free_strings(list);
10148 } else if (val == G_ALERTOTHER) {
10149 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10150 list_free_strings(list);
10155 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10156 compose_insert_file(compose, (const gchar *)tmp->data);
10158 list_free_strings(list);
10160 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10163 #if GTK_CHECK_VERSION(2, 8, 0)
10164 /* do nothing, handled by GTK */
10166 gchar *tmpfile = get_tmp_file();
10167 str_write_to_file((const gchar *)data->data, tmpfile);
10168 compose_insert_file(compose, tmpfile);
10169 claws_unlink(tmpfile);
10171 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10175 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10178 static void compose_header_drag_received_cb (GtkWidget *widget,
10179 GdkDragContext *drag_context,
10182 GtkSelectionData *data,
10185 gpointer user_data)
10187 GtkEditable *entry = (GtkEditable *)user_data;
10188 gchar *email = (gchar *)data->data;
10190 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10193 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10194 gchar *decoded=g_new(gchar, strlen(email));
10197 email += strlen("mailto:");
10198 decode_uri(decoded, email); /* will fit */
10199 gtk_editable_delete_text(entry, 0, -1);
10200 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10201 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10205 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10208 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10210 Compose *compose = (Compose *)data;
10212 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10213 compose->return_receipt = TRUE;
10215 compose->return_receipt = FALSE;
10218 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10220 Compose *compose = (Compose *)data;
10222 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10223 compose->remove_references = TRUE;
10225 compose->remove_references = FALSE;
10228 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10229 GdkEventKey *event,
10230 ComposeHeaderEntry *headerentry)
10232 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10233 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10234 !(event->state & GDK_MODIFIER_MASK) &&
10235 (event->keyval == GDK_BackSpace) &&
10236 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10237 gtk_container_remove
10238 (GTK_CONTAINER(headerentry->compose->header_table),
10239 headerentry->combo);
10240 gtk_container_remove
10241 (GTK_CONTAINER(headerentry->compose->header_table),
10242 headerentry->entry);
10243 headerentry->compose->header_list =
10244 g_slist_remove(headerentry->compose->header_list,
10246 g_free(headerentry);
10247 } else if (event->keyval == GDK_Tab) {
10248 if (headerentry->compose->header_last == headerentry) {
10249 /* Override default next focus, and give it to subject_entry
10250 * instead of notebook tabs
10252 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10253 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10260 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10261 ComposeHeaderEntry *headerentry)
10263 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10264 compose_create_header_entry(headerentry->compose);
10265 g_signal_handlers_disconnect_matched
10266 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10267 0, 0, NULL, NULL, headerentry);
10269 /* Automatically scroll down */
10270 compose_show_first_last_header(headerentry->compose, FALSE);
10276 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10278 GtkAdjustment *vadj;
10280 g_return_if_fail(compose);
10281 g_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10282 g_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10284 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10285 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10286 gtk_adjustment_changed(vadj);
10289 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10290 const gchar *text, gint len, Compose *compose)
10292 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10293 (G_OBJECT(compose->text), "paste_as_quotation"));
10296 g_return_if_fail(text != NULL);
10298 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10299 G_CALLBACK(text_inserted),
10301 if (paste_as_quotation) {
10303 const gchar *qmark;
10305 GtkTextIter start_iter;
10308 len = strlen(text);
10310 new_text = g_strndup(text, len);
10312 qmark = compose_quote_char_from_context(compose);
10314 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10315 gtk_text_buffer_place_cursor(buffer, iter);
10317 pos = gtk_text_iter_get_offset(iter);
10319 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10320 _("Quote format error at line %d."));
10321 quote_fmt_reset_vartable();
10323 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10324 GINT_TO_POINTER(paste_as_quotation - 1));
10326 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10327 gtk_text_buffer_place_cursor(buffer, iter);
10328 gtk_text_buffer_delete_mark(buffer, mark);
10330 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10331 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10332 compose_beautify_paragraph(compose, &start_iter, FALSE);
10333 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10334 gtk_text_buffer_delete_mark(buffer, mark);
10336 if (strcmp(text, "\n") || compose->automatic_break
10337 || gtk_text_iter_starts_line(iter)) {
10338 GtkTextIter before_ins;
10339 gtk_text_buffer_insert(buffer, iter, text, len);
10340 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10341 before_ins = *iter;
10342 gtk_text_iter_backward_chars(&before_ins, len);
10343 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10346 /* check if the preceding is just whitespace or quote */
10347 GtkTextIter start_line;
10348 gchar *tmp = NULL, *quote = NULL;
10349 gint quote_len = 0, is_normal = 0;
10350 start_line = *iter;
10351 gtk_text_iter_set_line_offset(&start_line, 0);
10352 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10355 if (*tmp == '\0') {
10358 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10366 gtk_text_buffer_insert(buffer, iter, text, len);
10368 gtk_text_buffer_insert_with_tags_by_name(buffer,
10369 iter, text, len, "no_join", NULL);
10374 if (!paste_as_quotation) {
10375 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10376 compose_beautify_paragraph(compose, iter, FALSE);
10377 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10378 gtk_text_buffer_delete_mark(buffer, mark);
10381 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10382 G_CALLBACK(text_inserted),
10384 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10386 if (prefs_common.autosave &&
10387 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10388 compose->draft_timeout_tag != -2 /* disabled while loading */)
10389 compose->draft_timeout_tag = g_timeout_add
10390 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10392 static gint compose_defer_auto_save_draft(Compose *compose)
10394 compose->draft_timeout_tag = -1;
10395 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10400 static void compose_check_all(GtkAction *action, gpointer data)
10402 Compose *compose = (Compose *)data;
10403 if (compose->gtkaspell)
10404 gtkaspell_check_all(compose->gtkaspell);
10407 static void compose_highlight_all(GtkAction *action, gpointer data)
10409 Compose *compose = (Compose *)data;
10410 if (compose->gtkaspell)
10411 gtkaspell_highlight_all(compose->gtkaspell);
10414 static void compose_check_backwards(GtkAction *action, gpointer data)
10416 Compose *compose = (Compose *)data;
10417 if (compose->gtkaspell)
10418 gtkaspell_check_backwards(compose->gtkaspell);
10420 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10424 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10426 Compose *compose = (Compose *)data;
10427 if (compose->gtkaspell)
10428 gtkaspell_check_forwards_go(compose->gtkaspell);
10430 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10436 *\brief Guess originating forward account from MsgInfo and several
10437 * "common preference" settings. Return NULL if no guess.
10439 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10441 PrefsAccount *account = NULL;
10443 g_return_val_if_fail(msginfo, NULL);
10444 g_return_val_if_fail(msginfo->folder, NULL);
10445 g_return_val_if_fail(msginfo->folder->prefs, NULL);
10447 if (msginfo->folder->prefs->enable_default_account)
10448 account = account_find_from_id(msginfo->folder->prefs->default_account);
10451 account = msginfo->folder->folder->account;
10453 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10455 Xstrdup_a(to, msginfo->to, return NULL);
10456 extract_address(to);
10457 account = account_find_from_address(to, FALSE);
10460 if (!account && prefs_common.forward_account_autosel) {
10461 gchar cc[BUFFSIZE];
10462 if (!procheader_get_header_from_msginfo
10463 (msginfo, cc,sizeof cc , "Cc:")) {
10464 gchar *buf = cc + strlen("Cc:");
10465 extract_address(buf);
10466 account = account_find_from_address(buf, FALSE);
10470 if (!account && prefs_common.forward_account_autosel) {
10471 gchar deliveredto[BUFFSIZE];
10472 if (!procheader_get_header_from_msginfo
10473 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10474 gchar *buf = deliveredto + strlen("Delivered-To:");
10475 extract_address(buf);
10476 account = account_find_from_address(buf, FALSE);
10483 gboolean compose_close(Compose *compose)
10487 if (!g_mutex_trylock(compose->mutex)) {
10488 /* we have to wait for the (possibly deferred by auto-save)
10489 * drafting to be done, before destroying the compose under
10491 debug_print("waiting for drafting to finish...\n");
10492 compose_allow_user_actions(compose, FALSE);
10493 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10496 g_return_val_if_fail(compose, FALSE);
10497 gtkut_widget_get_uposition(compose->window, &x, &y);
10498 if (!compose->batch) {
10499 prefs_common.compose_x = x;
10500 prefs_common.compose_y = y;
10502 g_mutex_unlock(compose->mutex);
10503 compose_destroy(compose);
10508 * Add entry field for each address in list.
10509 * \param compose E-Mail composition object.
10510 * \param listAddress List of (formatted) E-Mail addresses.
10512 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10515 node = listAddress;
10517 addr = ( gchar * ) node->data;
10518 compose_entry_append( compose, addr, COMPOSE_TO );
10519 node = g_list_next( node );
10523 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10524 guint action, gboolean opening_multiple)
10526 gchar *body = NULL;
10527 GSList *new_msglist = NULL;
10528 MsgInfo *tmp_msginfo = NULL;
10529 gboolean originally_enc = FALSE;
10530 gboolean originally_sig = FALSE;
10531 Compose *compose = NULL;
10533 g_return_if_fail(msgview != NULL);
10535 g_return_if_fail(msginfo_list != NULL);
10537 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10538 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10539 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10541 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10542 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10543 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10544 orig_msginfo, mimeinfo);
10545 if (tmp_msginfo != NULL) {
10546 new_msglist = g_slist_append(NULL, tmp_msginfo);
10548 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10549 privacy_msginfo_get_signed_state(orig_msginfo);
10550 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
10552 tmp_msginfo->folder = orig_msginfo->folder;
10553 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10554 if (orig_msginfo->tags)
10555 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10560 if (!opening_multiple)
10561 body = messageview_get_selection(msgview);
10564 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10565 procmsg_msginfo_free(tmp_msginfo);
10566 g_slist_free(new_msglist);
10568 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10570 if (compose && originally_enc) {
10571 compose_force_encryption(compose, compose->account, FALSE);
10574 if (compose && originally_sig && compose->account->default_sign_reply) {
10575 compose_force_signing(compose, compose->account);
10581 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10584 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
10585 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10586 GSList *cur = msginfo_list;
10587 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10588 "messages. Opening the windows "
10589 "could take some time. Do you "
10590 "want to continue?"),
10591 g_slist_length(msginfo_list));
10592 if (g_slist_length(msginfo_list) > 9
10593 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10594 != G_ALERTALTERNATE) {
10599 /* We'll open multiple compose windows */
10600 /* let the WM place the next windows */
10601 compose_force_window_origin = FALSE;
10602 for (; cur; cur = cur->next) {
10604 tmplist.data = cur->data;
10605 tmplist.next = NULL;
10606 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10608 compose_force_window_origin = TRUE;
10610 /* forwarding multiple mails as attachments is done via a
10611 * single compose window */
10612 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10616 void compose_set_position(Compose *compose, gint pos)
10618 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10620 gtkut_text_view_set_position(text, pos);
10623 gboolean compose_search_string(Compose *compose,
10624 const gchar *str, gboolean case_sens)
10626 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10628 return gtkut_text_view_search_string(text, str, case_sens);
10631 gboolean compose_search_string_backward(Compose *compose,
10632 const gchar *str, gboolean case_sens)
10634 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10636 return gtkut_text_view_search_string_backward(text, str, case_sens);
10639 /* allocate a msginfo structure and populate its data from a compose data structure */
10640 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10642 MsgInfo *newmsginfo;
10644 gchar buf[BUFFSIZE];
10646 g_return_val_if_fail( compose != NULL, NULL );
10648 newmsginfo = procmsg_msginfo_new();
10651 get_rfc822_date(buf, sizeof(buf));
10652 newmsginfo->date = g_strdup(buf);
10655 if (compose->from_name) {
10656 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10657 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10661 if (compose->subject_entry)
10662 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10664 /* to, cc, reply-to, newsgroups */
10665 for (list = compose->header_list; list; list = list->next) {
10666 gchar *header = gtk_editable_get_chars(
10668 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
10669 gchar *entry = gtk_editable_get_chars(
10670 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10672 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10673 if ( newmsginfo->to == NULL ) {
10674 newmsginfo->to = g_strdup(entry);
10675 } else if (entry && *entry) {
10676 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10677 g_free(newmsginfo->to);
10678 newmsginfo->to = tmp;
10681 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10682 if ( newmsginfo->cc == NULL ) {
10683 newmsginfo->cc = g_strdup(entry);
10684 } else if (entry && *entry) {
10685 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10686 g_free(newmsginfo->cc);
10687 newmsginfo->cc = tmp;
10690 if ( strcasecmp(header,
10691 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10692 if ( newmsginfo->newsgroups == NULL ) {
10693 newmsginfo->newsgroups = g_strdup(entry);
10694 } else if (entry && *entry) {
10695 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10696 g_free(newmsginfo->newsgroups);
10697 newmsginfo->newsgroups = tmp;
10705 /* other data is unset */
10711 /* update compose's dictionaries from folder dict settings */
10712 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10713 FolderItem *folder_item)
10715 g_return_if_fail(compose != NULL);
10717 if (compose->gtkaspell && folder_item && folder_item->prefs) {
10718 FolderItemPrefs *prefs = folder_item->prefs;
10720 if (prefs->enable_default_dictionary)
10721 gtkaspell_change_dict(compose->gtkaspell,
10722 prefs->default_dictionary, FALSE);
10723 if (folder_item->prefs->enable_default_alt_dictionary)
10724 gtkaspell_change_alt_dict(compose->gtkaspell,
10725 prefs->default_alt_dictionary);
10726 if (prefs->enable_default_dictionary
10727 || prefs->enable_default_alt_dictionary)
10728 compose_spell_menu_changed(compose);