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, "_Automatic"), /* RADIO compose_set_encoding_cb */
681 ENC_ACTION(CS_US_ASCII, C_US_ASCII, "7bit ASCII (US-ASC_II)"), /* RADIO compose_set_encoding_cb */
682 ENC_ACTION(CS_UTF_8, C_UTF_8, "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, "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, "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, "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 gtk_text_buffer_place_cursor(buffer, &iter);
3235 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3236 G_CALLBACK(compose_changed_cb),
3242 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3245 GtkTextBuffer *buffer;
3248 const gchar *cur_encoding;
3249 gchar buf[BUFFSIZE];
3252 gboolean prev_autowrap;
3253 gboolean badtxt = FALSE;
3255 g_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3257 if ((fp = g_fopen(file, "rb")) == NULL) {
3258 FILE_OP_ERROR(file, "fopen");
3259 return COMPOSE_INSERT_READ_ERROR;
3262 prev_autowrap = compose->autowrap;
3263 compose->autowrap = FALSE;
3265 text = GTK_TEXT_VIEW(compose->text);
3266 buffer = gtk_text_view_get_buffer(text);
3267 mark = gtk_text_buffer_get_insert(buffer);
3268 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3270 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3271 G_CALLBACK(text_inserted),
3274 cur_encoding = conv_get_locale_charset_str_no_utf8();
3276 while (fgets(buf, sizeof(buf), fp) != NULL) {
3279 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3280 str = g_strdup(buf);
3282 str = conv_codeset_strdup
3283 (buf, cur_encoding, CS_INTERNAL);
3286 /* strip <CR> if DOS/Windows file,
3287 replace <CR> with <LF> if Macintosh file. */
3290 if (len > 0 && str[len - 1] != '\n') {
3292 if (str[len] == '\r') str[len] = '\n';
3295 gtk_text_buffer_insert(buffer, &iter, str, -1);
3299 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3300 G_CALLBACK(text_inserted),
3302 compose->autowrap = prev_autowrap;
3303 if (compose->autowrap)
3304 compose_wrap_all(compose);
3309 return COMPOSE_INSERT_INVALID_CHARACTER;
3311 return COMPOSE_INSERT_SUCCESS;
3314 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3315 const gchar *filename,
3316 const gchar *content_type)
3324 GtkListStore *store;
3326 gboolean has_binary = FALSE;
3328 if (!is_file_exist(file)) {
3329 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3330 gboolean result = FALSE;
3331 if (file_from_uri && is_file_exist(file_from_uri)) {
3332 result = compose_attach_append(
3333 compose, file_from_uri,
3337 g_free(file_from_uri);
3340 alertpanel_error("File %s doesn't exist\n", filename);
3343 if ((size = get_file_size(file)) < 0) {
3344 alertpanel_error("Can't get file size of %s\n", filename);
3348 alertpanel_error(_("File %s is empty."), filename);
3351 if ((fp = g_fopen(file, "rb")) == NULL) {
3352 alertpanel_error(_("Can't read %s."), filename);
3357 ainfo = g_new0(AttachInfo, 1);
3358 auto_ainfo = g_auto_pointer_new_with_free
3359 (ainfo, (GFreeFunc) compose_attach_info_free);
3360 ainfo->file = g_strdup(file);
3363 ainfo->content_type = g_strdup(content_type);
3364 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3366 MsgFlags flags = {0, 0};
3368 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3369 ainfo->encoding = ENC_7BIT;
3371 ainfo->encoding = ENC_8BIT;
3373 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3374 if (msginfo && msginfo->subject)
3375 name = g_strdup(msginfo->subject);
3377 name = g_path_get_basename(filename ? filename : file);
3379 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3381 procmsg_msginfo_free(msginfo);
3383 if (!g_ascii_strncasecmp(content_type, "text", 4))
3384 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3386 ainfo->encoding = ENC_BASE64;
3387 name = g_path_get_basename(filename ? filename : file);
3388 ainfo->name = g_strdup(name);
3392 ainfo->content_type = procmime_get_mime_type(file);
3393 if (!ainfo->content_type) {
3394 ainfo->content_type =
3395 g_strdup("application/octet-stream");
3396 ainfo->encoding = ENC_BASE64;
3397 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3399 procmime_get_encoding_for_text_file(file, &has_binary);
3401 ainfo->encoding = ENC_BASE64;
3402 name = g_path_get_basename(filename ? filename : file);
3403 ainfo->name = g_strdup(name);
3407 if (ainfo->name != NULL
3408 && !strcmp(ainfo->name, ".")) {
3409 g_free(ainfo->name);
3413 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3414 g_free(ainfo->content_type);
3415 ainfo->content_type = g_strdup("application/octet-stream");
3418 ainfo->size = (goffset)size;
3419 size_text = to_human_readable((goffset)size);
3421 store = GTK_LIST_STORE(gtk_tree_view_get_model
3422 (GTK_TREE_VIEW(compose->attach_clist)));
3424 gtk_list_store_append(store, &iter);
3425 gtk_list_store_set(store, &iter,
3426 COL_MIMETYPE, ainfo->content_type,
3427 COL_SIZE, size_text,
3428 COL_NAME, ainfo->name,
3430 COL_AUTODATA, auto_ainfo,
3433 g_auto_pointer_free(auto_ainfo);
3434 compose_attach_update_label(compose);
3438 static void compose_use_signing(Compose *compose, gboolean use_signing)
3440 compose->use_signing = use_signing;
3441 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3444 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3446 compose->use_encryption = use_encryption;
3447 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3450 #define NEXT_PART_NOT_CHILD(info) \
3452 node = info->node; \
3453 while (node->children) \
3454 node = g_node_last_child(node); \
3455 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3458 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3462 MimeInfo *firsttext = NULL;
3463 MimeInfo *encrypted = NULL;
3466 const gchar *partname = NULL;
3468 mimeinfo = procmime_scan_message(msginfo);
3469 if (!mimeinfo) return;
3471 if (mimeinfo->node->children == NULL) {
3472 procmime_mimeinfo_free_all(mimeinfo);
3476 /* find first content part */
3477 child = (MimeInfo *) mimeinfo->node->children->data;
3478 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3479 child = (MimeInfo *)child->node->children->data;
3481 if (child->type == MIMETYPE_TEXT) {
3483 debug_print("First text part found\n");
3484 } else if (compose->mode == COMPOSE_REEDIT &&
3485 child->type == MIMETYPE_APPLICATION &&
3486 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3487 encrypted = (MimeInfo *)child->node->parent->data;
3490 child = (MimeInfo *) mimeinfo->node->children->data;
3491 while (child != NULL) {
3494 if (child == encrypted) {
3495 /* skip this part of tree */
3496 NEXT_PART_NOT_CHILD(child);
3500 if (child->type == MIMETYPE_MULTIPART) {
3501 /* get the actual content */
3502 child = procmime_mimeinfo_next(child);
3506 if (child == firsttext) {
3507 child = procmime_mimeinfo_next(child);
3511 outfile = procmime_get_tmp_file_name(child);
3512 if ((err = procmime_get_part(outfile, child)) < 0)
3513 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3515 gchar *content_type;
3517 content_type = procmime_get_content_type_str(child->type, child->subtype);
3519 /* if we meet a pgp signature, we don't attach it, but
3520 * we force signing. */
3521 if ((strcmp(content_type, "application/pgp-signature") &&
3522 strcmp(content_type, "application/pkcs7-signature") &&
3523 strcmp(content_type, "application/x-pkcs7-signature"))
3524 || compose->mode == COMPOSE_REDIRECT) {
3525 partname = procmime_mimeinfo_get_parameter(child, "filename");
3526 if (partname == NULL)
3527 partname = procmime_mimeinfo_get_parameter(child, "name");
3528 if (partname == NULL)
3530 compose_attach_append(compose, outfile,
3531 partname, content_type);
3533 compose_force_signing(compose, compose->account);
3535 g_free(content_type);
3538 NEXT_PART_NOT_CHILD(child);
3540 procmime_mimeinfo_free_all(mimeinfo);
3543 #undef NEXT_PART_NOT_CHILD
3548 WAIT_FOR_INDENT_CHAR,
3549 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3552 /* return indent length, we allow:
3553 indent characters followed by indent characters or spaces/tabs,
3554 alphabets and numbers immediately followed by indent characters,
3555 and the repeating sequences of the above
3556 If quote ends with multiple spaces, only the first one is included. */
3557 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3558 const GtkTextIter *start, gint *len)
3560 GtkTextIter iter = *start;
3564 IndentState state = WAIT_FOR_INDENT_CHAR;
3567 gint alnum_count = 0;
3568 gint space_count = 0;
3571 if (prefs_common.quote_chars == NULL) {
3575 while (!gtk_text_iter_ends_line(&iter)) {
3576 wc = gtk_text_iter_get_char(&iter);
3577 if (g_unichar_iswide(wc))
3579 clen = g_unichar_to_utf8(wc, ch);
3583 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3584 is_space = g_unichar_isspace(wc);
3586 if (state == WAIT_FOR_INDENT_CHAR) {
3587 if (!is_indent && !g_unichar_isalnum(wc))
3590 quote_len += alnum_count + space_count + 1;
3591 alnum_count = space_count = 0;
3592 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3595 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3596 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3600 else if (is_indent) {
3601 quote_len += alnum_count + space_count + 1;
3602 alnum_count = space_count = 0;
3605 state = WAIT_FOR_INDENT_CHAR;
3609 gtk_text_iter_forward_char(&iter);
3612 if (quote_len > 0 && space_count > 0)
3618 if (quote_len > 0) {
3620 gtk_text_iter_forward_chars(&iter, quote_len);
3621 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3627 /* return >0 if the line is itemized */
3628 static int compose_itemized_length(GtkTextBuffer *buffer,
3629 const GtkTextIter *start)
3631 GtkTextIter iter = *start;
3636 if (gtk_text_iter_ends_line(&iter))
3641 wc = gtk_text_iter_get_char(&iter);
3642 if (!g_unichar_isspace(wc))
3644 gtk_text_iter_forward_char(&iter);
3645 if (gtk_text_iter_ends_line(&iter))
3649 clen = g_unichar_to_utf8(wc, ch);
3653 if (!strchr("*-+", ch[0]))
3656 gtk_text_iter_forward_char(&iter);
3657 if (gtk_text_iter_ends_line(&iter))
3659 wc = gtk_text_iter_get_char(&iter);
3660 if (g_unichar_isspace(wc)) {
3666 /* return the string at the start of the itemization */
3667 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3668 const GtkTextIter *start)
3670 GtkTextIter iter = *start;
3673 GString *item_chars = g_string_new("");
3676 if (gtk_text_iter_ends_line(&iter))
3681 wc = gtk_text_iter_get_char(&iter);
3682 if (!g_unichar_isspace(wc))
3684 gtk_text_iter_forward_char(&iter);
3685 if (gtk_text_iter_ends_line(&iter))
3687 g_string_append_unichar(item_chars, wc);
3690 str = item_chars->str;
3691 g_string_free(item_chars, FALSE);
3695 /* return the number of spaces at a line's start */
3696 static int compose_left_offset_length(GtkTextBuffer *buffer,
3697 const GtkTextIter *start)
3699 GtkTextIter iter = *start;
3702 if (gtk_text_iter_ends_line(&iter))
3706 wc = gtk_text_iter_get_char(&iter);
3707 if (!g_unichar_isspace(wc))
3710 gtk_text_iter_forward_char(&iter);
3711 if (gtk_text_iter_ends_line(&iter))
3715 gtk_text_iter_forward_char(&iter);
3716 if (gtk_text_iter_ends_line(&iter))
3721 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3722 const GtkTextIter *start,
3723 GtkTextIter *break_pos,
3727 GtkTextIter iter = *start, line_end = *start;
3728 PangoLogAttr *attrs;
3735 gboolean can_break = FALSE;
3736 gboolean do_break = FALSE;
3737 gboolean was_white = FALSE;
3738 gboolean prev_dont_break = FALSE;
3740 gtk_text_iter_forward_to_line_end(&line_end);
3741 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3742 len = g_utf8_strlen(str, -1);
3746 g_warning("compose_get_line_break_pos: len = 0!\n");
3750 /* g_print("breaking line: %d: %s (len = %d)\n",
3751 gtk_text_iter_get_line(&iter), str, len); */
3753 attrs = g_new(PangoLogAttr, len + 1);
3755 pango_default_break(str, -1, NULL, attrs, len + 1);
3759 /* skip quote and leading spaces */
3760 for (i = 0; *p != '\0' && i < len; i++) {
3763 wc = g_utf8_get_char(p);
3764 if (i >= quote_len && !g_unichar_isspace(wc))
3766 if (g_unichar_iswide(wc))
3768 else if (*p == '\t')
3772 p = g_utf8_next_char(p);
3775 for (; *p != '\0' && i < len; i++) {
3776 PangoLogAttr *attr = attrs + i;
3780 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3783 was_white = attr->is_white;
3785 /* don't wrap URI */
3786 if ((uri_len = get_uri_len(p)) > 0) {
3788 if (pos > 0 && col > max_col) {
3798 wc = g_utf8_get_char(p);
3799 if (g_unichar_iswide(wc)) {
3801 if (prev_dont_break && can_break && attr->is_line_break)
3803 } else if (*p == '\t')
3807 if (pos > 0 && col > max_col) {
3812 if (*p == '-' || *p == '/')
3813 prev_dont_break = TRUE;
3815 prev_dont_break = FALSE;
3817 p = g_utf8_next_char(p);
3821 debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3826 *break_pos = *start;
3827 gtk_text_iter_set_line_offset(break_pos, pos);
3832 static gboolean compose_join_next_line(Compose *compose,
3833 GtkTextBuffer *buffer,
3835 const gchar *quote_str)
3837 GtkTextIter iter_ = *iter, cur, prev, next, end;
3838 PangoLogAttr attrs[3];
3840 gchar *next_quote_str;
3843 gboolean keep_cursor = FALSE;
3845 if (!gtk_text_iter_forward_line(&iter_) ||
3846 gtk_text_iter_ends_line(&iter_)) {
3849 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
3851 if ((quote_str || next_quote_str) &&
3852 strcmp2(quote_str, next_quote_str) != 0) {
3853 g_free(next_quote_str);
3856 g_free(next_quote_str);
3859 if (quote_len > 0) {
3860 gtk_text_iter_forward_chars(&end, quote_len);
3861 if (gtk_text_iter_ends_line(&end)) {
3866 /* don't join itemized lines */
3867 if (compose_itemized_length(buffer, &end) > 0) {
3871 /* don't join signature separator */
3872 if (compose_is_sig_separator(compose, buffer, &iter_)) {
3875 /* delete quote str */
3877 gtk_text_buffer_delete(buffer, &iter_, &end);
3879 /* don't join line breaks put by the user */
3881 gtk_text_iter_backward_char(&cur);
3882 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
3883 gtk_text_iter_forward_char(&cur);
3887 gtk_text_iter_forward_char(&cur);
3888 /* delete linebreak and extra spaces */
3889 while (gtk_text_iter_backward_char(&cur)) {
3890 wc1 = gtk_text_iter_get_char(&cur);
3891 if (!g_unichar_isspace(wc1))
3896 while (!gtk_text_iter_ends_line(&cur)) {
3897 wc1 = gtk_text_iter_get_char(&cur);
3898 if (!g_unichar_isspace(wc1))
3900 gtk_text_iter_forward_char(&cur);
3903 if (!gtk_text_iter_equal(&prev, &next)) {
3906 mark = gtk_text_buffer_get_insert(buffer);
3907 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
3908 if (gtk_text_iter_equal(&prev, &cur))
3910 gtk_text_buffer_delete(buffer, &prev, &next);
3914 /* insert space if required */
3915 gtk_text_iter_backward_char(&prev);
3916 wc1 = gtk_text_iter_get_char(&prev);
3917 wc2 = gtk_text_iter_get_char(&next);
3918 gtk_text_iter_forward_char(&next);
3919 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
3920 pango_default_break(str, -1, NULL, attrs, 3);
3921 if (!attrs[1].is_line_break ||
3922 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
3923 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
3925 gtk_text_iter_backward_char(&iter_);
3926 gtk_text_buffer_place_cursor(buffer, &iter_);
3935 #define ADD_TXT_POS(bp_, ep_, pti_) \
3936 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
3937 last = last->next; \
3938 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
3939 last->next = NULL; \
3941 g_warning("alloc error scanning URIs\n"); \
3944 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
3946 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3947 GtkTextBuffer *buffer;
3948 GtkTextIter iter, break_pos, end_of_line;
3949 gchar *quote_str = NULL;
3951 gboolean wrap_quote = prefs_common.linewrap_quote;
3952 gboolean prev_autowrap = compose->autowrap;
3953 gint startq_offset = -1, noq_offset = -1;
3954 gint uri_start = -1, uri_stop = -1;
3955 gint nouri_start = -1, nouri_stop = -1;
3956 gint num_blocks = 0;
3957 gint quotelevel = -1;
3958 gboolean modified = force;
3959 gboolean removed = FALSE;
3960 gboolean modified_before_remove = FALSE;
3962 gboolean start = TRUE;
3963 gint itemized_len = 0, rem_item_len = 0;
3964 gchar *itemized_chars = NULL;
3965 gboolean item_continuation = FALSE;
3970 if (compose->draft_timeout_tag == -2) {
3974 compose->autowrap = FALSE;
3976 buffer = gtk_text_view_get_buffer(text);
3977 undo_wrapping(compose->undostruct, TRUE);
3982 mark = gtk_text_buffer_get_insert(buffer);
3983 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3987 if (compose->draft_timeout_tag == -2) {
3988 if (gtk_text_iter_ends_line(&iter)) {
3989 while (gtk_text_iter_ends_line(&iter) &&
3990 gtk_text_iter_forward_line(&iter))
3993 while (gtk_text_iter_backward_line(&iter)) {
3994 if (gtk_text_iter_ends_line(&iter)) {
3995 gtk_text_iter_forward_line(&iter);
4001 /* move to line start */
4002 gtk_text_iter_set_line_offset(&iter, 0);
4005 itemized_len = compose_itemized_length(buffer, &iter);
4007 if (!itemized_len) {
4008 itemized_len = compose_left_offset_length(buffer, &iter);
4009 item_continuation = TRUE;
4013 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4015 /* go until paragraph end (empty line) */
4016 while (start || !gtk_text_iter_ends_line(&iter)) {
4017 gchar *scanpos = NULL;
4018 /* parse table - in order of priority */
4020 const gchar *needle; /* token */
4022 /* token search function */
4023 gchar *(*search) (const gchar *haystack,
4024 const gchar *needle);
4025 /* part parsing function */
4026 gboolean (*parse) (const gchar *start,
4027 const gchar *scanpos,
4031 /* part to URI function */
4032 gchar *(*build_uri) (const gchar *bp,
4036 static struct table parser[] = {
4037 {"http://", strcasestr, get_uri_part, make_uri_string},
4038 {"https://", strcasestr, get_uri_part, make_uri_string},
4039 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4040 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4041 {"www.", strcasestr, get_uri_part, make_http_string},
4042 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4043 {"@", strcasestr, get_email_part, make_email_string}
4045 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4046 gint last_index = PARSE_ELEMS;
4048 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4052 if (!prev_autowrap && num_blocks == 0) {
4054 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4055 G_CALLBACK(text_inserted),
4058 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4061 uri_start = uri_stop = -1;
4063 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4066 debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4067 if (startq_offset == -1)
4068 startq_offset = gtk_text_iter_get_offset(&iter);
4069 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4070 if (quotelevel > 2) {
4071 /* recycle colors */
4072 if (prefs_common.recycle_quote_colors)
4081 if (startq_offset == -1)
4082 noq_offset = gtk_text_iter_get_offset(&iter);
4086 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4089 if (gtk_text_iter_ends_line(&iter)) {
4091 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4092 prefs_common.linewrap_len,
4094 GtkTextIter prev, next, cur;
4095 if (prev_autowrap != FALSE || force) {
4096 compose->automatic_break = TRUE;
4098 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4099 compose->automatic_break = FALSE;
4100 if (itemized_len && compose->autoindent) {
4101 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4102 if (!item_continuation)
4103 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4105 } else if (quote_str && wrap_quote) {
4106 compose->automatic_break = TRUE;
4108 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4109 compose->automatic_break = FALSE;
4110 if (itemized_len && compose->autoindent) {
4111 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4112 if (!item_continuation)
4113 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4117 /* remove trailing spaces */
4119 rem_item_len = itemized_len;
4120 while (compose->autoindent && rem_item_len-- > 0)
4121 gtk_text_iter_backward_char(&cur);
4122 gtk_text_iter_backward_char(&cur);
4125 while (!gtk_text_iter_starts_line(&cur)) {
4128 gtk_text_iter_backward_char(&cur);
4129 wc = gtk_text_iter_get_char(&cur);
4130 if (!g_unichar_isspace(wc))
4134 if (!gtk_text_iter_equal(&prev, &next)) {
4135 gtk_text_buffer_delete(buffer, &prev, &next);
4137 gtk_text_iter_forward_char(&break_pos);
4141 gtk_text_buffer_insert(buffer, &break_pos,
4145 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4147 /* move iter to current line start */
4148 gtk_text_iter_set_line_offset(&iter, 0);
4155 /* move iter to next line start */
4161 if (!prev_autowrap && num_blocks > 0) {
4163 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4164 G_CALLBACK(text_inserted),
4168 while (!gtk_text_iter_ends_line(&end_of_line)) {
4169 gtk_text_iter_forward_char(&end_of_line);
4171 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4173 nouri_start = gtk_text_iter_get_offset(&iter);
4174 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4176 walk_pos = gtk_text_iter_get_offset(&iter);
4177 /* FIXME: this looks phony. scanning for anything in the parse table */
4178 for (n = 0; n < PARSE_ELEMS; n++) {
4181 tmp = parser[n].search(walk, parser[n].needle);
4183 if (scanpos == NULL || tmp < scanpos) {
4192 /* check if URI can be parsed */
4193 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4194 (const gchar **)&ep, FALSE)
4195 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4199 strlen(parser[last_index].needle);
4202 uri_start = walk_pos + (bp - o_walk);
4203 uri_stop = walk_pos + (ep - o_walk);
4207 gtk_text_iter_forward_line(&iter);
4210 if (startq_offset != -1) {
4211 GtkTextIter startquote, endquote;
4212 gtk_text_buffer_get_iter_at_offset(
4213 buffer, &startquote, startq_offset);
4216 switch (quotelevel) {
4218 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4219 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4220 gtk_text_buffer_apply_tag_by_name(
4221 buffer, "quote0", &startquote, &endquote);
4222 gtk_text_buffer_remove_tag_by_name(
4223 buffer, "quote1", &startquote, &endquote);
4224 gtk_text_buffer_remove_tag_by_name(
4225 buffer, "quote2", &startquote, &endquote);
4230 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4231 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4232 gtk_text_buffer_apply_tag_by_name(
4233 buffer, "quote1", &startquote, &endquote);
4234 gtk_text_buffer_remove_tag_by_name(
4235 buffer, "quote0", &startquote, &endquote);
4236 gtk_text_buffer_remove_tag_by_name(
4237 buffer, "quote2", &startquote, &endquote);
4242 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4243 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4244 gtk_text_buffer_apply_tag_by_name(
4245 buffer, "quote2", &startquote, &endquote);
4246 gtk_text_buffer_remove_tag_by_name(
4247 buffer, "quote0", &startquote, &endquote);
4248 gtk_text_buffer_remove_tag_by_name(
4249 buffer, "quote1", &startquote, &endquote);
4255 } else if (noq_offset != -1) {
4256 GtkTextIter startnoquote, endnoquote;
4257 gtk_text_buffer_get_iter_at_offset(
4258 buffer, &startnoquote, noq_offset);
4261 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4262 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4263 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4264 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4265 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4266 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4267 gtk_text_buffer_remove_tag_by_name(
4268 buffer, "quote0", &startnoquote, &endnoquote);
4269 gtk_text_buffer_remove_tag_by_name(
4270 buffer, "quote1", &startnoquote, &endnoquote);
4271 gtk_text_buffer_remove_tag_by_name(
4272 buffer, "quote2", &startnoquote, &endnoquote);
4278 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4279 GtkTextIter nouri_start_iter, nouri_end_iter;
4280 gtk_text_buffer_get_iter_at_offset(
4281 buffer, &nouri_start_iter, nouri_start);
4282 gtk_text_buffer_get_iter_at_offset(
4283 buffer, &nouri_end_iter, nouri_stop);
4284 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4285 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4286 gtk_text_buffer_remove_tag_by_name(
4287 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4288 modified_before_remove = modified;
4293 if (uri_start >= 0 && uri_stop > 0) {
4294 GtkTextIter uri_start_iter, uri_end_iter, back;
4295 gtk_text_buffer_get_iter_at_offset(
4296 buffer, &uri_start_iter, uri_start);
4297 gtk_text_buffer_get_iter_at_offset(
4298 buffer, &uri_end_iter, uri_stop);
4299 back = uri_end_iter;
4300 gtk_text_iter_backward_char(&back);
4301 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4302 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4303 gtk_text_buffer_apply_tag_by_name(
4304 buffer, "link", &uri_start_iter, &uri_end_iter);
4306 if (removed && !modified_before_remove) {
4312 debug_print("not modified, out after %d lines\n", lines);
4316 debug_print("modified, out after %d lines\n", lines);
4318 g_free(itemized_chars);
4321 undo_wrapping(compose->undostruct, FALSE);
4322 compose->autowrap = prev_autowrap;
4327 void compose_action_cb(void *data)
4329 Compose *compose = (Compose *)data;
4330 compose_wrap_all(compose);
4333 static void compose_wrap_all(Compose *compose)
4335 compose_wrap_all_full(compose, FALSE);
4338 static void compose_wrap_all_full(Compose *compose, gboolean force)
4340 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4341 GtkTextBuffer *buffer;
4343 gboolean modified = TRUE;
4345 buffer = gtk_text_view_get_buffer(text);
4347 gtk_text_buffer_get_start_iter(buffer, &iter);
4348 while (!gtk_text_iter_is_end(&iter) && modified)
4349 modified = compose_beautify_paragraph(compose, &iter, force);
4353 static void compose_set_title(Compose *compose)
4359 edited = compose->modified ? _(" [Edited]") : "";
4361 subject = gtk_editable_get_chars(
4362 GTK_EDITABLE(compose->subject_entry), 0, -1);
4364 #ifndef GENERIC_UMPC
4365 if (subject && strlen(subject))
4366 str = g_strdup_printf(_("%s - Compose message%s"),
4369 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4371 str = g_strdup(_("Compose message"));
4374 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4380 * compose_current_mail_account:
4382 * Find a current mail account (the currently selected account, or the
4383 * default account, if a news account is currently selected). If a
4384 * mail account cannot be found, display an error message.
4386 * Return value: Mail account, or NULL if not found.
4388 static PrefsAccount *
4389 compose_current_mail_account(void)
4393 if (cur_account && cur_account->protocol != A_NNTP)
4396 ac = account_get_default();
4397 if (!ac || ac->protocol == A_NNTP) {
4398 alertpanel_error(_("Account for sending mail is not specified.\n"
4399 "Please select a mail account before sending."));
4406 #define QUOTE_IF_REQUIRED(out, str) \
4408 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4412 len = strlen(str) + 3; \
4413 if ((__tmp = alloca(len)) == NULL) { \
4414 g_warning("can't allocate memory\n"); \
4415 g_string_free(header, TRUE); \
4418 g_snprintf(__tmp, len, "\"%s\"", str); \
4423 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4424 g_warning("can't allocate memory\n"); \
4425 g_string_free(header, TRUE); \
4428 strcpy(__tmp, str); \
4434 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4436 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4440 len = strlen(str) + 3; \
4441 if ((__tmp = alloca(len)) == NULL) { \
4442 g_warning("can't allocate memory\n"); \
4445 g_snprintf(__tmp, len, "\"%s\"", str); \
4450 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4451 g_warning("can't allocate memory\n"); \
4454 strcpy(__tmp, str); \
4460 static void compose_select_account(Compose *compose, PrefsAccount *account,
4465 g_return_if_fail(account != NULL);
4467 compose->account = account;
4469 if (account->name && *account->name) {
4471 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4472 from = g_strdup_printf("%s <%s>",
4473 buf, account->address);
4474 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4476 from = g_strdup_printf("<%s>",
4478 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4483 compose_set_title(compose);
4485 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4486 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4488 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4489 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4490 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4492 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4494 activate_privacy_system(compose, account, FALSE);
4496 if (!init && compose->mode != COMPOSE_REDIRECT) {
4497 undo_block(compose->undostruct);
4498 compose_insert_sig(compose, TRUE);
4499 undo_unblock(compose->undostruct);
4503 /* use account's dict info if set */
4504 if (compose->gtkaspell) {
4505 if (account->enable_default_dictionary)
4506 gtkaspell_change_dict(compose->gtkaspell,
4507 account->default_dictionary, FALSE);
4508 if (account->enable_default_alt_dictionary)
4509 gtkaspell_change_alt_dict(compose->gtkaspell,
4510 account->default_alt_dictionary);
4511 if (account->enable_default_dictionary
4512 || account->enable_default_alt_dictionary)
4513 compose_spell_menu_changed(compose);
4518 gboolean compose_check_for_valid_recipient(Compose *compose) {
4519 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4520 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4521 gboolean recipient_found = FALSE;
4525 /* free to and newsgroup list */
4526 slist_free_strings(compose->to_list);
4527 g_slist_free(compose->to_list);
4528 compose->to_list = NULL;
4530 slist_free_strings(compose->newsgroup_list);
4531 g_slist_free(compose->newsgroup_list);
4532 compose->newsgroup_list = NULL;
4534 /* search header entries for to and newsgroup entries */
4535 for (list = compose->header_list; list; list = list->next) {
4538 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4539 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4542 if (entry[0] != '\0') {
4543 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4544 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4545 compose->to_list = address_list_append(compose->to_list, entry);
4546 recipient_found = TRUE;
4549 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4550 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4551 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4552 recipient_found = TRUE;
4559 return recipient_found;
4562 static gboolean compose_check_for_set_recipients(Compose *compose)
4564 if (compose->account->set_autocc && compose->account->auto_cc) {
4565 gboolean found_other = FALSE;
4567 /* search header entries for to and newsgroup entries */
4568 for (list = compose->header_list; list; list = list->next) {
4571 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4572 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4575 if (strcmp(entry, compose->account->auto_cc)
4576 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4586 if (compose->batch) {
4587 gtk_widget_show_all(compose->window);
4589 aval = alertpanel(_("Send"),
4590 _("The only recipient is the default CC address. Send anyway?"),
4591 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4592 if (aval != G_ALERTALTERNATE)
4596 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4597 gboolean found_other = FALSE;
4599 /* search header entries for to and newsgroup entries */
4600 for (list = compose->header_list; list; list = list->next) {
4603 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4604 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4607 if (strcmp(entry, compose->account->auto_bcc)
4608 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4618 if (compose->batch) {
4619 gtk_widget_show_all(compose->window);
4621 aval = alertpanel(_("Send"),
4622 _("The only recipient is the default BCC address. Send anyway?"),
4623 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4624 if (aval != G_ALERTALTERNATE)
4631 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4635 if (compose_check_for_valid_recipient(compose) == FALSE) {
4636 if (compose->batch) {
4637 gtk_widget_show_all(compose->window);
4639 alertpanel_error(_("Recipient is not specified."));
4643 if (compose_check_for_set_recipients(compose) == FALSE) {
4647 if (!compose->batch) {
4648 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4649 if (*str == '\0' && check_everything == TRUE &&
4650 compose->mode != COMPOSE_REDIRECT) {
4652 gchar *button_label;
4655 if (compose->sending)
4656 button_label = _("+_Send");
4658 button_label = _("+_Queue");
4659 message = g_strdup_printf(_("Subject is empty. %s"),
4660 compose->sending?_("Send it anyway?"):
4661 _("Queue it anyway?"));
4663 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4664 GTK_STOCK_CANCEL, button_label, NULL);
4666 if (aval != G_ALERTALTERNATE)
4671 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4677 gint compose_send(Compose *compose)
4680 FolderItem *folder = NULL;
4682 gchar *msgpath = NULL;
4683 gboolean discard_window = FALSE;
4684 gchar *errstr = NULL;
4685 gchar *tmsgid = NULL;
4686 MainWindow *mainwin = mainwindow_get_mainwindow();
4687 gboolean queued_removed = FALSE;
4689 if (prefs_common.send_dialog_invisible
4690 || compose->batch == TRUE)
4691 discard_window = TRUE;
4693 compose_allow_user_actions (compose, FALSE);
4694 compose->sending = TRUE;
4696 if (compose_check_entries(compose, TRUE) == FALSE) {
4697 if (compose->batch) {
4698 gtk_widget_show_all(compose->window);
4704 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4707 if (compose->batch) {
4708 gtk_widget_show_all(compose->window);
4711 alertpanel_error(_("Could not queue message for sending:\n\n"
4712 "Charset conversion failed."));
4713 } else if (val == -5) {
4714 alertpanel_error(_("Could not queue message for sending:\n\n"
4715 "Couldn't get recipient encryption key."));
4716 } else if (val == -6) {
4718 } else if (val == -3) {
4719 if (privacy_peek_error())
4720 alertpanel_error(_("Could not queue message for sending:\n\n"
4721 "Signature failed: %s"), privacy_get_error());
4722 } else if (val == -2 && errno != 0) {
4723 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4725 alertpanel_error(_("Could not queue message for sending."));
4730 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4731 if (discard_window) {
4732 compose->sending = FALSE;
4733 compose_close(compose);
4734 /* No more compose access in the normal codepath
4735 * after this point! */
4740 alertpanel_error(_("The message was queued but could not be "
4741 "sent.\nUse \"Send queued messages\" from "
4742 "the main window to retry."));
4743 if (!discard_window) {
4750 if (msgpath == NULL) {
4751 msgpath = folder_item_fetch_msg(folder, msgnum);
4752 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4755 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4756 claws_unlink(msgpath);
4759 if (!discard_window) {
4761 if (!queued_removed)
4762 folder_item_remove_msg(folder, msgnum);
4763 folder_item_scan(folder);
4765 /* make sure we delete that */
4766 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4768 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4769 folder_item_remove_msg(folder, tmp->msgnum);
4770 procmsg_msginfo_free(tmp);
4777 if (!queued_removed)
4778 folder_item_remove_msg(folder, msgnum);
4779 folder_item_scan(folder);
4781 /* make sure we delete that */
4782 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4784 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4785 folder_item_remove_msg(folder, tmp->msgnum);
4786 procmsg_msginfo_free(tmp);
4789 if (!discard_window) {
4790 compose->sending = FALSE;
4791 compose_allow_user_actions (compose, TRUE);
4792 compose_close(compose);
4796 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4797 "the main window to retry."), errstr);
4800 alertpanel_error_log(_("The message was queued but could not be "
4801 "sent.\nUse \"Send queued messages\" from "
4802 "the main window to retry."));
4804 if (!discard_window) {
4813 toolbar_main_set_sensitive(mainwin);
4814 main_window_set_menu_sensitive(mainwin);
4820 compose_allow_user_actions (compose, TRUE);
4821 compose->sending = FALSE;
4822 compose->modified = TRUE;
4823 toolbar_main_set_sensitive(mainwin);
4824 main_window_set_menu_sensitive(mainwin);
4829 static gboolean compose_use_attach(Compose *compose)
4831 GtkTreeModel *model = gtk_tree_view_get_model
4832 (GTK_TREE_VIEW(compose->attach_clist));
4833 return gtk_tree_model_iter_n_children(model, NULL) > 0;
4836 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
4839 gchar buf[BUFFSIZE];
4841 gboolean first_to_address;
4842 gboolean first_cc_address;
4844 ComposeHeaderEntry *headerentry;
4845 const gchar *headerentryname;
4846 const gchar *cc_hdr;
4847 const gchar *to_hdr;
4848 gboolean err = FALSE;
4850 debug_print("Writing redirect header\n");
4852 cc_hdr = prefs_common_translated_header_name("Cc:");
4853 to_hdr = prefs_common_translated_header_name("To:");
4855 first_to_address = TRUE;
4856 for (list = compose->header_list; list; list = list->next) {
4857 headerentry = ((ComposeHeaderEntry *)list->data);
4858 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4860 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
4861 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4862 Xstrdup_a(str, entstr, return -1);
4864 if (str[0] != '\0') {
4865 compose_convert_header
4866 (compose, buf, sizeof(buf), str,
4867 strlen("Resent-To") + 2, TRUE);
4869 if (first_to_address) {
4870 err |= (fprintf(fp, "Resent-To: ") < 0);
4871 first_to_address = FALSE;
4873 err |= (fprintf(fp, ",") < 0);
4875 err |= (fprintf(fp, "%s", buf) < 0);
4879 if (!first_to_address) {
4880 err |= (fprintf(fp, "\n") < 0);
4883 first_cc_address = TRUE;
4884 for (list = compose->header_list; list; list = list->next) {
4885 headerentry = ((ComposeHeaderEntry *)list->data);
4886 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4888 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
4889 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4890 Xstrdup_a(str, strg, return -1);
4892 if (str[0] != '\0') {
4893 compose_convert_header
4894 (compose, buf, sizeof(buf), str,
4895 strlen("Resent-Cc") + 2, TRUE);
4897 if (first_cc_address) {
4898 err |= (fprintf(fp, "Resent-Cc: ") < 0);
4899 first_cc_address = FALSE;
4901 err |= (fprintf(fp, ",") < 0);
4903 err |= (fprintf(fp, "%s", buf) < 0);
4907 if (!first_cc_address) {
4908 err |= (fprintf(fp, "\n") < 0);
4911 return (err ? -1:0);
4914 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
4916 gchar buf[BUFFSIZE];
4918 const gchar *entstr;
4919 /* struct utsname utsbuf; */
4920 gboolean err = FALSE;
4922 g_return_val_if_fail(fp != NULL, -1);
4923 g_return_val_if_fail(compose->account != NULL, -1);
4924 g_return_val_if_fail(compose->account->address != NULL, -1);
4927 get_rfc822_date(buf, sizeof(buf));
4928 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
4931 if (compose->account->name && *compose->account->name) {
4932 compose_convert_header
4933 (compose, buf, sizeof(buf), compose->account->name,
4934 strlen("From: "), TRUE);
4935 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
4936 buf, compose->account->address) < 0);
4938 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
4941 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4942 if (*entstr != '\0') {
4943 Xstrdup_a(str, entstr, return -1);
4946 compose_convert_header(compose, buf, sizeof(buf), str,
4947 strlen("Subject: "), FALSE);
4948 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
4952 /* Resent-Message-ID */
4953 if (compose->account->set_domain && compose->account->domain) {
4954 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
4955 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
4956 g_snprintf(buf, sizeof(buf), "%s",
4957 strchr(compose->account->address, '@') ?
4958 strchr(compose->account->address, '@')+1 :
4959 compose->account->address);
4961 g_snprintf(buf, sizeof(buf), "%s", "");
4964 if (compose->account->gen_msgid) {
4965 generate_msgid(buf, sizeof(buf));
4966 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
4967 compose->msgid = g_strdup(buf);
4969 compose->msgid = NULL;
4972 if (compose_redirect_write_headers_from_headerlist(compose, fp))
4975 /* separator between header and body */
4976 err |= (fputs("\n", fp) == EOF);
4978 return (err ? -1:0);
4981 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
4985 gchar buf[BUFFSIZE];
4987 gboolean skip = FALSE;
4988 gboolean err = FALSE;
4989 gchar *not_included[]={
4990 "Return-Path:", "Delivered-To:", "Received:",
4991 "Subject:", "X-UIDL:", "AF:",
4992 "NF:", "PS:", "SRH:",
4993 "SFN:", "DSR:", "MID:",
4994 "CFG:", "PT:", "S:",
4995 "RQ:", "SSV:", "NSV:",
4996 "SSH:", "R:", "MAID:",
4997 "NAID:", "RMID:", "FMID:",
4998 "SCF:", "RRCPT:", "NG:",
4999 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5000 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5001 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5002 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5003 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5006 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5007 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5011 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5013 for (i = 0; not_included[i] != NULL; i++) {
5014 if (g_ascii_strncasecmp(buf, not_included[i],
5015 strlen(not_included[i])) == 0) {
5022 if (fputs(buf, fdest) == -1)
5025 if (!prefs_common.redirect_keep_from) {
5026 if (g_ascii_strncasecmp(buf, "From:",
5027 strlen("From:")) == 0) {
5028 err |= (fputs(" (by way of ", fdest) == EOF);
5029 if (compose->account->name
5030 && *compose->account->name) {
5031 compose_convert_header
5032 (compose, buf, sizeof(buf),
5033 compose->account->name,
5036 err |= (fprintf(fdest, "%s <%s>",
5038 compose->account->address) < 0);
5040 err |= (fprintf(fdest, "%s",
5041 compose->account->address) < 0);
5042 err |= (fputs(")", fdest) == EOF);
5046 if (fputs("\n", fdest) == -1)
5053 if (compose_redirect_write_headers(compose, fdest))
5056 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5057 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5070 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5072 GtkTextBuffer *buffer;
5073 GtkTextIter start, end;
5076 const gchar *out_codeset;
5077 EncodingType encoding = ENC_UNKNOWN;
5078 MimeInfo *mimemsg, *mimetext;
5080 const gchar *src_codeset = CS_INTERNAL;
5082 if (action == COMPOSE_WRITE_FOR_SEND)
5083 attach_parts = TRUE;
5085 /* create message MimeInfo */
5086 mimemsg = procmime_mimeinfo_new();
5087 mimemsg->type = MIMETYPE_MESSAGE;
5088 mimemsg->subtype = g_strdup("rfc822");
5089 mimemsg->content = MIMECONTENT_MEM;
5090 mimemsg->tmp = TRUE; /* must free content later */
5091 mimemsg->data.mem = compose_get_header(compose);
5093 /* Create text part MimeInfo */
5094 /* get all composed text */
5095 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5096 gtk_text_buffer_get_start_iter(buffer, &start);
5097 gtk_text_buffer_get_end_iter(buffer, &end);
5098 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5100 out_codeset = conv_get_charset_str(compose->out_encoding);
5102 if (!out_codeset && is_ascii_str(chars)) {
5103 out_codeset = CS_US_ASCII;
5104 } else if (prefs_common.outgoing_fallback_to_ascii &&
5105 is_ascii_str(chars)) {
5106 out_codeset = CS_US_ASCII;
5107 encoding = ENC_7BIT;
5111 gchar *test_conv_global_out = NULL;
5112 gchar *test_conv_reply = NULL;
5114 /* automatic mode. be automatic. */
5115 codeconv_set_strict(TRUE);
5117 out_codeset = conv_get_outgoing_charset_str();
5119 debug_print("trying to convert to %s\n", out_codeset);
5120 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5123 if (!test_conv_global_out && compose->orig_charset
5124 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5125 out_codeset = compose->orig_charset;
5126 debug_print("failure; trying to convert to %s\n", out_codeset);
5127 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5130 if (!test_conv_global_out && !test_conv_reply) {
5132 out_codeset = CS_INTERNAL;
5133 debug_print("failure; finally using %s\n", out_codeset);
5135 g_free(test_conv_global_out);
5136 g_free(test_conv_reply);
5137 codeconv_set_strict(FALSE);
5140 if (encoding == ENC_UNKNOWN) {
5141 if (prefs_common.encoding_method == CTE_BASE64)
5142 encoding = ENC_BASE64;
5143 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5144 encoding = ENC_QUOTED_PRINTABLE;
5145 else if (prefs_common.encoding_method == CTE_8BIT)
5146 encoding = ENC_8BIT;
5148 encoding = procmime_get_encoding_for_charset(out_codeset);
5151 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5152 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5154 if (action == COMPOSE_WRITE_FOR_SEND) {
5155 codeconv_set_strict(TRUE);
5156 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5157 codeconv_set_strict(FALSE);
5163 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5164 "to the specified %s charset.\n"
5165 "Send it as %s?"), out_codeset, src_codeset);
5166 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5167 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5170 if (aval != G_ALERTALTERNATE) {
5175 out_codeset = src_codeset;
5181 out_codeset = src_codeset;
5186 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5187 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5188 strstr(buf, "\nFrom ") != NULL) {
5189 encoding = ENC_QUOTED_PRINTABLE;
5193 mimetext = procmime_mimeinfo_new();
5194 mimetext->content = MIMECONTENT_MEM;
5195 mimetext->tmp = TRUE; /* must free content later */
5196 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5197 * and free the data, which we need later. */
5198 mimetext->data.mem = g_strdup(buf);
5199 mimetext->type = MIMETYPE_TEXT;
5200 mimetext->subtype = g_strdup("plain");
5201 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5202 g_strdup(out_codeset));
5204 /* protect trailing spaces when signing message */
5205 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5206 privacy_system_can_sign(compose->privacy_system)) {
5207 encoding = ENC_QUOTED_PRINTABLE;
5210 debug_print("main text: %zd bytes encoded as %s in %d\n",
5211 strlen(buf), out_codeset, encoding);
5213 /* check for line length limit */
5214 if (action == COMPOSE_WRITE_FOR_SEND &&
5215 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5216 check_line_length(buf, 1000, &line) < 0) {
5220 msg = g_strdup_printf
5221 (_("Line %d exceeds the line length limit (998 bytes).\n"
5222 "The contents of the message might be broken on the way to the delivery.\n"
5224 "Send it anyway?"), line + 1);
5225 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5227 if (aval != G_ALERTALTERNATE) {
5233 if (encoding != ENC_UNKNOWN)
5234 procmime_encode_content(mimetext, encoding);
5236 /* append attachment parts */
5237 if (compose_use_attach(compose) && attach_parts) {
5238 MimeInfo *mimempart;
5239 gchar *boundary = NULL;
5240 mimempart = procmime_mimeinfo_new();
5241 mimempart->content = MIMECONTENT_EMPTY;
5242 mimempart->type = MIMETYPE_MULTIPART;
5243 mimempart->subtype = g_strdup("mixed");
5247 boundary = generate_mime_boundary(NULL);
5248 } while (strstr(buf, boundary) != NULL);
5250 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5253 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5255 g_node_append(mimempart->node, mimetext->node);
5256 g_node_append(mimemsg->node, mimempart->node);
5258 compose_add_attachments(compose, mimempart);
5260 g_node_append(mimemsg->node, mimetext->node);
5264 /* sign message if sending */
5265 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5266 privacy_system_can_sign(compose->privacy_system))
5267 if (!privacy_sign(compose->privacy_system, mimemsg, compose->account))
5270 procmime_write_mimeinfo(mimemsg, fp);
5272 procmime_mimeinfo_free_all(mimemsg);
5277 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5279 GtkTextBuffer *buffer;
5280 GtkTextIter start, end;
5285 if ((fp = g_fopen(file, "wb")) == NULL) {
5286 FILE_OP_ERROR(file, "fopen");
5290 /* chmod for security */
5291 if (change_file_mode_rw(fp, file) < 0) {
5292 FILE_OP_ERROR(file, "chmod");
5293 g_warning("can't change file mode\n");
5296 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5297 gtk_text_buffer_get_start_iter(buffer, &start);
5298 gtk_text_buffer_get_end_iter(buffer, &end);
5299 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5301 chars = conv_codeset_strdup
5302 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5305 if (!chars) return -1;
5308 len = strlen(chars);
5309 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5310 FILE_OP_ERROR(file, "fwrite");
5319 if (fclose(fp) == EOF) {
5320 FILE_OP_ERROR(file, "fclose");
5327 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5330 MsgInfo *msginfo = compose->targetinfo;
5332 g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5333 if (!msginfo) return -1;
5335 if (!force && MSG_IS_LOCKED(msginfo->flags))
5338 item = msginfo->folder;
5339 g_return_val_if_fail(item != NULL, -1);
5341 if (procmsg_msg_exist(msginfo) &&
5342 (folder_has_parent_of_type(item, F_QUEUE) ||
5343 folder_has_parent_of_type(item, F_DRAFT)
5344 || msginfo == compose->autosaved_draft)) {
5345 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5346 g_warning("can't remove the old message\n");
5349 debug_print("removed reedit target %d\n", msginfo->msgnum);
5356 static void compose_remove_draft(Compose *compose)
5359 MsgInfo *msginfo = compose->targetinfo;
5360 drafts = account_get_special_folder(compose->account, F_DRAFT);
5362 if (procmsg_msg_exist(msginfo)) {
5363 folder_item_remove_msg(drafts, msginfo->msgnum);
5368 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5369 gboolean remove_reedit_target)
5371 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5374 static gboolean compose_warn_encryption(Compose *compose)
5376 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5377 AlertValue val = G_ALERTALTERNATE;
5379 if (warning == NULL)
5382 val = alertpanel_full(_("Encryption warning"), warning,
5383 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5384 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5385 if (val & G_ALERTDISABLE) {
5386 val &= ~G_ALERTDISABLE;
5387 if (val == G_ALERTALTERNATE)
5388 privacy_inhibit_encrypt_warning(compose->privacy_system,
5392 if (val == G_ALERTALTERNATE) {
5399 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5400 gchar **msgpath, gboolean check_subject,
5401 gboolean remove_reedit_target)
5408 static gboolean lock = FALSE;
5409 PrefsAccount *mailac = NULL, *newsac = NULL;
5410 gboolean err = FALSE;
5412 debug_print("queueing message...\n");
5413 g_return_val_if_fail(compose->account != NULL, -1);
5417 if (compose_check_entries(compose, check_subject) == FALSE) {
5419 if (compose->batch) {
5420 gtk_widget_show_all(compose->window);
5425 if (!compose->to_list && !compose->newsgroup_list) {
5426 g_warning("can't get recipient list.");
5431 if (compose->to_list) {
5432 if (compose->account->protocol != A_NNTP)
5433 mailac = compose->account;
5434 else if (cur_account && cur_account->protocol != A_NNTP)
5435 mailac = cur_account;
5436 else if (!(mailac = compose_current_mail_account())) {
5438 alertpanel_error(_("No account for sending mails available!"));
5443 if (compose->newsgroup_list) {
5444 if (compose->account->protocol == A_NNTP)
5445 newsac = compose->account;
5446 else if (!newsac->protocol != A_NNTP) {
5448 alertpanel_error(_("No account for posting news available!"));
5453 /* write queue header */
5454 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5455 G_DIR_SEPARATOR, compose, (guint) rand());
5456 debug_print("queuing to %s\n", tmp);
5457 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5458 FILE_OP_ERROR(tmp, "fopen");
5464 if (change_file_mode_rw(fp, tmp) < 0) {
5465 FILE_OP_ERROR(tmp, "chmod");
5466 g_warning("can't change file mode\n");
5469 /* queueing variables */
5470 err |= (fprintf(fp, "AF:\n") < 0);
5471 err |= (fprintf(fp, "NF:0\n") < 0);
5472 err |= (fprintf(fp, "PS:10\n") < 0);
5473 err |= (fprintf(fp, "SRH:1\n") < 0);
5474 err |= (fprintf(fp, "SFN:\n") < 0);
5475 err |= (fprintf(fp, "DSR:\n") < 0);
5477 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5479 err |= (fprintf(fp, "MID:\n") < 0);
5480 err |= (fprintf(fp, "CFG:\n") < 0);
5481 err |= (fprintf(fp, "PT:0\n") < 0);
5482 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5483 err |= (fprintf(fp, "RQ:\n") < 0);
5485 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5487 err |= (fprintf(fp, "SSV:\n") < 0);
5489 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5491 err |= (fprintf(fp, "NSV:\n") < 0);
5492 err |= (fprintf(fp, "SSH:\n") < 0);
5493 /* write recepient list */
5494 if (compose->to_list) {
5495 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5496 for (cur = compose->to_list->next; cur != NULL;
5498 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5499 err |= (fprintf(fp, "\n") < 0);
5501 /* write newsgroup list */
5502 if (compose->newsgroup_list) {
5503 err |= (fprintf(fp, "NG:") < 0);
5504 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5505 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5506 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5507 err |= (fprintf(fp, "\n") < 0);
5509 /* Sylpheed account IDs */
5511 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5513 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5516 if (compose->privacy_system != NULL) {
5517 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5518 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5519 if (compose->use_encryption) {
5521 if (!compose_warn_encryption(compose)) {
5528 if (mailac && mailac->encrypt_to_self) {
5529 GSList *tmp_list = g_slist_copy(compose->to_list);
5530 tmp_list = g_slist_append(tmp_list, compose->account->address);
5531 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5532 g_slist_free(tmp_list);
5534 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5536 if (encdata != NULL) {
5537 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5538 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5539 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5541 } /* else we finally dont want to encrypt */
5543 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5544 /* and if encdata was null, it means there's been a problem in
5556 /* Save copy folder */
5557 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5558 gchar *savefolderid;
5560 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
5561 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5562 g_free(savefolderid);
5564 /* Save copy folder */
5565 if (compose->return_receipt) {
5566 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5568 /* Message-ID of message replying to */
5569 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5572 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5573 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5576 /* Message-ID of message forwarding to */
5577 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5580 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5581 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5585 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5586 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5588 /* end of headers */
5589 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5591 if (compose->redirect_filename != NULL) {
5592 if (compose_redirect_write_to_file(compose, fp) < 0) {
5601 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5606 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5610 g_warning("failed to write queue message\n");
5617 if (fclose(fp) == EOF) {
5618 FILE_OP_ERROR(tmp, "fclose");
5625 if (item && *item) {
5628 queue = account_get_special_folder(compose->account, F_QUEUE);
5631 g_warning("can't find queue folder\n");
5637 folder_item_scan(queue);
5638 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5639 g_warning("can't queue the message\n");
5646 if (msgpath == NULL) {
5652 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5653 compose_remove_reedit_target(compose, FALSE);
5656 if ((msgnum != NULL) && (item != NULL)) {
5664 static void compose_add_attachments(Compose *compose, MimeInfo *parent)
5667 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5669 struct stat statbuf;
5670 gchar *type, *subtype;
5671 GtkTreeModel *model;
5674 model = gtk_tree_view_get_model(tree_view);
5676 if (!gtk_tree_model_get_iter_first(model, &iter))
5679 gtk_tree_model_get(model, &iter,
5683 mimepart = procmime_mimeinfo_new();
5684 mimepart->content = MIMECONTENT_FILE;
5685 mimepart->data.filename = g_strdup(ainfo->file);
5686 mimepart->tmp = FALSE; /* or we destroy our attachment */
5687 mimepart->offset = 0;
5689 stat(ainfo->file, &statbuf);
5690 mimepart->length = statbuf.st_size;
5692 type = g_strdup(ainfo->content_type);
5694 if (!strchr(type, '/')) {
5696 type = g_strdup("application/octet-stream");
5699 subtype = strchr(type, '/') + 1;
5700 *(subtype - 1) = '\0';
5701 mimepart->type = procmime_get_media_type(type);
5702 mimepart->subtype = g_strdup(subtype);
5705 if (mimepart->type == MIMETYPE_MESSAGE &&
5706 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5707 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5710 g_hash_table_insert(mimepart->typeparameters,
5711 g_strdup("name"), g_strdup(ainfo->name));
5712 g_hash_table_insert(mimepart->dispositionparameters,
5713 g_strdup("filename"), g_strdup(ainfo->name));
5714 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5718 if (compose->use_signing) {
5719 if (ainfo->encoding == ENC_7BIT)
5720 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5721 else if (ainfo->encoding == ENC_8BIT)
5722 ainfo->encoding = ENC_BASE64;
5725 procmime_encode_content(mimepart, ainfo->encoding);
5727 g_node_append(parent->node, mimepart->node);
5728 } while (gtk_tree_model_iter_next(model, &iter));
5731 #define IS_IN_CUSTOM_HEADER(header) \
5732 (compose->account->add_customhdr && \
5733 custom_header_find(compose->account->customhdr_list, header) != NULL)
5735 static void compose_add_headerfield_from_headerlist(Compose *compose,
5737 const gchar *fieldname,
5738 const gchar *seperator)
5740 gchar *str, *fieldname_w_colon;
5741 gboolean add_field = FALSE;
5743 ComposeHeaderEntry *headerentry;
5744 const gchar *headerentryname;
5745 const gchar *trans_fieldname;
5748 if (IS_IN_CUSTOM_HEADER(fieldname))
5751 debug_print("Adding %s-fields\n", fieldname);
5753 fieldstr = g_string_sized_new(64);
5755 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5756 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5758 for (list = compose->header_list; list; list = list->next) {
5759 headerentry = ((ComposeHeaderEntry *)list->data);
5760 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5762 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5763 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5765 if (str[0] != '\0') {
5767 g_string_append(fieldstr, seperator);
5768 g_string_append(fieldstr, str);
5777 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5778 compose_convert_header
5779 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
5780 strlen(fieldname) + 2, TRUE);
5781 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5785 g_free(fieldname_w_colon);
5786 g_string_free(fieldstr, TRUE);
5791 static gchar *compose_get_header(Compose *compose)
5793 gchar buf[BUFFSIZE];
5794 const gchar *entry_str;
5798 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5800 gchar *from_name = NULL, *from_address = NULL;
5803 g_return_val_if_fail(compose->account != NULL, NULL);
5804 g_return_val_if_fail(compose->account->address != NULL, NULL);
5806 header = g_string_sized_new(64);
5809 get_rfc822_date(buf, sizeof(buf));
5810 g_string_append_printf(header, "Date: %s\n", buf);
5814 if (compose->account->name && *compose->account->name) {
5816 QUOTE_IF_REQUIRED(buf, compose->account->name);
5817 tmp = g_strdup_printf("%s <%s>",
5818 buf, compose->account->address);
5820 tmp = g_strdup_printf("%s",
5821 compose->account->address);
5823 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5824 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5826 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5827 from_address = g_strdup(compose->account->address);
5829 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5830 /* extract name and address */
5831 if (strstr(spec, " <") && strstr(spec, ">")) {
5832 from_address = g_strdup(strrchr(spec, '<')+1);
5833 *(strrchr(from_address, '>')) = '\0';
5834 from_name = g_strdup(spec);
5835 *(strrchr(from_name, '<')) = '\0';
5838 from_address = g_strdup(spec);
5845 if (from_name && *from_name) {
5846 compose_convert_header
5847 (compose, buf, sizeof(buf), from_name,
5848 strlen("From: "), TRUE);
5849 QUOTE_IF_REQUIRED(name, buf);
5851 g_string_append_printf(header, "From: %s <%s>\n",
5852 name, from_address);
5854 g_string_append_printf(header, "From: %s\n", from_address);
5857 g_free(from_address);
5860 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
5863 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
5866 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
5869 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
5872 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
5874 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
5877 compose_convert_header(compose, buf, sizeof(buf), str,
5878 strlen("Subject: "), FALSE);
5879 g_string_append_printf(header, "Subject: %s\n", buf);
5885 if (compose->account->set_domain && compose->account->domain) {
5886 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5887 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5888 g_snprintf(buf, sizeof(buf), "%s",
5889 strchr(compose->account->address, '@') ?
5890 strchr(compose->account->address, '@')+1 :
5891 compose->account->address);
5893 g_snprintf(buf, sizeof(buf), "%s", "");
5896 if (compose->account->gen_msgid) {
5897 generate_msgid(buf, sizeof(buf));
5898 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
5899 compose->msgid = g_strdup(buf);
5901 compose->msgid = NULL;
5904 if (compose->remove_references == FALSE) {
5906 if (compose->inreplyto && compose->to_list)
5907 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
5910 if (compose->references)
5911 g_string_append_printf(header, "References: %s\n", compose->references);
5915 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
5918 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
5921 if (compose->account->organization &&
5922 strlen(compose->account->organization) &&
5923 !IS_IN_CUSTOM_HEADER("Organization")) {
5924 compose_convert_header(compose, buf, sizeof(buf),
5925 compose->account->organization,
5926 strlen("Organization: "), FALSE);
5927 g_string_append_printf(header, "Organization: %s\n", buf);
5930 /* Program version and system info */
5931 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
5932 !compose->newsgroup_list) {
5933 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
5935 gtk_major_version, gtk_minor_version, gtk_micro_version,
5938 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
5939 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
5941 gtk_major_version, gtk_minor_version, gtk_micro_version,
5945 /* custom headers */
5946 if (compose->account->add_customhdr) {
5949 for (cur = compose->account->customhdr_list; cur != NULL;
5951 CustomHeader *chdr = (CustomHeader *)cur->data;
5953 if (custom_header_is_allowed(chdr->name)) {
5954 compose_convert_header
5955 (compose, buf, sizeof(buf),
5956 chdr->value ? chdr->value : "",
5957 strlen(chdr->name) + 2, FALSE);
5958 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
5963 /* Automatic Faces and X-Faces */
5964 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
5965 g_string_append_printf(header, "X-Face: %s\n", buf);
5967 else if (get_default_xface (buf, sizeof(buf)) == 0) {
5968 g_string_append_printf(header, "X-Face: %s\n", buf);
5970 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
5971 g_string_append_printf(header, "Face: %s\n", buf);
5973 else if (get_default_face (buf, sizeof(buf)) == 0) {
5974 g_string_append_printf(header, "Face: %s\n", buf);
5978 switch (compose->priority) {
5979 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
5980 "X-Priority: 1 (Highest)\n");
5982 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
5983 "X-Priority: 2 (High)\n");
5985 case PRIORITY_NORMAL: break;
5986 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
5987 "X-Priority: 4 (Low)\n");
5989 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
5990 "X-Priority: 5 (Lowest)\n");
5992 default: debug_print("compose: priority unknown : %d\n",
5996 /* Request Return Receipt */
5997 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
5998 if (compose->return_receipt) {
5999 if (compose->account->name
6000 && *compose->account->name) {
6001 compose_convert_header(compose, buf, sizeof(buf),
6002 compose->account->name,
6003 strlen("Disposition-Notification-To: "),
6005 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6007 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6011 /* get special headers */
6012 for (list = compose->header_list; list; list = list->next) {
6013 ComposeHeaderEntry *headerentry;
6016 gchar *headername_wcolon;
6017 const gchar *headername_trans;
6020 gboolean standard_header = FALSE;
6022 headerentry = ((ComposeHeaderEntry *)list->data);
6024 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6026 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6031 if (!strstr(tmp, ":")) {
6032 headername_wcolon = g_strconcat(tmp, ":", NULL);
6033 headername = g_strdup(tmp);
6035 headername_wcolon = g_strdup(tmp);
6036 headername = g_strdup(strtok(tmp, ":"));
6040 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6041 Xstrdup_a(headervalue, entry_str, return NULL);
6042 subst_char(headervalue, '\r', ' ');
6043 subst_char(headervalue, '\n', ' ');
6044 string = std_headers;
6045 while (*string != NULL) {
6046 headername_trans = prefs_common_translated_header_name(*string);
6047 if (!strcmp(headername_trans, headername_wcolon))
6048 standard_header = TRUE;
6051 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6052 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6055 g_free(headername_wcolon);
6059 g_string_free(header, FALSE);
6064 #undef IS_IN_CUSTOM_HEADER
6066 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6067 gint header_len, gboolean addr_field)
6069 gchar *tmpstr = NULL;
6070 const gchar *out_codeset = NULL;
6072 g_return_if_fail(src != NULL);
6073 g_return_if_fail(dest != NULL);
6075 if (len < 1) return;
6077 tmpstr = g_strdup(src);
6079 subst_char(tmpstr, '\n', ' ');
6080 subst_char(tmpstr, '\r', ' ');
6083 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6084 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6085 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6090 codeconv_set_strict(TRUE);
6091 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6092 conv_get_charset_str(compose->out_encoding));
6093 codeconv_set_strict(FALSE);
6095 if (!dest || *dest == '\0') {
6096 gchar *test_conv_global_out = NULL;
6097 gchar *test_conv_reply = NULL;
6099 /* automatic mode. be automatic. */
6100 codeconv_set_strict(TRUE);
6102 out_codeset = conv_get_outgoing_charset_str();
6104 debug_print("trying to convert to %s\n", out_codeset);
6105 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6108 if (!test_conv_global_out && compose->orig_charset
6109 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6110 out_codeset = compose->orig_charset;
6111 debug_print("failure; trying to convert to %s\n", out_codeset);
6112 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6115 if (!test_conv_global_out && !test_conv_reply) {
6117 out_codeset = CS_INTERNAL;
6118 debug_print("finally using %s\n", out_codeset);
6120 g_free(test_conv_global_out);
6121 g_free(test_conv_reply);
6122 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6124 codeconv_set_strict(FALSE);
6129 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6133 g_return_if_fail(user_data != NULL);
6135 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6136 g_strstrip(address);
6137 if (*address != '\0') {
6138 gchar *name = procheader_get_fromname(address);
6139 extract_address(address);
6140 addressbook_add_contact(name, address, NULL, NULL);
6145 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6147 GtkWidget *menuitem;
6150 g_return_if_fail(menu != NULL);
6151 g_return_if_fail(GTK_IS_MENU_SHELL(menu));
6153 menuitem = gtk_separator_menu_item_new();
6154 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6155 gtk_widget_show(menuitem);
6157 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6158 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6160 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6161 g_strstrip(address);
6162 if (*address == '\0') {
6163 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6166 g_signal_connect(G_OBJECT(menuitem), "activate",
6167 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6168 gtk_widget_show(menuitem);
6171 static void compose_create_header_entry(Compose *compose)
6173 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6178 const gchar *header = NULL;
6179 ComposeHeaderEntry *headerentry;
6180 gboolean standard_header = FALSE;
6181 #if !(GTK_CHECK_VERSION(2,12,0))
6182 GtkTooltips *tips = compose->tooltips;
6185 headerentry = g_new0(ComposeHeaderEntry, 1);
6188 combo = gtk_combo_box_entry_new_text();
6190 while(*string != NULL) {
6191 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6192 (gchar*)prefs_common_translated_header_name(*string));
6195 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6196 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6197 G_CALLBACK(compose_grab_focus_cb), compose);
6198 gtk_widget_show(combo);
6199 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6200 compose->header_nextrow, compose->header_nextrow+1,
6201 GTK_SHRINK, GTK_FILL, 0, 0);
6202 if (compose->header_last) {
6203 const gchar *last_header_entry = gtk_entry_get_text(
6204 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6206 while (*string != NULL) {
6207 if (!strcmp(*string, last_header_entry))
6208 standard_header = TRUE;
6211 if (standard_header)
6212 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6214 if (!compose->header_last || !standard_header) {
6215 switch(compose->account->protocol) {
6217 header = prefs_common_translated_header_name("Newsgroups:");
6220 header = prefs_common_translated_header_name("To:");
6225 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6227 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6228 G_CALLBACK(compose_grab_focus_cb), compose);
6231 entry = gtk_entry_new();
6232 gtk_widget_show(entry);
6233 CLAWS_SET_TIP(entry,
6234 _("Use <tab> to autocomplete from addressbook"));
6235 gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2,
6236 compose->header_nextrow, compose->header_nextrow+1,
6237 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6239 g_signal_connect(G_OBJECT(entry), "key-press-event",
6240 G_CALLBACK(compose_headerentry_key_press_event_cb),
6242 g_signal_connect(G_OBJECT(entry), "changed",
6243 G_CALLBACK(compose_headerentry_changed_cb),
6245 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6246 G_CALLBACK(compose_grab_focus_cb), compose);
6249 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6250 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6251 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6252 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6253 G_CALLBACK(compose_header_drag_received_cb),
6255 g_signal_connect(G_OBJECT(entry), "drag-drop",
6256 G_CALLBACK(compose_drag_drop),
6258 g_signal_connect(G_OBJECT(entry), "populate-popup",
6259 G_CALLBACK(compose_entry_popup_extend),
6262 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6264 headerentry->compose = compose;
6265 headerentry->combo = combo;
6266 headerentry->entry = entry;
6267 headerentry->headernum = compose->header_nextrow;
6269 compose->header_nextrow++;
6270 compose->header_last = headerentry;
6271 compose->header_list =
6272 g_slist_append(compose->header_list,
6276 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text)
6278 ComposeHeaderEntry *last_header;
6280 last_header = compose->header_last;
6282 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((last_header->combo)))), header);
6283 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6286 static void compose_remove_header_entries(Compose *compose)
6289 for (list = compose->header_list; list; list = list->next) {
6290 ComposeHeaderEntry *headerentry =
6291 (ComposeHeaderEntry *)list->data;
6292 gtk_widget_destroy(headerentry->combo);
6293 gtk_widget_destroy(headerentry->entry);
6294 g_free(headerentry);
6296 compose->header_last = NULL;
6297 g_slist_free(compose->header_list);
6298 compose->header_list = NULL;
6299 compose->header_nextrow = 1;
6300 compose_create_header_entry(compose);
6303 static GtkWidget *compose_create_header(Compose *compose)
6305 GtkWidget *from_optmenu_hbox;
6306 GtkWidget *header_scrolledwin;
6307 GtkWidget *header_table;
6311 /* header labels and entries */
6312 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6313 gtk_widget_show(header_scrolledwin);
6314 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6316 header_table = gtk_table_new(2, 2, FALSE);
6317 gtk_widget_show(header_table);
6318 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6319 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6320 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6323 /* option menu for selecting accounts */
6324 from_optmenu_hbox = compose_account_option_menu_create(compose);
6325 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6326 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6329 compose->header_table = header_table;
6330 compose->header_list = NULL;
6331 compose->header_nextrow = count;
6333 compose_create_header_entry(compose);
6335 compose->table = NULL;
6337 return header_scrolledwin ;
6340 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6342 Compose *compose = (Compose *)data;
6343 GdkEventButton event;
6346 event.time = gtk_get_current_event_time();
6348 return attach_button_pressed(compose->attach_clist, &event, compose);
6351 static GtkWidget *compose_create_attach(Compose *compose)
6353 GtkWidget *attach_scrwin;
6354 GtkWidget *attach_clist;
6356 GtkListStore *store;
6357 GtkCellRenderer *renderer;
6358 GtkTreeViewColumn *column;
6359 GtkTreeSelection *selection;
6361 /* attachment list */
6362 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6363 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6364 GTK_POLICY_AUTOMATIC,
6365 GTK_POLICY_AUTOMATIC);
6366 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6368 store = gtk_list_store_new(N_ATTACH_COLS,
6373 G_TYPE_AUTO_POINTER,
6375 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6376 (GTK_TREE_MODEL(store)));
6377 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6378 g_object_unref(store);
6380 renderer = gtk_cell_renderer_text_new();
6381 column = gtk_tree_view_column_new_with_attributes
6382 (_("Mime type"), renderer, "text",
6383 COL_MIMETYPE, NULL);
6384 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6386 renderer = gtk_cell_renderer_text_new();
6387 column = gtk_tree_view_column_new_with_attributes
6388 (_("Size"), renderer, "text",
6390 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6392 renderer = gtk_cell_renderer_text_new();
6393 column = gtk_tree_view_column_new_with_attributes
6394 (_("Name"), renderer, "text",
6396 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6398 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6399 prefs_common.use_stripes_everywhere);
6400 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6401 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6403 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6404 G_CALLBACK(attach_selected), compose);
6405 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6406 G_CALLBACK(attach_button_pressed), compose);
6408 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6409 G_CALLBACK(popup_attach_button_pressed), compose);
6411 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6412 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6413 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6414 G_CALLBACK(popup_attach_button_pressed), compose);
6416 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6417 G_CALLBACK(attach_key_pressed), compose);
6420 gtk_drag_dest_set(attach_clist,
6421 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6422 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6423 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6424 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6425 G_CALLBACK(compose_attach_drag_received_cb),
6427 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6428 G_CALLBACK(compose_drag_drop),
6431 compose->attach_scrwin = attach_scrwin;
6432 compose->attach_clist = attach_clist;
6434 return attach_scrwin;
6437 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6438 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6440 static GtkWidget *compose_create_others(Compose *compose)
6443 GtkWidget *savemsg_checkbtn;
6444 GtkWidget *savemsg_entry;
6445 GtkWidget *savemsg_select;
6448 gchar *folderidentifier;
6450 /* Table for settings */
6451 table = gtk_table_new(3, 1, FALSE);
6452 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6453 gtk_widget_show(table);
6454 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6457 /* Save Message to folder */
6458 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6459 gtk_widget_show(savemsg_checkbtn);
6460 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6461 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6462 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6464 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6465 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6467 savemsg_entry = gtk_entry_new();
6468 gtk_widget_show(savemsg_entry);
6469 gtk_table_attach_defaults(GTK_TABLE(table), savemsg_entry, 1, 2, rowcount, rowcount + 1);
6470 gtk_editable_set_editable(GTK_EDITABLE(savemsg_entry), prefs_common.savemsg);
6471 g_signal_connect_after(G_OBJECT(savemsg_entry), "grab_focus",
6472 G_CALLBACK(compose_grab_focus_cb), compose);
6473 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6474 folderidentifier = folder_item_get_identifier(account_get_special_folder
6475 (compose->account, F_OUTBOX));
6476 gtk_entry_set_text(GTK_ENTRY(savemsg_entry), folderidentifier);
6477 g_free(folderidentifier);
6480 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6481 gtk_widget_show(savemsg_select);
6482 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6483 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6484 G_CALLBACK(compose_savemsg_select_cb),
6489 compose->savemsg_checkbtn = savemsg_checkbtn;
6490 compose->savemsg_entry = savemsg_entry;
6495 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6497 gtk_editable_set_editable(GTK_EDITABLE(compose->savemsg_entry),
6498 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6501 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6506 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6509 path = folder_item_get_identifier(dest);
6511 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), path);
6515 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6516 GdkAtom clip, GtkTextIter *insert_place);
6519 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6523 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6525 if (event->button == 3) {
6527 GtkTextIter sel_start, sel_end;
6528 gboolean stuff_selected;
6530 /* move the cursor to allow GtkAspell to check the word
6531 * under the mouse */
6532 if (event->x && event->y) {
6533 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6534 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6536 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6539 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6540 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6543 stuff_selected = gtk_text_buffer_get_selection_bounds(
6545 &sel_start, &sel_end);
6547 gtk_text_buffer_place_cursor (buffer, &iter);
6548 /* reselect stuff */
6550 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6551 gtk_text_buffer_select_range(buffer,
6552 &sel_start, &sel_end);
6554 return FALSE; /* pass the event so that the right-click goes through */
6557 if (event->button == 2) {
6562 /* get the middle-click position to paste at the correct place */
6563 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6564 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6566 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6569 entry_paste_clipboard(compose, text,
6570 prefs_common.linewrap_pastes,
6571 GDK_SELECTION_PRIMARY, &iter);
6579 static void compose_spell_menu_changed(void *data)
6581 Compose *compose = (Compose *)data;
6583 GtkWidget *menuitem;
6584 GtkWidget *parent_item;
6585 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6588 if (compose->gtkaspell == NULL)
6591 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6592 "/Menu/Spelling/Options");
6594 /* setting the submenu removes /Spelling/Options from the factory
6595 * so we need to save it */
6597 if (parent_item == NULL) {
6598 parent_item = compose->aspell_options_menu;
6599 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6601 compose->aspell_options_menu = parent_item;
6603 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6605 spell_menu = g_slist_reverse(spell_menu);
6606 for (items = spell_menu;
6607 items; items = items->next) {
6608 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6609 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6610 gtk_widget_show(GTK_WIDGET(menuitem));
6612 g_slist_free(spell_menu);
6614 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6619 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6621 Compose *compose = (Compose *)data;
6622 GdkEventButton event;
6625 event.time = gtk_get_current_event_time();
6629 return text_clicked(compose->text, &event, compose);
6632 static gboolean compose_force_window_origin = TRUE;
6633 static Compose *compose_create(PrefsAccount *account,
6642 GtkWidget *handlebox;
6644 GtkWidget *notebook;
6646 GtkWidget *attach_hbox;
6647 GtkWidget *attach_lab1;
6648 GtkWidget *attach_lab2;
6653 GtkWidget *subject_hbox;
6654 GtkWidget *subject_frame;
6655 GtkWidget *subject_entry;
6659 GtkWidget *edit_vbox;
6660 GtkWidget *ruler_hbox;
6662 GtkWidget *scrolledwin;
6664 GtkTextBuffer *buffer;
6665 GtkClipboard *clipboard;
6668 UndoMain *undostruct;
6670 gchar *titles[N_ATTACH_COLS];
6671 GtkWidget *popupmenu;
6672 GtkWidget *tmpl_menu;
6673 GtkActionGroup *action_group = NULL;
6676 GtkAspell * gtkaspell = NULL;
6679 static GdkGeometry geometry;
6681 g_return_val_if_fail(account != NULL, NULL);
6683 debug_print("Creating compose window...\n");
6684 compose = g_new0(Compose, 1);
6686 titles[COL_MIMETYPE] = _("MIME type");
6687 titles[COL_SIZE] = _("Size");
6688 titles[COL_NAME] = _("Name");
6690 compose->batch = batch;
6691 compose->account = account;
6692 compose->folder = folder;
6694 compose->mutex = g_mutex_new();
6695 compose->set_cursor_pos = -1;
6697 #if !(GTK_CHECK_VERSION(2,12,0))
6698 compose->tooltips = tips;
6701 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6703 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6704 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6706 if (!geometry.max_width) {
6707 geometry.max_width = gdk_screen_width();
6708 geometry.max_height = gdk_screen_height();
6711 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6712 &geometry, GDK_HINT_MAX_SIZE);
6713 if (!geometry.min_width) {
6714 geometry.min_width = 600;
6715 geometry.min_height = 480;
6717 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6718 &geometry, GDK_HINT_MIN_SIZE);
6720 #ifndef GENERIC_UMPC
6721 if (compose_force_window_origin)
6722 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
6723 prefs_common.compose_y);
6725 g_signal_connect(G_OBJECT(window), "delete_event",
6726 G_CALLBACK(compose_delete_cb), compose);
6727 MANAGE_WINDOW_SIGNALS_CONNECT(window);
6728 gtk_widget_realize(window);
6730 gtkut_widget_set_composer_icon(window);
6732 vbox = gtk_vbox_new(FALSE, 0);
6733 gtk_container_add(GTK_CONTAINER(window), vbox);
6735 compose->ui_manager = gtk_ui_manager_new();
6736 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
6737 G_N_ELEMENTS(compose_entries), (gpointer)compose);
6738 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
6739 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
6740 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
6741 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
6742 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
6743 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
6744 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
6745 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
6748 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
6750 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENU)
6753 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
6754 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
6756 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
6758 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
6759 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
6760 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
6763 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
6764 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
6765 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6766 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
6767 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
6768 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
6769 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6770 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
6771 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6772 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
6775 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
6776 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
6777 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6779 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
6780 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
6781 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
6783 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
6784 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
6785 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
6786 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
6788 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
6790 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
6791 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
6792 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
6793 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
6794 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
6795 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
6796 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
6797 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
6798 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
6799 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
6800 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
6801 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
6802 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
6803 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
6804 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
6806 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6808 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
6809 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
6810 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
6811 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
6812 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
6814 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6816 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
6820 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
6821 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
6822 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
6823 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
6824 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
6825 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
6829 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
6830 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
6831 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
6832 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
6833 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
6835 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6836 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
6837 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6838 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
6839 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
6842 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6843 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
6844 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
6845 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
6846 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
6847 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
6848 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
6850 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6851 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
6852 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6853 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
6854 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6856 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
6858 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
6859 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6860 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
6861 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
6862 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6864 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
6865 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)
6866 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)
6867 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
6869 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
6871 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
6872 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)
6873 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)
6875 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
6877 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
6878 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)
6879 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
6881 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
6882 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)
6883 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
6885 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
6887 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
6888 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)
6889 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
6890 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
6891 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
6893 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
6894 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)
6895 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)
6896 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
6897 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
6899 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
6900 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
6901 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
6902 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
6903 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
6905 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
6906 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
6907 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)
6909 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
6910 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
6911 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
6915 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
6916 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
6917 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
6918 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6919 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
6920 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6923 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
6925 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
6926 gtk_widget_show_all(menubar);
6928 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
6930 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
6932 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
6935 if (prefs_common.toolbar_detachable) {
6936 handlebox = gtk_handle_box_new();
6938 handlebox = gtk_hbox_new(FALSE, 0);
6940 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
6942 gtk_widget_realize(handlebox);
6944 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
6947 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
6951 vbox2 = gtk_vbox_new(FALSE, 2);
6952 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
6953 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
6956 notebook = gtk_notebook_new();
6957 gtk_widget_set_size_request(notebook, -1, 130);
6958 gtk_widget_show(notebook);
6960 /* header labels and entries */
6961 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6962 compose_create_header(compose),
6963 gtk_label_new_with_mnemonic(_("Hea_der")));
6964 /* attachment list */
6965 attach_hbox = gtk_hbox_new(FALSE, 0);
6966 gtk_widget_show(attach_hbox);
6968 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
6969 gtk_widget_show(attach_lab1);
6970 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
6972 attach_lab2 = gtk_label_new("");
6973 gtk_widget_show(attach_lab2);
6974 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
6976 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6977 compose_create_attach(compose),
6980 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6981 compose_create_others(compose),
6982 gtk_label_new_with_mnemonic(_("Othe_rs")));
6985 subject_hbox = gtk_hbox_new(FALSE, 0);
6986 gtk_widget_show(subject_hbox);
6988 subject_frame = gtk_frame_new(NULL);
6989 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
6990 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
6991 gtk_widget_show(subject_frame);
6993 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
6994 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
6995 gtk_widget_show(subject);
6997 label = gtk_label_new(_("Subject:"));
6998 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
6999 gtk_widget_show(label);
7001 subject_entry = gtk_entry_new();
7002 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7003 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7004 G_CALLBACK(compose_grab_focus_cb), compose);
7005 gtk_widget_show(subject_entry);
7006 compose->subject_entry = subject_entry;
7007 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7009 edit_vbox = gtk_vbox_new(FALSE, 0);
7011 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7014 ruler_hbox = gtk_hbox_new(FALSE, 0);
7015 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7017 ruler = gtk_shruler_new();
7018 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7019 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7023 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7024 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7025 GTK_POLICY_AUTOMATIC,
7026 GTK_POLICY_AUTOMATIC);
7027 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7029 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7030 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7032 text = gtk_text_view_new();
7033 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7034 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7035 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7036 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7037 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7039 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7041 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7042 G_CALLBACK(compose_edit_size_alloc),
7044 g_signal_connect(G_OBJECT(buffer), "changed",
7045 G_CALLBACK(compose_changed_cb), compose);
7046 g_signal_connect(G_OBJECT(text), "grab_focus",
7047 G_CALLBACK(compose_grab_focus_cb), compose);
7048 g_signal_connect(G_OBJECT(buffer), "insert_text",
7049 G_CALLBACK(text_inserted), compose);
7050 g_signal_connect(G_OBJECT(text), "button_press_event",
7051 G_CALLBACK(text_clicked), compose);
7053 g_signal_connect(G_OBJECT(text), "popup-menu",
7054 G_CALLBACK(compose_popup_menu), compose);
7056 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7057 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7058 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7059 G_CALLBACK(compose_popup_menu), compose);
7061 g_signal_connect(G_OBJECT(subject_entry), "changed",
7062 G_CALLBACK(compose_changed_cb), compose);
7065 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7066 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7067 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7068 g_signal_connect(G_OBJECT(text), "drag_data_received",
7069 G_CALLBACK(compose_insert_drag_received_cb),
7071 g_signal_connect(G_OBJECT(text), "drag-drop",
7072 G_CALLBACK(compose_drag_drop),
7074 gtk_widget_show_all(vbox);
7076 /* pane between attach clist and text */
7077 paned = gtk_vpaned_new();
7078 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7080 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7081 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7083 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7085 gtk_paned_add1(GTK_PANED(paned), notebook);
7086 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7087 gtk_widget_show_all(paned);
7090 if (prefs_common.textfont) {
7091 PangoFontDescription *font_desc;
7093 font_desc = pango_font_description_from_string
7094 (prefs_common.textfont);
7096 gtk_widget_modify_font(text, font_desc);
7097 pango_font_description_free(font_desc);
7101 gtk_action_group_add_actions(action_group, compose_popup_entries,
7102 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7103 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7104 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7105 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7106 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7107 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7108 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7110 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7112 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7113 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7114 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7116 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7118 undostruct = undo_init(text);
7119 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7122 address_completion_start(window);
7124 compose->window = window;
7125 compose->vbox = vbox;
7126 compose->menubar = menubar;
7127 compose->handlebox = handlebox;
7129 compose->vbox2 = vbox2;
7131 compose->paned = paned;
7133 compose->attach_label = attach_lab2;
7135 compose->notebook = notebook;
7136 compose->edit_vbox = edit_vbox;
7137 compose->ruler_hbox = ruler_hbox;
7138 compose->ruler = ruler;
7139 compose->scrolledwin = scrolledwin;
7140 compose->text = text;
7142 compose->focused_editable = NULL;
7144 compose->popupmenu = popupmenu;
7146 compose->tmpl_menu = tmpl_menu;
7148 compose->mode = mode;
7149 compose->rmode = mode;
7151 compose->targetinfo = NULL;
7152 compose->replyinfo = NULL;
7153 compose->fwdinfo = NULL;
7155 compose->replyto = NULL;
7157 compose->bcc = NULL;
7158 compose->followup_to = NULL;
7160 compose->ml_post = NULL;
7162 compose->inreplyto = NULL;
7163 compose->references = NULL;
7164 compose->msgid = NULL;
7165 compose->boundary = NULL;
7167 compose->autowrap = prefs_common.autowrap;
7168 compose->autoindent = prefs_common.auto_indent;
7169 compose->use_signing = FALSE;
7170 compose->use_encryption = FALSE;
7171 compose->privacy_system = NULL;
7173 compose->modified = FALSE;
7175 compose->return_receipt = FALSE;
7177 compose->to_list = NULL;
7178 compose->newsgroup_list = NULL;
7180 compose->undostruct = undostruct;
7182 compose->sig_str = NULL;
7184 compose->exteditor_file = NULL;
7185 compose->exteditor_pid = -1;
7186 compose->exteditor_tag = -1;
7187 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7190 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7191 if (mode != COMPOSE_REDIRECT) {
7192 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7193 strcmp(prefs_common.dictionary, "")) {
7194 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7195 prefs_common.alt_dictionary,
7196 conv_get_locale_charset_str(),
7197 prefs_common.misspelled_col,
7198 prefs_common.check_while_typing,
7199 prefs_common.recheck_when_changing_dict,
7200 prefs_common.use_alternate,
7201 prefs_common.use_both_dicts,
7202 GTK_TEXT_VIEW(text),
7203 GTK_WINDOW(compose->window),
7204 compose_spell_menu_changed,
7207 alertpanel_error(_("Spell checker could not "
7209 gtkaspell_checkers_strerror());
7210 gtkaspell_checkers_reset_error();
7212 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7216 compose->gtkaspell = gtkaspell;
7217 compose_spell_menu_changed(compose);
7220 compose_select_account(compose, account, TRUE);
7222 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7223 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7225 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7226 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7228 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7229 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7231 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7232 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7234 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7235 if (account->protocol != A_NNTP)
7236 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7237 prefs_common_translated_header_name("To:"));
7239 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7240 prefs_common_translated_header_name("Newsgroups:"));
7242 addressbook_set_target_compose(compose);
7244 if (mode != COMPOSE_REDIRECT)
7245 compose_set_template_menu(compose);
7247 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7250 compose_list = g_list_append(compose_list, compose);
7252 if (!prefs_common.show_ruler)
7253 gtk_widget_hide(ruler_hbox);
7255 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7258 compose->priority = PRIORITY_NORMAL;
7259 compose_update_priority_menu_item(compose);
7261 compose_set_out_encoding(compose);
7264 compose_update_actions_menu(compose);
7266 /* Privacy Systems menu */
7267 compose_update_privacy_systems_menu(compose);
7269 activate_privacy_system(compose, account, TRUE);
7270 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7272 gtk_widget_realize(window);
7274 gtk_widget_show(window);
7276 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7277 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7284 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7289 GtkWidget *optmenubox;
7292 GtkWidget *from_name = NULL;
7293 #if !(GTK_CHECK_VERSION(2,12,0))
7294 GtkTooltips *tips = compose->tooltips;
7297 gint num = 0, def_menu = 0;
7299 accounts = account_get_list();
7300 g_return_val_if_fail(accounts != NULL, NULL);
7302 optmenubox = gtk_event_box_new();
7303 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7304 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7306 hbox = gtk_hbox_new(FALSE, 6);
7307 from_name = gtk_entry_new();
7309 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7310 G_CALLBACK(compose_grab_focus_cb), compose);
7312 for (; accounts != NULL; accounts = accounts->next, num++) {
7313 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7314 gchar *name, *from = NULL;
7316 if (ac == compose->account) def_menu = num;
7318 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7321 if (ac == compose->account) {
7322 if (ac->name && *ac->name) {
7324 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7325 from = g_strdup_printf("%s <%s>",
7327 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7329 from = g_strdup_printf("%s",
7331 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7334 COMBOBOX_ADD(menu, name, ac->account_id);
7339 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7341 g_signal_connect(G_OBJECT(optmenu), "changed",
7342 G_CALLBACK(account_activated),
7344 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7345 G_CALLBACK(compose_entry_popup_extend),
7348 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7349 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7351 CLAWS_SET_TIP(optmenubox,
7352 _("Account to use for this email"));
7353 CLAWS_SET_TIP(from_name,
7354 _("Sender address to be used"));
7356 compose->from_name = from_name;
7361 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7363 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7364 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7365 Compose *compose = (Compose *) data;
7367 compose->priority = value;
7371 static void compose_reply_change_mode(Compose *compose,
7374 gboolean was_modified = compose->modified;
7376 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7378 g_return_if_fail(compose->replyinfo != NULL);
7380 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7382 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7384 if (action == COMPOSE_REPLY_TO_ALL)
7386 if (action == COMPOSE_REPLY_TO_SENDER)
7388 if (action == COMPOSE_REPLY_TO_LIST)
7391 compose_remove_header_entries(compose);
7392 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7393 if (compose->account->set_autocc && compose->account->auto_cc)
7394 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7396 if (compose->account->set_autobcc && compose->account->auto_bcc)
7397 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7399 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7400 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7401 compose_show_first_last_header(compose, TRUE);
7402 compose->modified = was_modified;
7403 compose_set_title(compose);
7406 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7408 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7409 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7410 Compose *compose = (Compose *) data;
7413 compose_reply_change_mode(compose, value);
7416 static void compose_update_priority_menu_item(Compose * compose)
7418 GtkWidget *menuitem = NULL;
7419 switch (compose->priority) {
7420 case PRIORITY_HIGHEST:
7421 menuitem = gtk_ui_manager_get_widget
7422 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7425 menuitem = gtk_ui_manager_get_widget
7426 (compose->ui_manager, "/Menu/Options/Priority/High");
7428 case PRIORITY_NORMAL:
7429 menuitem = gtk_ui_manager_get_widget
7430 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7433 menuitem = gtk_ui_manager_get_widget
7434 (compose->ui_manager, "/Menu/Options/Priority/Low");
7436 case PRIORITY_LOWEST:
7437 menuitem = gtk_ui_manager_get_widget
7438 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7441 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7444 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7446 Compose *compose = (Compose *) data;
7448 gboolean can_sign = FALSE, can_encrypt = FALSE;
7450 g_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7452 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7455 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7456 g_free(compose->privacy_system);
7457 compose->privacy_system = NULL;
7458 if (systemid != NULL) {
7459 compose->privacy_system = g_strdup(systemid);
7461 can_sign = privacy_system_can_sign(systemid);
7462 can_encrypt = privacy_system_can_encrypt(systemid);
7465 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7467 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7468 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7471 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7473 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7474 GtkWidget *menuitem = NULL;
7476 gboolean can_sign = FALSE, can_encrypt = FALSE;
7477 gboolean found = FALSE;
7479 if (compose->privacy_system != NULL) {
7481 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7482 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7483 g_return_if_fail(menuitem != NULL);
7485 amenu = GTK_MENU_SHELL(menuitem)->children;
7487 while (amenu != NULL) {
7488 GList *alist = amenu->next;
7490 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7491 if (systemid != NULL) {
7492 if (strcmp(systemid, compose->privacy_system) == 0 &&
7493 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7494 menuitem = GTK_WIDGET(amenu->data);
7496 can_sign = privacy_system_can_sign(systemid);
7497 can_encrypt = privacy_system_can_encrypt(systemid);
7501 } else if (strlen(compose->privacy_system) == 0 &&
7502 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7503 menuitem = GTK_WIDGET(amenu->data);
7506 can_encrypt = FALSE;
7513 if (menuitem != NULL)
7514 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7516 if (warn && !found && strlen(compose->privacy_system)) {
7517 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7518 "will not be able to sign or encrypt this message."),
7519 compose->privacy_system);
7523 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7524 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7527 static void compose_set_out_encoding(Compose *compose)
7529 CharSet out_encoding;
7530 const gchar *branch = NULL;
7531 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7533 switch(out_encoding) {
7534 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7535 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7536 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7537 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7538 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7539 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7540 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7541 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7542 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7543 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7544 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7545 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7546 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7547 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7548 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7549 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7550 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7551 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7552 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7553 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7554 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7555 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7556 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7557 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7558 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7559 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7560 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7561 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7562 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7563 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7564 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7565 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7567 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7570 static void compose_set_template_menu(Compose *compose)
7572 GSList *tmpl_list, *cur;
7576 tmpl_list = template_get_config();
7578 menu = gtk_menu_new();
7580 gtk_menu_set_accel_group (GTK_MENU (menu),
7581 gtk_ui_manager_get_accel_group(compose->ui_manager));
7582 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7583 Template *tmpl = (Template *)cur->data;
7584 gchar *accel_path = NULL;
7585 item = gtk_menu_item_new_with_label(tmpl->name);
7586 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7587 g_signal_connect(G_OBJECT(item), "activate",
7588 G_CALLBACK(compose_template_activate_cb),
7590 g_object_set_data(G_OBJECT(item), "template", tmpl);
7591 gtk_widget_show(item);
7592 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7593 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7597 gtk_widget_show(menu);
7598 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7601 void compose_update_actions_menu(Compose *compose)
7603 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7606 static void compose_update_privacy_systems_menu(Compose *compose)
7608 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7609 GSList *systems, *cur;
7611 GtkWidget *system_none;
7613 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7614 GtkWidget *privacy_menu = gtk_menu_new();
7616 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7617 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7619 g_signal_connect(G_OBJECT(system_none), "activate",
7620 G_CALLBACK(compose_set_privacy_system_cb), compose);
7622 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7623 gtk_widget_show(system_none);
7625 systems = privacy_get_system_ids();
7626 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7627 gchar *systemid = cur->data;
7629 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7630 widget = gtk_radio_menu_item_new_with_label(group,
7631 privacy_system_get_name(systemid));
7632 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7633 g_strdup(systemid), g_free);
7634 g_signal_connect(G_OBJECT(widget), "activate",
7635 G_CALLBACK(compose_set_privacy_system_cb), compose);
7637 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7638 gtk_widget_show(widget);
7641 g_slist_free(systems);
7642 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7643 gtk_widget_show_all(privacy_menu);
7644 gtk_widget_show_all(privacy_menuitem);
7647 void compose_reflect_prefs_all(void)
7652 for (cur = compose_list; cur != NULL; cur = cur->next) {
7653 compose = (Compose *)cur->data;
7654 compose_set_template_menu(compose);
7658 void compose_reflect_prefs_pixmap_theme(void)
7663 for (cur = compose_list; cur != NULL; cur = cur->next) {
7664 compose = (Compose *)cur->data;
7665 toolbar_update(TOOLBAR_COMPOSE, compose);
7669 static const gchar *compose_quote_char_from_context(Compose *compose)
7671 const gchar *qmark = NULL;
7673 g_return_val_if_fail(compose != NULL, NULL);
7675 switch (compose->mode) {
7676 /* use forward-specific quote char */
7677 case COMPOSE_FORWARD:
7678 case COMPOSE_FORWARD_AS_ATTACH:
7679 case COMPOSE_FORWARD_INLINE:
7680 if (compose->folder && compose->folder->prefs &&
7681 compose->folder->prefs->forward_with_format)
7682 qmark = compose->folder->prefs->forward_quotemark;
7683 else if (compose->account->forward_with_format)
7684 qmark = compose->account->forward_quotemark;
7686 qmark = prefs_common.fw_quotemark;
7689 /* use reply-specific quote char in all other modes */
7691 if (compose->folder && compose->folder->prefs &&
7692 compose->folder->prefs->reply_with_format)
7693 qmark = compose->folder->prefs->reply_quotemark;
7694 else if (compose->account->reply_with_format)
7695 qmark = compose->account->reply_quotemark;
7697 qmark = prefs_common.quotemark;
7701 if (qmark == NULL || *qmark == '\0')
7707 static void compose_template_apply(Compose *compose, Template *tmpl,
7711 GtkTextBuffer *buffer;
7715 gchar *parsed_str = NULL;
7716 gint cursor_pos = 0;
7717 const gchar *err_msg = _("Template body format error at line %d.");
7720 /* process the body */
7722 text = GTK_TEXT_VIEW(compose->text);
7723 buffer = gtk_text_view_get_buffer(text);
7726 qmark = compose_quote_char_from_context(compose);
7728 if (compose->replyinfo != NULL) {
7731 gtk_text_buffer_set_text(buffer, "", -1);
7732 mark = gtk_text_buffer_get_insert(buffer);
7733 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7735 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7736 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7738 } else if (compose->fwdinfo != NULL) {
7741 gtk_text_buffer_set_text(buffer, "", -1);
7742 mark = gtk_text_buffer_get_insert(buffer);
7743 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7745 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7746 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7749 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7751 GtkTextIter start, end;
7754 gtk_text_buffer_get_start_iter(buffer, &start);
7755 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7756 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7758 /* clear the buffer now */
7760 gtk_text_buffer_set_text(buffer, "", -1);
7762 parsed_str = compose_quote_fmt(compose, dummyinfo,
7763 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7764 procmsg_msginfo_free( dummyinfo );
7770 gtk_text_buffer_set_text(buffer, "", -1);
7771 mark = gtk_text_buffer_get_insert(buffer);
7772 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7775 if (replace && parsed_str && compose->account->auto_sig)
7776 compose_insert_sig(compose, FALSE);
7778 if (replace && parsed_str) {
7779 gtk_text_buffer_get_start_iter(buffer, &iter);
7780 gtk_text_buffer_place_cursor(buffer, &iter);
7784 cursor_pos = quote_fmt_get_cursor_pos();
7785 compose->set_cursor_pos = cursor_pos;
7786 if (cursor_pos == -1)
7788 gtk_text_buffer_get_start_iter(buffer, &iter);
7789 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7790 gtk_text_buffer_place_cursor(buffer, &iter);
7793 /* process the other fields */
7795 compose_template_apply_fields(compose, tmpl);
7796 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7797 quote_fmt_reset_vartable();
7798 compose_changed_cb(NULL, compose);
7801 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7803 MsgInfo* dummyinfo = NULL;
7804 MsgInfo *msginfo = NULL;
7807 if (compose->replyinfo != NULL)
7808 msginfo = compose->replyinfo;
7809 else if (compose->fwdinfo != NULL)
7810 msginfo = compose->fwdinfo;
7812 dummyinfo = compose_msginfo_new_from_compose(compose);
7813 msginfo = dummyinfo;
7816 if (tmpl->from && *tmpl->from != '\0') {
7818 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7819 compose->gtkaspell);
7821 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7823 quote_fmt_scan_string(tmpl->from);
7826 buf = quote_fmt_get_buffer();
7828 alertpanel_error(_("Template From format error."));
7830 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
7834 if (tmpl->to && *tmpl->to != '\0') {
7836 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7837 compose->gtkaspell);
7839 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7841 quote_fmt_scan_string(tmpl->to);
7844 buf = quote_fmt_get_buffer();
7846 alertpanel_error(_("Template To format error."));
7848 compose_entry_append(compose, buf, COMPOSE_TO);
7852 if (tmpl->cc && *tmpl->cc != '\0') {
7854 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7855 compose->gtkaspell);
7857 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7859 quote_fmt_scan_string(tmpl->cc);
7862 buf = quote_fmt_get_buffer();
7864 alertpanel_error(_("Template Cc format error."));
7866 compose_entry_append(compose, buf, COMPOSE_CC);
7870 if (tmpl->bcc && *tmpl->bcc != '\0') {
7872 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7873 compose->gtkaspell);
7875 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7877 quote_fmt_scan_string(tmpl->bcc);
7880 buf = quote_fmt_get_buffer();
7882 alertpanel_error(_("Template Bcc format error."));
7884 compose_entry_append(compose, buf, COMPOSE_BCC);
7888 /* process the subject */
7889 if (tmpl->subject && *tmpl->subject != '\0') {
7891 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7892 compose->gtkaspell);
7894 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7896 quote_fmt_scan_string(tmpl->subject);
7899 buf = quote_fmt_get_buffer();
7901 alertpanel_error(_("Template subject format error."));
7903 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
7907 procmsg_msginfo_free( dummyinfo );
7910 static void compose_destroy(Compose *compose)
7912 GtkTextBuffer *buffer;
7913 GtkClipboard *clipboard;
7915 compose_list = g_list_remove(compose_list, compose);
7917 if (compose->updating) {
7918 debug_print("danger, not destroying anything now\n");
7919 compose->deferred_destroy = TRUE;
7922 /* NOTE: address_completion_end() does nothing with the window
7923 * however this may change. */
7924 address_completion_end(compose->window);
7926 slist_free_strings(compose->to_list);
7927 g_slist_free(compose->to_list);
7928 slist_free_strings(compose->newsgroup_list);
7929 g_slist_free(compose->newsgroup_list);
7930 slist_free_strings(compose->header_list);
7931 g_slist_free(compose->header_list);
7933 procmsg_msginfo_free(compose->targetinfo);
7934 procmsg_msginfo_free(compose->replyinfo);
7935 procmsg_msginfo_free(compose->fwdinfo);
7937 g_free(compose->replyto);
7938 g_free(compose->cc);
7939 g_free(compose->bcc);
7940 g_free(compose->newsgroups);
7941 g_free(compose->followup_to);
7943 g_free(compose->ml_post);
7945 g_free(compose->inreplyto);
7946 g_free(compose->references);
7947 g_free(compose->msgid);
7948 g_free(compose->boundary);
7950 g_free(compose->redirect_filename);
7951 if (compose->undostruct)
7952 undo_destroy(compose->undostruct);
7954 g_free(compose->sig_str);
7956 g_free(compose->exteditor_file);
7958 g_free(compose->orig_charset);
7960 g_free(compose->privacy_system);
7962 if (addressbook_get_target_compose() == compose)
7963 addressbook_set_target_compose(NULL);
7966 if (compose->gtkaspell) {
7967 gtkaspell_delete(compose->gtkaspell);
7968 compose->gtkaspell = NULL;
7972 if (!compose->batch) {
7973 prefs_common.compose_width = compose->scrolledwin->allocation.width;
7974 prefs_common.compose_height = compose->window->allocation.height;
7977 if (!gtk_widget_get_parent(compose->paned))
7978 gtk_widget_destroy(compose->paned);
7979 gtk_widget_destroy(compose->popupmenu);
7981 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
7982 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7983 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
7985 gtk_widget_destroy(compose->window);
7986 toolbar_destroy(compose->toolbar);
7987 g_free(compose->toolbar);
7988 g_mutex_free(compose->mutex);
7992 static void compose_attach_info_free(AttachInfo *ainfo)
7994 g_free(ainfo->file);
7995 g_free(ainfo->content_type);
7996 g_free(ainfo->name);
8000 static void compose_attach_update_label(Compose *compose)
8005 GtkTreeModel *model;
8010 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8011 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8012 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8016 while(gtk_tree_model_iter_next(model, &iter))
8019 text = g_strdup_printf("(%d)", i);
8020 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8024 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8026 Compose *compose = (Compose *)data;
8027 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8028 GtkTreeSelection *selection;
8030 GtkTreeModel *model;
8032 selection = gtk_tree_view_get_selection(tree_view);
8033 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8038 for (cur = sel; cur != NULL; cur = cur->next) {
8039 GtkTreePath *path = cur->data;
8040 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8043 gtk_tree_path_free(path);
8046 for (cur = sel; cur != NULL; cur = cur->next) {
8047 GtkTreeRowReference *ref = cur->data;
8048 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8051 if (gtk_tree_model_get_iter(model, &iter, path))
8052 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8054 gtk_tree_path_free(path);
8055 gtk_tree_row_reference_free(ref);
8059 compose_attach_update_label(compose);
8062 static struct _AttachProperty
8065 GtkWidget *mimetype_entry;
8066 GtkWidget *encoding_optmenu;
8067 GtkWidget *path_entry;
8068 GtkWidget *filename_entry;
8070 GtkWidget *cancel_btn;
8073 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8075 gtk_tree_path_free((GtkTreePath *)ptr);
8078 static void compose_attach_property(GtkAction *action, gpointer data)
8080 Compose *compose = (Compose *)data;
8081 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8083 GtkComboBox *optmenu;
8084 GtkTreeSelection *selection;
8086 GtkTreeModel *model;
8089 static gboolean cancelled;
8091 /* only if one selected */
8092 selection = gtk_tree_view_get_selection(tree_view);
8093 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8096 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8100 path = (GtkTreePath *) sel->data;
8101 gtk_tree_model_get_iter(model, &iter, path);
8102 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8105 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8111 if (!attach_prop.window)
8112 compose_attach_property_create(&cancelled);
8113 gtk_widget_grab_focus(attach_prop.ok_btn);
8114 gtk_widget_show(attach_prop.window);
8115 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8117 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8118 if (ainfo->encoding == ENC_UNKNOWN)
8119 combobox_select_by_data(optmenu, ENC_BASE64);
8121 combobox_select_by_data(optmenu, ainfo->encoding);
8123 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8124 ainfo->content_type ? ainfo->content_type : "");
8125 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8126 ainfo->file ? ainfo->file : "");
8127 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8128 ainfo->name ? ainfo->name : "");
8131 const gchar *entry_text;
8133 gchar *cnttype = NULL;
8140 gtk_widget_hide(attach_prop.window);
8145 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8146 if (*entry_text != '\0') {
8149 text = g_strstrip(g_strdup(entry_text));
8150 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8151 cnttype = g_strdup(text);
8154 alertpanel_error(_("Invalid MIME type."));
8160 ainfo->encoding = combobox_get_active_data(optmenu);
8162 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8163 if (*entry_text != '\0') {
8164 if (is_file_exist(entry_text) &&
8165 (size = get_file_size(entry_text)) > 0)
8166 file = g_strdup(entry_text);
8169 (_("File doesn't exist or is empty."));
8175 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8176 if (*entry_text != '\0') {
8177 g_free(ainfo->name);
8178 ainfo->name = g_strdup(entry_text);
8182 g_free(ainfo->content_type);
8183 ainfo->content_type = cnttype;
8186 g_free(ainfo->file);
8190 ainfo->size = (goffset)size;
8192 /* update tree store */
8193 text = to_human_readable(ainfo->size);
8194 gtk_tree_model_get_iter(model, &iter, path);
8195 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8196 COL_MIMETYPE, ainfo->content_type,
8198 COL_NAME, ainfo->name,
8204 gtk_tree_path_free(path);
8207 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8209 label = gtk_label_new(str); \
8210 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8211 GTK_FILL, 0, 0, 0); \
8212 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8214 entry = gtk_entry_new(); \
8215 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8216 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8219 static void compose_attach_property_create(gboolean *cancelled)
8225 GtkWidget *mimetype_entry;
8228 GtkListStore *optmenu_menu;
8229 GtkWidget *path_entry;
8230 GtkWidget *filename_entry;
8233 GtkWidget *cancel_btn;
8234 GList *mime_type_list, *strlist;
8237 debug_print("Creating attach_property window...\n");
8239 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8240 gtk_widget_set_size_request(window, 480, -1);
8241 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8242 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8243 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8244 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8245 g_signal_connect(G_OBJECT(window), "delete_event",
8246 G_CALLBACK(attach_property_delete_event),
8248 g_signal_connect(G_OBJECT(window), "key_press_event",
8249 G_CALLBACK(attach_property_key_pressed),
8252 vbox = gtk_vbox_new(FALSE, 8);
8253 gtk_container_add(GTK_CONTAINER(window), vbox);
8255 table = gtk_table_new(4, 2, FALSE);
8256 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8257 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8258 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8260 label = gtk_label_new(_("MIME type"));
8261 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8263 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8264 mimetype_entry = gtk_combo_box_entry_new_text();
8265 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8266 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8268 /* stuff with list */
8269 mime_type_list = procmime_get_mime_type_list();
8271 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8272 MimeType *type = (MimeType *) mime_type_list->data;
8275 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8277 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8280 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8281 (GCompareFunc)strcmp2);
8284 for (mime_type_list = strlist; mime_type_list != NULL;
8285 mime_type_list = mime_type_list->next) {
8286 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8287 g_free(mime_type_list->data);
8289 g_list_free(strlist);
8290 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8291 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8293 label = gtk_label_new(_("Encoding"));
8294 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8296 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8298 hbox = gtk_hbox_new(FALSE, 0);
8299 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8300 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8302 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8303 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8305 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8306 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8307 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8308 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8309 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8311 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8313 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8314 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8316 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8317 &ok_btn, GTK_STOCK_OK,
8319 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8320 gtk_widget_grab_default(ok_btn);
8322 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8323 G_CALLBACK(attach_property_ok),
8325 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8326 G_CALLBACK(attach_property_cancel),
8329 gtk_widget_show_all(vbox);
8331 attach_prop.window = window;
8332 attach_prop.mimetype_entry = mimetype_entry;
8333 attach_prop.encoding_optmenu = optmenu;
8334 attach_prop.path_entry = path_entry;
8335 attach_prop.filename_entry = filename_entry;
8336 attach_prop.ok_btn = ok_btn;
8337 attach_prop.cancel_btn = cancel_btn;
8340 #undef SET_LABEL_AND_ENTRY
8342 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8348 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8354 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8355 gboolean *cancelled)
8363 static gboolean attach_property_key_pressed(GtkWidget *widget,
8365 gboolean *cancelled)
8367 if (event && event->keyval == GDK_Escape) {
8374 static void compose_exec_ext_editor(Compose *compose)
8381 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8382 G_DIR_SEPARATOR, compose);
8384 if (pipe(pipe_fds) < 0) {
8390 if ((pid = fork()) < 0) {
8397 /* close the write side of the pipe */
8400 compose->exteditor_file = g_strdup(tmp);
8401 compose->exteditor_pid = pid;
8403 compose_set_ext_editor_sensitive(compose, FALSE);
8405 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8406 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8410 } else { /* process-monitoring process */
8416 /* close the read side of the pipe */
8419 if (compose_write_body_to_file(compose, tmp) < 0) {
8420 fd_write_all(pipe_fds[1], "2\n", 2);
8424 pid_ed = compose_exec_ext_editor_real(tmp);
8426 fd_write_all(pipe_fds[1], "1\n", 2);
8430 /* wait until editor is terminated */
8431 waitpid(pid_ed, NULL, 0);
8433 fd_write_all(pipe_fds[1], "0\n", 2);
8440 #endif /* G_OS_UNIX */
8444 static gint compose_exec_ext_editor_real(const gchar *file)
8451 g_return_val_if_fail(file != NULL, -1);
8453 if ((pid = fork()) < 0) {
8458 if (pid != 0) return pid;
8460 /* grandchild process */
8462 if (setpgid(0, getppid()))
8465 if (prefs_common_get_ext_editor_cmd() &&
8466 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8467 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8468 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8470 if (prefs_common_get_ext_editor_cmd())
8471 g_warning("External editor command line is invalid: '%s'\n",
8472 prefs_common_get_ext_editor_cmd());
8473 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8476 cmdline = strsplit_with_quote(buf, " ", 1024);
8477 execvp(cmdline[0], cmdline);
8480 g_strfreev(cmdline);
8485 static gboolean compose_ext_editor_kill(Compose *compose)
8487 pid_t pgid = compose->exteditor_pid * -1;
8490 ret = kill(pgid, 0);
8492 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8496 msg = g_strdup_printf
8497 (_("The external editor is still working.\n"
8498 "Force terminating the process?\n"
8499 "process group id: %d"), -pgid);
8500 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8501 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8505 if (val == G_ALERTALTERNATE) {
8506 g_source_remove(compose->exteditor_tag);
8507 g_io_channel_shutdown(compose->exteditor_ch,
8509 g_io_channel_unref(compose->exteditor_ch);
8511 if (kill(pgid, SIGTERM) < 0) perror("kill");
8512 waitpid(compose->exteditor_pid, NULL, 0);
8514 g_warning("Terminated process group id: %d", -pgid);
8515 g_warning("Temporary file: %s",
8516 compose->exteditor_file);
8518 compose_set_ext_editor_sensitive(compose, TRUE);
8520 g_free(compose->exteditor_file);
8521 compose->exteditor_file = NULL;
8522 compose->exteditor_pid = -1;
8523 compose->exteditor_ch = NULL;
8524 compose->exteditor_tag = -1;
8532 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8536 Compose *compose = (Compose *)data;
8539 debug_print(_("Compose: input from monitoring process\n"));
8541 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8543 g_io_channel_shutdown(source, FALSE, NULL);
8544 g_io_channel_unref(source);
8546 waitpid(compose->exteditor_pid, NULL, 0);
8548 if (buf[0] == '0') { /* success */
8549 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8550 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8552 gtk_text_buffer_set_text(buffer, "", -1);
8553 compose_insert_file(compose, compose->exteditor_file);
8554 compose_changed_cb(NULL, compose);
8556 if (claws_unlink(compose->exteditor_file) < 0)
8557 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8558 } else if (buf[0] == '1') { /* failed */
8559 g_warning("Couldn't exec external editor\n");
8560 if (claws_unlink(compose->exteditor_file) < 0)
8561 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8562 } else if (buf[0] == '2') {
8563 g_warning("Couldn't write to file\n");
8564 } else if (buf[0] == '3') {
8565 g_warning("Pipe read failed\n");
8568 compose_set_ext_editor_sensitive(compose, TRUE);
8570 g_free(compose->exteditor_file);
8571 compose->exteditor_file = NULL;
8572 compose->exteditor_pid = -1;
8573 compose->exteditor_ch = NULL;
8574 compose->exteditor_tag = -1;
8579 static void compose_set_ext_editor_sensitive(Compose *compose,
8582 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8583 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8584 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8585 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8586 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8587 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8588 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8590 gtk_widget_set_sensitive(compose->text, sensitive);
8591 if (compose->toolbar->send_btn)
8592 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8593 if (compose->toolbar->sendl_btn)
8594 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8595 if (compose->toolbar->draft_btn)
8596 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8597 if (compose->toolbar->insert_btn)
8598 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8599 if (compose->toolbar->sig_btn)
8600 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8601 if (compose->toolbar->exteditor_btn)
8602 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8603 if (compose->toolbar->linewrap_current_btn)
8604 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8605 if (compose->toolbar->linewrap_all_btn)
8606 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8608 #endif /* G_OS_UNIX */
8611 * compose_undo_state_changed:
8613 * Change the sensivity of the menuentries undo and redo
8615 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8616 gint redo_state, gpointer data)
8618 Compose *compose = (Compose *)data;
8620 switch (undo_state) {
8621 case UNDO_STATE_TRUE:
8622 if (!undostruct->undo_state) {
8623 undostruct->undo_state = TRUE;
8624 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8627 case UNDO_STATE_FALSE:
8628 if (undostruct->undo_state) {
8629 undostruct->undo_state = FALSE;
8630 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8633 case UNDO_STATE_UNCHANGED:
8635 case UNDO_STATE_REFRESH:
8636 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8639 g_warning("Undo state not recognized");
8643 switch (redo_state) {
8644 case UNDO_STATE_TRUE:
8645 if (!undostruct->redo_state) {
8646 undostruct->redo_state = TRUE;
8647 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8650 case UNDO_STATE_FALSE:
8651 if (undostruct->redo_state) {
8652 undostruct->redo_state = FALSE;
8653 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8656 case UNDO_STATE_UNCHANGED:
8658 case UNDO_STATE_REFRESH:
8659 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8662 g_warning("Redo state not recognized");
8667 /* callback functions */
8669 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8670 * includes "non-client" (windows-izm) in calculation, so this calculation
8671 * may not be accurate.
8673 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8674 GtkAllocation *allocation,
8675 GtkSHRuler *shruler)
8677 if (prefs_common.show_ruler) {
8678 gint char_width = 0, char_height = 0;
8679 gint line_width_in_chars;
8681 gtkut_get_font_size(GTK_WIDGET(widget),
8682 &char_width, &char_height);
8683 line_width_in_chars =
8684 (allocation->width - allocation->x) / char_width;
8686 /* got the maximum */
8687 gtk_ruler_set_range(GTK_RULER(shruler),
8688 0.0, line_width_in_chars, 0,
8689 /*line_width_in_chars*/ char_width);
8695 static void account_activated(GtkComboBox *optmenu, gpointer data)
8697 Compose *compose = (Compose *)data;
8700 gchar *folderidentifier;
8701 gint account_id = 0;
8705 /* Get ID of active account in the combo box */
8706 menu = gtk_combo_box_get_model(optmenu);
8707 gtk_combo_box_get_active_iter(optmenu, &iter);
8708 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8710 ac = account_find_from_id(account_id);
8711 g_return_if_fail(ac != NULL);
8713 if (ac != compose->account)
8714 compose_select_account(compose, ac, FALSE);
8716 /* Set message save folder */
8717 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8718 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8720 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8721 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8723 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8724 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8725 folderidentifier = folder_item_get_identifier(account_get_special_folder
8726 (compose->account, F_OUTBOX));
8727 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
8728 g_free(folderidentifier);
8732 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8733 GtkTreeViewColumn *column, Compose *compose)
8735 compose_attach_property(NULL, compose);
8738 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8741 Compose *compose = (Compose *)data;
8742 GtkTreeSelection *attach_selection;
8743 gint attach_nr_selected;
8745 if (!event) return FALSE;
8747 if (event->button == 3) {
8748 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8749 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8751 if (attach_nr_selected > 0)
8753 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8754 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8756 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8757 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8760 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8761 NULL, NULL, event->button, event->time);
8768 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8771 Compose *compose = (Compose *)data;
8773 if (!event) return FALSE;
8775 switch (event->keyval) {
8777 compose_attach_remove_selected(NULL, compose);
8783 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8785 toolbar_comp_set_sensitive(compose, allow);
8786 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8787 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8789 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8791 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8792 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8793 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8795 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8799 static void compose_send_cb(GtkAction *action, gpointer data)
8801 Compose *compose = (Compose *)data;
8803 if (prefs_common.work_offline &&
8804 !inc_offline_should_override(TRUE,
8805 _("Claws Mail needs network access in order "
8806 "to send this email.")))
8809 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8810 g_source_remove(compose->draft_timeout_tag);
8811 compose->draft_timeout_tag = -1;
8814 compose_send(compose);
8817 static void compose_send_later_cb(GtkAction *action, gpointer data)
8819 Compose *compose = (Compose *)data;
8823 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8827 compose_close(compose);
8828 } else if (val == -1) {
8829 alertpanel_error(_("Could not queue message."));
8830 } else if (val == -2) {
8831 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8832 } else if (val == -3) {
8833 if (privacy_peek_error())
8834 alertpanel_error(_("Could not queue message for sending:\n\n"
8835 "Signature failed: %s"), privacy_get_error());
8836 } else if (val == -4) {
8837 alertpanel_error(_("Could not queue message for sending:\n\n"
8838 "Charset conversion failed."));
8839 } else if (val == -5) {
8840 alertpanel_error(_("Could not queue message for sending:\n\n"
8841 "Couldn't get recipient encryption key."));
8842 } else if (val == -6) {
8845 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
8848 #define DRAFTED_AT_EXIT "drafted_at_exit"
8849 static void compose_register_draft(MsgInfo *info)
8851 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8852 DRAFTED_AT_EXIT, NULL);
8853 FILE *fp = fopen(filepath, "ab");
8856 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
8864 gboolean compose_draft (gpointer data, guint action)
8866 Compose *compose = (Compose *)data;
8870 MsgFlags flag = {0, 0};
8871 static gboolean lock = FALSE;
8872 MsgInfo *newmsginfo;
8874 gboolean target_locked = FALSE;
8875 gboolean err = FALSE;
8877 if (lock) return FALSE;
8879 if (compose->sending)
8882 draft = account_get_special_folder(compose->account, F_DRAFT);
8883 g_return_val_if_fail(draft != NULL, FALSE);
8885 if (!g_mutex_trylock(compose->mutex)) {
8886 /* we don't want to lock the mutex once it's available,
8887 * because as the only other part of compose.c locking
8888 * it is compose_close - which means once unlocked,
8889 * the compose struct will be freed */
8890 debug_print("couldn't lock mutex, probably sending\n");
8896 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
8897 G_DIR_SEPARATOR, compose);
8898 if ((fp = g_fopen(tmp, "wb")) == NULL) {
8899 FILE_OP_ERROR(tmp, "fopen");
8903 /* chmod for security */
8904 if (change_file_mode_rw(fp, tmp) < 0) {
8905 FILE_OP_ERROR(tmp, "chmod");
8906 g_warning("can't change file mode\n");
8909 /* Save draft infos */
8910 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
8911 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
8913 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
8914 gchar *savefolderid;
8916 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8917 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
8918 g_free(savefolderid);
8920 if (compose->return_receipt) {
8921 err |= (fprintf(fp, "RRCPT:1\n") < 0);
8923 if (compose->privacy_system) {
8924 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
8925 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
8926 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
8929 /* Message-ID of message replying to */
8930 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
8933 folderid = folder_item_get_identifier(compose->replyinfo->folder);
8934 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
8937 /* Message-ID of message forwarding to */
8938 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
8941 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
8942 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
8946 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
8947 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
8949 /* end of headers */
8950 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
8957 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
8961 if (fclose(fp) == EOF) {
8965 if (compose->targetinfo) {
8966 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
8967 flag.perm_flags = target_locked?MSG_LOCKED:0;
8969 flag.tmp_flags = MSG_DRAFT;
8971 folder_item_scan(draft);
8972 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
8973 MsgInfo *tmpinfo = NULL;
8974 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
8975 if (compose->msgid) {
8976 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
8979 msgnum = tmpinfo->msgnum;
8980 procmsg_msginfo_free(tmpinfo);
8981 debug_print("got draft msgnum %d from scanning\n", msgnum);
8983 debug_print("didn't get draft msgnum after scanning\n");
8986 debug_print("got draft msgnum %d from adding\n", msgnum);
8992 if (action != COMPOSE_AUTO_SAVE) {
8993 if (action != COMPOSE_DRAFT_FOR_EXIT)
8994 alertpanel_error(_("Could not save draft."));
8997 gtkut_window_popup(compose->window);
8998 val = alertpanel_full(_("Could not save draft"),
8999 _("Could not save draft.\n"
9000 "Do you want to cancel exit or discard this email?"),
9001 _("_Cancel exit"), _("_Discard email"), NULL,
9002 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9003 if (val == G_ALERTALTERNATE) {
9005 g_mutex_unlock(compose->mutex); /* must be done before closing */
9006 compose_close(compose);
9010 g_mutex_unlock(compose->mutex); /* must be done before closing */
9019 if (compose->mode == COMPOSE_REEDIT) {
9020 compose_remove_reedit_target(compose, TRUE);
9023 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9026 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9028 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9030 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9031 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9032 procmsg_msginfo_set_flags(newmsginfo, 0,
9033 MSG_HAS_ATTACHMENT);
9035 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9036 compose_register_draft(newmsginfo);
9038 procmsg_msginfo_free(newmsginfo);
9041 folder_item_scan(draft);
9043 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9045 g_mutex_unlock(compose->mutex); /* must be done before closing */
9046 compose_close(compose);
9052 path = folder_item_fetch_msg(draft, msgnum);
9054 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9057 if (g_stat(path, &s) < 0) {
9058 FILE_OP_ERROR(path, "stat");
9064 procmsg_msginfo_free(compose->targetinfo);
9065 compose->targetinfo = procmsg_msginfo_new();
9066 compose->targetinfo->msgnum = msgnum;
9067 compose->targetinfo->size = (goffset)s.st_size;
9068 compose->targetinfo->mtime = s.st_mtime;
9069 compose->targetinfo->folder = draft;
9071 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9072 compose->mode = COMPOSE_REEDIT;
9074 if (action == COMPOSE_AUTO_SAVE) {
9075 compose->autosaved_draft = compose->targetinfo;
9077 compose->modified = FALSE;
9078 compose_set_title(compose);
9082 g_mutex_unlock(compose->mutex);
9086 void compose_clear_exit_drafts(void)
9088 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9089 DRAFTED_AT_EXIT, NULL);
9090 if (is_file_exist(filepath))
9091 claws_unlink(filepath);
9096 void compose_reopen_exit_drafts(void)
9098 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9099 DRAFTED_AT_EXIT, NULL);
9100 FILE *fp = fopen(filepath, "rb");
9104 while (fgets(buf, sizeof(buf), fp)) {
9105 gchar **parts = g_strsplit(buf, "\t", 2);
9106 const gchar *folder = parts[0];
9107 int msgnum = parts[1] ? atoi(parts[1]):-1;
9109 if (folder && *folder && msgnum > -1) {
9110 FolderItem *item = folder_find_item_from_identifier(folder);
9111 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9113 compose_reedit(info, FALSE);
9120 compose_clear_exit_drafts();
9123 static void compose_save_cb(GtkAction *action, gpointer data)
9125 compose_draft(data, COMPOSE_KEEP_EDITING);
9128 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9130 if (compose && file_list) {
9133 for ( tmp = file_list; tmp; tmp = tmp->next) {
9134 gchar *file = (gchar *) tmp->data;
9135 gchar *utf8_filename = conv_filename_to_utf8(file);
9136 compose_attach_append(compose, file, utf8_filename, NULL);
9137 compose_changed_cb(NULL, compose);
9142 g_free(utf8_filename);
9147 static void compose_attach_cb(GtkAction *action, gpointer data)
9149 Compose *compose = (Compose *)data;
9152 if (compose->redirect_filename != NULL)
9155 file_list = filesel_select_multiple_files_open(_("Select file"));
9158 compose_attach_from_list(compose, file_list, TRUE);
9159 g_list_free(file_list);
9163 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9165 Compose *compose = (Compose *)data;
9168 file_list = filesel_select_multiple_files_open(_("Select file"));
9173 for ( tmp = file_list; tmp; tmp = tmp->next) {
9174 gchar *file = (gchar *) tmp->data;
9175 gchar *filedup = g_strdup(file);
9176 gchar *shortfile = g_path_get_basename(filedup);
9177 ComposeInsertResult res;
9179 res = compose_insert_file(compose, file);
9180 if (res == COMPOSE_INSERT_READ_ERROR) {
9181 alertpanel_error(_("File '%s' could not be read."), shortfile);
9182 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9183 alertpanel_error(_("File '%s' contained invalid characters\n"
9184 "for the current encoding, insertion may be incorrect."), shortfile);
9190 g_list_free(file_list);
9194 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9196 Compose *compose = (Compose *)data;
9198 compose_insert_sig(compose, FALSE);
9201 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9205 Compose *compose = (Compose *)data;
9207 gtkut_widget_get_uposition(widget, &x, &y);
9208 if (!compose->batch) {
9209 prefs_common.compose_x = x;
9210 prefs_common.compose_y = y;
9212 if (compose->sending || compose->updating)
9214 compose_close_cb(NULL, compose);
9218 void compose_close_toolbar(Compose *compose)
9220 compose_close_cb(NULL, compose);
9223 static void compose_close_cb(GtkAction *action, gpointer data)
9225 Compose *compose = (Compose *)data;
9229 if (compose->exteditor_tag != -1) {
9230 if (!compose_ext_editor_kill(compose))
9235 if (compose->modified) {
9236 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9237 if (!g_mutex_trylock(compose->mutex)) {
9238 /* we don't want to lock the mutex once it's available,
9239 * because as the only other part of compose.c locking
9240 * it is compose_close - which means once unlocked,
9241 * the compose struct will be freed */
9242 debug_print("couldn't lock mutex, probably sending\n");
9246 val = alertpanel(_("Discard message"),
9247 _("This message has been modified. Discard it?"),
9248 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9250 val = alertpanel(_("Save changes"),
9251 _("This message has been modified. Save the latest changes?"),
9252 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9254 g_mutex_unlock(compose->mutex);
9256 case G_ALERTDEFAULT:
9257 if (prefs_common.autosave && !reedit)
9258 compose_remove_draft(compose);
9260 case G_ALERTALTERNATE:
9261 compose_draft(data, COMPOSE_QUIT_EDITING);
9268 compose_close(compose);
9271 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9273 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9274 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9275 Compose *compose = (Compose *) data;
9278 compose->out_encoding = (CharSet)value;
9281 static void compose_address_cb(GtkAction *action, gpointer data)
9283 Compose *compose = (Compose *)data;
9285 addressbook_open(compose);
9288 static void about_show_cb(GtkAction *action, gpointer data)
9293 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9295 Compose *compose = (Compose *)data;
9300 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9301 g_return_if_fail(tmpl != NULL);
9303 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9305 val = alertpanel(_("Apply template"), msg,
9306 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9309 if (val == G_ALERTDEFAULT)
9310 compose_template_apply(compose, tmpl, TRUE);
9311 else if (val == G_ALERTALTERNATE)
9312 compose_template_apply(compose, tmpl, FALSE);
9315 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9317 Compose *compose = (Compose *)data;
9319 compose_exec_ext_editor(compose);
9322 static void compose_undo_cb(GtkAction *action, gpointer data)
9324 Compose *compose = (Compose *)data;
9325 gboolean prev_autowrap = compose->autowrap;
9327 compose->autowrap = FALSE;
9328 undo_undo(compose->undostruct);
9329 compose->autowrap = prev_autowrap;
9332 static void compose_redo_cb(GtkAction *action, gpointer data)
9334 Compose *compose = (Compose *)data;
9335 gboolean prev_autowrap = compose->autowrap;
9337 compose->autowrap = FALSE;
9338 undo_redo(compose->undostruct);
9339 compose->autowrap = prev_autowrap;
9342 static void entry_cut_clipboard(GtkWidget *entry)
9344 if (GTK_IS_EDITABLE(entry))
9345 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9346 else if (GTK_IS_TEXT_VIEW(entry))
9347 gtk_text_buffer_cut_clipboard(
9348 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9349 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9353 static void entry_copy_clipboard(GtkWidget *entry)
9355 if (GTK_IS_EDITABLE(entry))
9356 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9357 else if (GTK_IS_TEXT_VIEW(entry))
9358 gtk_text_buffer_copy_clipboard(
9359 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9360 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9363 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9364 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9366 if (GTK_IS_TEXT_VIEW(entry)) {
9367 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9368 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9369 GtkTextIter start_iter, end_iter;
9371 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9373 if (contents == NULL)
9376 /* we shouldn't delete the selection when middle-click-pasting, or we
9377 * can't mid-click-paste our own selection */
9378 if (clip != GDK_SELECTION_PRIMARY) {
9379 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9380 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9383 if (insert_place == NULL) {
9384 /* if insert_place isn't specified, insert at the cursor.
9385 * used for Ctrl-V pasting */
9386 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9387 start = gtk_text_iter_get_offset(&start_iter);
9388 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9390 /* if insert_place is specified, paste here.
9391 * used for mid-click-pasting */
9392 start = gtk_text_iter_get_offset(insert_place);
9393 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9397 /* paste unwrapped: mark the paste so it's not wrapped later */
9398 end = start + strlen(contents);
9399 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9400 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9401 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9402 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9403 /* rewrap paragraph now (after a mid-click-paste) */
9404 mark_start = gtk_text_buffer_get_insert(buffer);
9405 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9406 gtk_text_iter_backward_char(&start_iter);
9407 compose_beautify_paragraph(compose, &start_iter, TRUE);
9409 } else if (GTK_IS_EDITABLE(entry))
9410 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9412 compose->modified = TRUE;
9415 static void entry_allsel(GtkWidget *entry)
9417 if (GTK_IS_EDITABLE(entry))
9418 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9419 else if (GTK_IS_TEXT_VIEW(entry)) {
9420 GtkTextIter startiter, enditer;
9421 GtkTextBuffer *textbuf;
9423 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9424 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9425 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9427 gtk_text_buffer_move_mark_by_name(textbuf,
9428 "selection_bound", &startiter);
9429 gtk_text_buffer_move_mark_by_name(textbuf,
9430 "insert", &enditer);
9434 static void compose_cut_cb(GtkAction *action, gpointer data)
9436 Compose *compose = (Compose *)data;
9437 if (compose->focused_editable
9438 #ifndef GENERIC_UMPC
9439 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9442 entry_cut_clipboard(compose->focused_editable);
9445 static void compose_copy_cb(GtkAction *action, gpointer data)
9447 Compose *compose = (Compose *)data;
9448 if (compose->focused_editable
9449 #ifndef GENERIC_UMPC
9450 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9453 entry_copy_clipboard(compose->focused_editable);
9456 static void compose_paste_cb(GtkAction *action, gpointer data)
9458 Compose *compose = (Compose *)data;
9460 GtkTextBuffer *buffer;
9462 if (compose->focused_editable &&
9463 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9464 entry_paste_clipboard(compose, compose->focused_editable,
9465 prefs_common.linewrap_pastes,
9466 GDK_SELECTION_CLIPBOARD, NULL);
9470 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9472 Compose *compose = (Compose *)data;
9473 gint wrap_quote = prefs_common.linewrap_quote;
9474 if (compose->focused_editable
9475 #ifndef GENERIC_UMPC
9476 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9479 /* let text_insert() (called directly or at a later time
9480 * after the gtk_editable_paste_clipboard) know that
9481 * text is to be inserted as a quotation. implemented
9482 * by using a simple refcount... */
9483 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9484 G_OBJECT(compose->focused_editable),
9485 "paste_as_quotation"));
9486 g_object_set_data(G_OBJECT(compose->focused_editable),
9487 "paste_as_quotation",
9488 GINT_TO_POINTER(paste_as_quotation + 1));
9489 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9490 entry_paste_clipboard(compose, compose->focused_editable,
9491 prefs_common.linewrap_pastes,
9492 GDK_SELECTION_CLIPBOARD, NULL);
9493 prefs_common.linewrap_quote = wrap_quote;
9497 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9499 Compose *compose = (Compose *)data;
9501 GtkTextBuffer *buffer;
9503 if (compose->focused_editable
9504 #ifndef GENERIC_UMPC
9505 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9508 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9509 GDK_SELECTION_CLIPBOARD, NULL);
9513 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9515 Compose *compose = (Compose *)data;
9517 GtkTextBuffer *buffer;
9519 if (compose->focused_editable
9520 #ifndef GENERIC_UMPC
9521 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9524 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9525 GDK_SELECTION_CLIPBOARD, NULL);
9529 static void compose_allsel_cb(GtkAction *action, gpointer data)
9531 Compose *compose = (Compose *)data;
9532 if (compose->focused_editable
9533 #ifndef GENERIC_UMPC
9534 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9537 entry_allsel(compose->focused_editable);
9540 static void textview_move_beginning_of_line (GtkTextView *text)
9542 GtkTextBuffer *buffer;
9546 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9548 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9549 mark = gtk_text_buffer_get_insert(buffer);
9550 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9551 gtk_text_iter_set_line_offset(&ins, 0);
9552 gtk_text_buffer_place_cursor(buffer, &ins);
9555 static void textview_move_forward_character (GtkTextView *text)
9557 GtkTextBuffer *buffer;
9561 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9563 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9564 mark = gtk_text_buffer_get_insert(buffer);
9565 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9566 if (gtk_text_iter_forward_cursor_position(&ins))
9567 gtk_text_buffer_place_cursor(buffer, &ins);
9570 static void textview_move_backward_character (GtkTextView *text)
9572 GtkTextBuffer *buffer;
9576 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9578 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9579 mark = gtk_text_buffer_get_insert(buffer);
9580 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9581 if (gtk_text_iter_backward_cursor_position(&ins))
9582 gtk_text_buffer_place_cursor(buffer, &ins);
9585 static void textview_move_forward_word (GtkTextView *text)
9587 GtkTextBuffer *buffer;
9592 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9594 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9595 mark = gtk_text_buffer_get_insert(buffer);
9596 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9597 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9598 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9599 gtk_text_iter_backward_word_start(&ins);
9600 gtk_text_buffer_place_cursor(buffer, &ins);
9604 static void textview_move_backward_word (GtkTextView *text)
9606 GtkTextBuffer *buffer;
9611 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9613 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9614 mark = gtk_text_buffer_get_insert(buffer);
9615 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9616 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9617 if (gtk_text_iter_backward_word_starts(&ins, 1))
9618 gtk_text_buffer_place_cursor(buffer, &ins);
9621 static void textview_move_end_of_line (GtkTextView *text)
9623 GtkTextBuffer *buffer;
9627 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9629 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9630 mark = gtk_text_buffer_get_insert(buffer);
9631 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9632 if (gtk_text_iter_forward_to_line_end(&ins))
9633 gtk_text_buffer_place_cursor(buffer, &ins);
9636 static void textview_move_next_line (GtkTextView *text)
9638 GtkTextBuffer *buffer;
9643 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9645 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9646 mark = gtk_text_buffer_get_insert(buffer);
9647 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9648 offset = gtk_text_iter_get_line_offset(&ins);
9649 if (gtk_text_iter_forward_line(&ins)) {
9650 gtk_text_iter_set_line_offset(&ins, offset);
9651 gtk_text_buffer_place_cursor(buffer, &ins);
9655 static void textview_move_previous_line (GtkTextView *text)
9657 GtkTextBuffer *buffer;
9662 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9664 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9665 mark = gtk_text_buffer_get_insert(buffer);
9666 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9667 offset = gtk_text_iter_get_line_offset(&ins);
9668 if (gtk_text_iter_backward_line(&ins)) {
9669 gtk_text_iter_set_line_offset(&ins, offset);
9670 gtk_text_buffer_place_cursor(buffer, &ins);
9674 static void textview_delete_forward_character (GtkTextView *text)
9676 GtkTextBuffer *buffer;
9678 GtkTextIter ins, end_iter;
9680 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9682 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9683 mark = gtk_text_buffer_get_insert(buffer);
9684 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9686 if (gtk_text_iter_forward_char(&end_iter)) {
9687 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9691 static void textview_delete_backward_character (GtkTextView *text)
9693 GtkTextBuffer *buffer;
9695 GtkTextIter ins, end_iter;
9697 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9699 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9700 mark = gtk_text_buffer_get_insert(buffer);
9701 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9703 if (gtk_text_iter_backward_char(&end_iter)) {
9704 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9708 static void textview_delete_forward_word (GtkTextView *text)
9710 GtkTextBuffer *buffer;
9712 GtkTextIter ins, end_iter;
9714 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9716 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9717 mark = gtk_text_buffer_get_insert(buffer);
9718 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9720 if (gtk_text_iter_forward_word_end(&end_iter)) {
9721 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9725 static void textview_delete_backward_word (GtkTextView *text)
9727 GtkTextBuffer *buffer;
9729 GtkTextIter ins, end_iter;
9731 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9733 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9734 mark = gtk_text_buffer_get_insert(buffer);
9735 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9737 if (gtk_text_iter_backward_word_start(&end_iter)) {
9738 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9742 static void textview_delete_line (GtkTextView *text)
9744 GtkTextBuffer *buffer;
9746 GtkTextIter ins, start_iter, end_iter;
9748 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9750 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9751 mark = gtk_text_buffer_get_insert(buffer);
9752 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9755 gtk_text_iter_set_line_offset(&start_iter, 0);
9758 if (gtk_text_iter_ends_line(&end_iter)){
9759 if (!gtk_text_iter_forward_char(&end_iter))
9760 gtk_text_iter_backward_char(&start_iter);
9763 gtk_text_iter_forward_to_line_end(&end_iter);
9764 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9767 static void textview_delete_to_line_end (GtkTextView *text)
9769 GtkTextBuffer *buffer;
9771 GtkTextIter ins, end_iter;
9773 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9775 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9776 mark = gtk_text_buffer_get_insert(buffer);
9777 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9779 if (gtk_text_iter_ends_line(&end_iter))
9780 gtk_text_iter_forward_char(&end_iter);
9782 gtk_text_iter_forward_to_line_end(&end_iter);
9783 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9786 #define DO_ACTION(name, act) { \
9787 if(!strcmp(name, a_name)) { \
9791 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
9793 const gchar *a_name = gtk_action_get_name(action);
9794 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
9795 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
9796 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
9797 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
9798 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
9799 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
9800 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
9801 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
9802 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
9803 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
9804 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
9805 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
9806 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
9807 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
9811 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
9813 Compose *compose = (Compose *)data;
9814 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9815 ComposeCallAdvancedAction action = -1;
9817 action = compose_call_advanced_action_from_path(gaction);
9820 void (*do_action) (GtkTextView *text);
9821 } action_table[] = {
9822 {textview_move_beginning_of_line},
9823 {textview_move_forward_character},
9824 {textview_move_backward_character},
9825 {textview_move_forward_word},
9826 {textview_move_backward_word},
9827 {textview_move_end_of_line},
9828 {textview_move_next_line},
9829 {textview_move_previous_line},
9830 {textview_delete_forward_character},
9831 {textview_delete_backward_character},
9832 {textview_delete_forward_word},
9833 {textview_delete_backward_word},
9834 {textview_delete_line},
9835 {textview_delete_to_line_end}
9838 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
9840 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
9841 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
9842 if (action_table[action].do_action)
9843 action_table[action].do_action(text);
9845 g_warning("Not implemented yet.");
9849 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
9853 if (GTK_IS_EDITABLE(widget)) {
9854 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
9855 gtk_editable_set_position(GTK_EDITABLE(widget),
9858 if (widget->parent && widget->parent->parent
9859 && widget->parent->parent->parent) {
9860 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
9861 gint y = widget->allocation.y;
9862 gint height = widget->allocation.height;
9863 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
9864 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
9866 if (y < (int)shown->value) {
9867 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
9869 if (y + height > (int)shown->value + (int)shown->page_size) {
9870 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
9871 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9872 y + height - (int)shown->page_size - 1);
9874 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9875 (int)shown->upper - (int)shown->page_size - 1);
9882 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
9883 compose->focused_editable = widget;
9886 if (GTK_IS_TEXT_VIEW(widget)
9887 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
9888 g_object_ref(compose->notebook);
9889 g_object_ref(compose->edit_vbox);
9890 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9891 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9892 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
9893 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
9894 g_object_unref(compose->notebook);
9895 g_object_unref(compose->edit_vbox);
9896 g_signal_handlers_block_by_func(G_OBJECT(widget),
9897 G_CALLBACK(compose_grab_focus_cb),
9899 gtk_widget_grab_focus(widget);
9900 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9901 G_CALLBACK(compose_grab_focus_cb),
9903 } else if (!GTK_IS_TEXT_VIEW(widget)
9904 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
9905 g_object_ref(compose->notebook);
9906 g_object_ref(compose->edit_vbox);
9907 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9908 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9909 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
9910 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
9911 g_object_unref(compose->notebook);
9912 g_object_unref(compose->edit_vbox);
9913 g_signal_handlers_block_by_func(G_OBJECT(widget),
9914 G_CALLBACK(compose_grab_focus_cb),
9916 gtk_widget_grab_focus(widget);
9917 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9918 G_CALLBACK(compose_grab_focus_cb),
9924 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
9926 compose->modified = TRUE;
9927 #ifndef GENERIC_UMPC
9928 compose_set_title(compose);
9932 static void compose_wrap_cb(GtkAction *action, gpointer data)
9934 Compose *compose = (Compose *)data;
9935 compose_beautify_paragraph(compose, NULL, TRUE);
9938 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
9940 Compose *compose = (Compose *)data;
9941 compose_wrap_all_full(compose, TRUE);
9944 static void compose_find_cb(GtkAction *action, gpointer data)
9946 Compose *compose = (Compose *)data;
9948 message_search_compose(compose);
9951 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
9954 Compose *compose = (Compose *)data;
9955 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9956 if (compose->autowrap)
9957 compose_wrap_all_full(compose, TRUE);
9958 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9961 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
9964 Compose *compose = (Compose *)data;
9965 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9968 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
9970 Compose *compose = (Compose *)data;
9972 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9975 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
9977 Compose *compose = (Compose *)data;
9979 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9982 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
9984 g_free(compose->privacy_system);
9986 compose->privacy_system = g_strdup(account->default_privacy_system);
9987 compose_update_privacy_system_menu_item(compose, warn);
9990 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
9992 Compose *compose = (Compose *)data;
9994 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
9995 gtk_widget_show(compose->ruler_hbox);
9996 prefs_common.show_ruler = TRUE;
9998 gtk_widget_hide(compose->ruler_hbox);
9999 gtk_widget_queue_resize(compose->edit_vbox);
10000 prefs_common.show_ruler = FALSE;
10004 static void compose_attach_drag_received_cb (GtkWidget *widget,
10005 GdkDragContext *context,
10008 GtkSelectionData *data,
10011 gpointer user_data)
10013 Compose *compose = (Compose *)user_data;
10016 if (gdk_atom_name(data->type) &&
10017 !strcmp(gdk_atom_name(data->type), "text/uri-list")
10018 && gtk_drag_get_source_widget(context) !=
10019 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10020 list = uri_list_extract_filenames((const gchar *)data->data);
10021 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10022 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10023 compose_attach_append
10024 (compose, (const gchar *)tmp->data,
10025 utf8_filename, NULL);
10026 g_free(utf8_filename);
10028 if (list) compose_changed_cb(NULL, compose);
10029 list_free_strings(list);
10031 } else if (gtk_drag_get_source_widget(context)
10032 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10033 /* comes from our summaryview */
10034 SummaryView * summaryview = NULL;
10035 GSList * list = NULL, *cur = NULL;
10037 if (mainwindow_get_mainwindow())
10038 summaryview = mainwindow_get_mainwindow()->summaryview;
10041 list = summary_get_selected_msg_list(summaryview);
10043 for (cur = list; cur; cur = cur->next) {
10044 MsgInfo *msginfo = (MsgInfo *)cur->data;
10045 gchar *file = NULL;
10047 file = procmsg_get_message_file_full(msginfo,
10050 compose_attach_append(compose, (const gchar *)file,
10051 (const gchar *)file, "message/rfc822");
10055 g_slist_free(list);
10059 static gboolean compose_drag_drop(GtkWidget *widget,
10060 GdkDragContext *drag_context,
10062 guint time, gpointer user_data)
10064 /* not handling this signal makes compose_insert_drag_received_cb
10069 static void compose_insert_drag_received_cb (GtkWidget *widget,
10070 GdkDragContext *drag_context,
10073 GtkSelectionData *data,
10076 gpointer user_data)
10078 Compose *compose = (Compose *)user_data;
10081 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10083 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10084 AlertValue val = G_ALERTDEFAULT;
10086 list = uri_list_extract_filenames((const gchar *)data->data);
10088 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10089 /* Assume a list of no files, and data has ://, is a remote link */
10090 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10091 gchar *tmpfile = get_tmp_file();
10092 str_write_to_file(tmpdata, tmpfile);
10094 compose_insert_file(compose, tmpfile);
10095 claws_unlink(tmpfile);
10097 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10098 compose_beautify_paragraph(compose, NULL, TRUE);
10101 switch (prefs_common.compose_dnd_mode) {
10102 case COMPOSE_DND_ASK:
10103 val = alertpanel_full(_("Insert or attach?"),
10104 _("Do you want to insert the contents of the file(s) "
10105 "into the message body, or attach it to the email?"),
10106 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10107 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10109 case COMPOSE_DND_INSERT:
10110 val = G_ALERTALTERNATE;
10112 case COMPOSE_DND_ATTACH:
10113 val = G_ALERTOTHER;
10116 /* unexpected case */
10117 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10120 if (val & G_ALERTDISABLE) {
10121 val &= ~G_ALERTDISABLE;
10122 /* remember what action to perform by default, only if we don't click Cancel */
10123 if (val == G_ALERTALTERNATE)
10124 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10125 else if (val == G_ALERTOTHER)
10126 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10129 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10130 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10131 list_free_strings(list);
10134 } else if (val == G_ALERTOTHER) {
10135 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10136 list_free_strings(list);
10141 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10142 compose_insert_file(compose, (const gchar *)tmp->data);
10144 list_free_strings(list);
10146 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10149 #if GTK_CHECK_VERSION(2, 8, 0)
10150 /* do nothing, handled by GTK */
10152 gchar *tmpfile = get_tmp_file();
10153 str_write_to_file((const gchar *)data->data, tmpfile);
10154 compose_insert_file(compose, tmpfile);
10155 claws_unlink(tmpfile);
10157 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10161 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10164 static void compose_header_drag_received_cb (GtkWidget *widget,
10165 GdkDragContext *drag_context,
10168 GtkSelectionData *data,
10171 gpointer user_data)
10173 GtkEditable *entry = (GtkEditable *)user_data;
10174 gchar *email = (gchar *)data->data;
10176 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10179 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10180 gchar *decoded=g_new(gchar, strlen(email));
10183 email += strlen("mailto:");
10184 decode_uri(decoded, email); /* will fit */
10185 gtk_editable_delete_text(entry, 0, -1);
10186 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10187 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10191 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10194 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10196 Compose *compose = (Compose *)data;
10198 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10199 compose->return_receipt = TRUE;
10201 compose->return_receipt = FALSE;
10204 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10206 Compose *compose = (Compose *)data;
10208 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10209 compose->remove_references = TRUE;
10211 compose->remove_references = FALSE;
10214 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10215 GdkEventKey *event,
10216 ComposeHeaderEntry *headerentry)
10218 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10219 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10220 !(event->state & GDK_MODIFIER_MASK) &&
10221 (event->keyval == GDK_BackSpace) &&
10222 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10223 gtk_container_remove
10224 (GTK_CONTAINER(headerentry->compose->header_table),
10225 headerentry->combo);
10226 gtk_container_remove
10227 (GTK_CONTAINER(headerentry->compose->header_table),
10228 headerentry->entry);
10229 headerentry->compose->header_list =
10230 g_slist_remove(headerentry->compose->header_list,
10232 g_free(headerentry);
10233 } else if (event->keyval == GDK_Tab) {
10234 if (headerentry->compose->header_last == headerentry) {
10235 /* Override default next focus, and give it to subject_entry
10236 * instead of notebook tabs
10238 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10239 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10246 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10247 ComposeHeaderEntry *headerentry)
10249 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10250 compose_create_header_entry(headerentry->compose);
10251 g_signal_handlers_disconnect_matched
10252 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10253 0, 0, NULL, NULL, headerentry);
10255 /* Automatically scroll down */
10256 compose_show_first_last_header(headerentry->compose, FALSE);
10262 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10264 GtkAdjustment *vadj;
10266 g_return_if_fail(compose);
10267 g_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10268 g_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10270 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10271 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10272 gtk_adjustment_changed(vadj);
10275 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10276 const gchar *text, gint len, Compose *compose)
10278 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10279 (G_OBJECT(compose->text), "paste_as_quotation"));
10282 g_return_if_fail(text != NULL);
10284 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10285 G_CALLBACK(text_inserted),
10287 if (paste_as_quotation) {
10289 const gchar *qmark;
10291 GtkTextIter start_iter;
10294 len = strlen(text);
10296 new_text = g_strndup(text, len);
10298 qmark = compose_quote_char_from_context(compose);
10300 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10301 gtk_text_buffer_place_cursor(buffer, iter);
10303 pos = gtk_text_iter_get_offset(iter);
10305 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10306 _("Quote format error at line %d."));
10307 quote_fmt_reset_vartable();
10309 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10310 GINT_TO_POINTER(paste_as_quotation - 1));
10312 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10313 gtk_text_buffer_place_cursor(buffer, iter);
10314 gtk_text_buffer_delete_mark(buffer, mark);
10316 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10317 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10318 compose_beautify_paragraph(compose, &start_iter, FALSE);
10319 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10320 gtk_text_buffer_delete_mark(buffer, mark);
10322 if (strcmp(text, "\n") || compose->automatic_break
10323 || gtk_text_iter_starts_line(iter)) {
10324 GtkTextIter before_ins;
10325 gtk_text_buffer_insert(buffer, iter, text, len);
10326 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10327 before_ins = *iter;
10328 gtk_text_iter_backward_chars(&before_ins, len);
10329 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10332 /* check if the preceding is just whitespace or quote */
10333 GtkTextIter start_line;
10334 gchar *tmp = NULL, *quote = NULL;
10335 gint quote_len = 0, is_normal = 0;
10336 start_line = *iter;
10337 gtk_text_iter_set_line_offset(&start_line, 0);
10338 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10341 if (*tmp == '\0') {
10344 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10352 gtk_text_buffer_insert(buffer, iter, text, len);
10354 gtk_text_buffer_insert_with_tags_by_name(buffer,
10355 iter, text, len, "no_join", NULL);
10360 if (!paste_as_quotation) {
10361 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10362 compose_beautify_paragraph(compose, iter, FALSE);
10363 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10364 gtk_text_buffer_delete_mark(buffer, mark);
10367 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10368 G_CALLBACK(text_inserted),
10370 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10372 if (prefs_common.autosave &&
10373 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10374 compose->draft_timeout_tag != -2 /* disabled while loading */)
10375 compose->draft_timeout_tag = g_timeout_add
10376 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10378 static gint compose_defer_auto_save_draft(Compose *compose)
10380 compose->draft_timeout_tag = -1;
10381 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10386 static void compose_check_all(GtkAction *action, gpointer data)
10388 Compose *compose = (Compose *)data;
10389 if (compose->gtkaspell)
10390 gtkaspell_check_all(compose->gtkaspell);
10393 static void compose_highlight_all(GtkAction *action, gpointer data)
10395 Compose *compose = (Compose *)data;
10396 if (compose->gtkaspell)
10397 gtkaspell_highlight_all(compose->gtkaspell);
10400 static void compose_check_backwards(GtkAction *action, gpointer data)
10402 Compose *compose = (Compose *)data;
10403 if (compose->gtkaspell)
10404 gtkaspell_check_backwards(compose->gtkaspell);
10406 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10410 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10412 Compose *compose = (Compose *)data;
10413 if (compose->gtkaspell)
10414 gtkaspell_check_forwards_go(compose->gtkaspell);
10416 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10422 *\brief Guess originating forward account from MsgInfo and several
10423 * "common preference" settings. Return NULL if no guess.
10425 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10427 PrefsAccount *account = NULL;
10429 g_return_val_if_fail(msginfo, NULL);
10430 g_return_val_if_fail(msginfo->folder, NULL);
10431 g_return_val_if_fail(msginfo->folder->prefs, NULL);
10433 if (msginfo->folder->prefs->enable_default_account)
10434 account = account_find_from_id(msginfo->folder->prefs->default_account);
10437 account = msginfo->folder->folder->account;
10439 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10441 Xstrdup_a(to, msginfo->to, return NULL);
10442 extract_address(to);
10443 account = account_find_from_address(to, FALSE);
10446 if (!account && prefs_common.forward_account_autosel) {
10447 gchar cc[BUFFSIZE];
10448 if (!procheader_get_header_from_msginfo
10449 (msginfo, cc,sizeof cc , "Cc:")) {
10450 gchar *buf = cc + strlen("Cc:");
10451 extract_address(buf);
10452 account = account_find_from_address(buf, FALSE);
10456 if (!account && prefs_common.forward_account_autosel) {
10457 gchar deliveredto[BUFFSIZE];
10458 if (!procheader_get_header_from_msginfo
10459 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10460 gchar *buf = deliveredto + strlen("Delivered-To:");
10461 extract_address(buf);
10462 account = account_find_from_address(buf, FALSE);
10469 gboolean compose_close(Compose *compose)
10473 if (!g_mutex_trylock(compose->mutex)) {
10474 /* we have to wait for the (possibly deferred by auto-save)
10475 * drafting to be done, before destroying the compose under
10477 debug_print("waiting for drafting to finish...\n");
10478 compose_allow_user_actions(compose, FALSE);
10479 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10482 g_return_val_if_fail(compose, FALSE);
10483 gtkut_widget_get_uposition(compose->window, &x, &y);
10484 if (!compose->batch) {
10485 prefs_common.compose_x = x;
10486 prefs_common.compose_y = y;
10488 g_mutex_unlock(compose->mutex);
10489 compose_destroy(compose);
10494 * Add entry field for each address in list.
10495 * \param compose E-Mail composition object.
10496 * \param listAddress List of (formatted) E-Mail addresses.
10498 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10501 node = listAddress;
10503 addr = ( gchar * ) node->data;
10504 compose_entry_append( compose, addr, COMPOSE_TO );
10505 node = g_list_next( node );
10509 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10510 guint action, gboolean opening_multiple)
10512 gchar *body = NULL;
10513 GSList *new_msglist = NULL;
10514 MsgInfo *tmp_msginfo = NULL;
10515 gboolean originally_enc = FALSE;
10516 gboolean originally_sig = FALSE;
10517 Compose *compose = NULL;
10519 g_return_if_fail(msgview != NULL);
10521 g_return_if_fail(msginfo_list != NULL);
10523 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10524 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10525 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10527 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10528 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10529 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10530 orig_msginfo, mimeinfo);
10531 if (tmp_msginfo != NULL) {
10532 new_msglist = g_slist_append(NULL, tmp_msginfo);
10534 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10535 privacy_msginfo_get_signed_state(orig_msginfo);
10536 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
10538 tmp_msginfo->folder = orig_msginfo->folder;
10539 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10540 if (orig_msginfo->tags)
10541 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10546 if (!opening_multiple)
10547 body = messageview_get_selection(msgview);
10550 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10551 procmsg_msginfo_free(tmp_msginfo);
10552 g_slist_free(new_msglist);
10554 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10556 if (compose && originally_enc) {
10557 compose_force_encryption(compose, compose->account, FALSE);
10560 if (compose && originally_sig && compose->account->default_sign_reply) {
10561 compose_force_signing(compose, compose->account);
10567 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10570 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
10571 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10572 GSList *cur = msginfo_list;
10573 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10574 "messages. Opening the windows "
10575 "could take some time. Do you "
10576 "want to continue?"),
10577 g_slist_length(msginfo_list));
10578 if (g_slist_length(msginfo_list) > 9
10579 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10580 != G_ALERTALTERNATE) {
10585 /* We'll open multiple compose windows */
10586 /* let the WM place the next windows */
10587 compose_force_window_origin = FALSE;
10588 for (; cur; cur = cur->next) {
10590 tmplist.data = cur->data;
10591 tmplist.next = NULL;
10592 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10594 compose_force_window_origin = TRUE;
10596 /* forwarding multiple mails as attachments is done via a
10597 * single compose window */
10598 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10602 void compose_set_position(Compose *compose, gint pos)
10604 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10606 gtkut_text_view_set_position(text, pos);
10609 gboolean compose_search_string(Compose *compose,
10610 const gchar *str, gboolean case_sens)
10612 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10614 return gtkut_text_view_search_string(text, str, case_sens);
10617 gboolean compose_search_string_backward(Compose *compose,
10618 const gchar *str, gboolean case_sens)
10620 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10622 return gtkut_text_view_search_string_backward(text, str, case_sens);
10625 /* allocate a msginfo structure and populate its data from a compose data structure */
10626 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10628 MsgInfo *newmsginfo;
10630 gchar buf[BUFFSIZE];
10632 g_return_val_if_fail( compose != NULL, NULL );
10634 newmsginfo = procmsg_msginfo_new();
10637 get_rfc822_date(buf, sizeof(buf));
10638 newmsginfo->date = g_strdup(buf);
10641 if (compose->from_name) {
10642 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10643 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10647 if (compose->subject_entry)
10648 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10650 /* to, cc, reply-to, newsgroups */
10651 for (list = compose->header_list; list; list = list->next) {
10652 gchar *header = gtk_editable_get_chars(
10654 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
10655 gchar *entry = gtk_editable_get_chars(
10656 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10658 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10659 if ( newmsginfo->to == NULL ) {
10660 newmsginfo->to = g_strdup(entry);
10661 } else if (entry && *entry) {
10662 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10663 g_free(newmsginfo->to);
10664 newmsginfo->to = tmp;
10667 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10668 if ( newmsginfo->cc == NULL ) {
10669 newmsginfo->cc = g_strdup(entry);
10670 } else if (entry && *entry) {
10671 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10672 g_free(newmsginfo->cc);
10673 newmsginfo->cc = tmp;
10676 if ( strcasecmp(header,
10677 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10678 if ( newmsginfo->newsgroups == NULL ) {
10679 newmsginfo->newsgroups = g_strdup(entry);
10680 } else if (entry && *entry) {
10681 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10682 g_free(newmsginfo->newsgroups);
10683 newmsginfo->newsgroups = tmp;
10691 /* other data is unset */
10697 /* update compose's dictionaries from folder dict settings */
10698 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10699 FolderItem *folder_item)
10701 g_return_if_fail(compose != NULL);
10703 if (compose->gtkaspell && folder_item && folder_item->prefs) {
10704 FolderItemPrefs *prefs = folder_item->prefs;
10706 if (prefs->enable_default_dictionary)
10707 gtkaspell_change_dict(compose->gtkaspell,
10708 prefs->default_dictionary, FALSE);
10709 if (folder_item->prefs->enable_default_alt_dictionary)
10710 gtkaspell_change_alt_dict(compose->gtkaspell,
10711 prefs->default_alt_dictionary);
10712 if (prefs->enable_default_dictionary
10713 || prefs->enable_default_alt_dictionary)
10714 compose_spell_menu_changed(compose);