2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2011 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #include "addressbook.h"
64 #include "folderview.h"
67 #include "stock_pixmap.h"
68 #include "send_message.h"
71 #include "customheader.h"
72 #include "prefs_common.h"
73 #include "prefs_account.h"
77 #include "procheader.h"
79 #include "statusbar.h"
82 #include "quoted-printable.h"
86 #include "gtkshruler.h"
88 #include "alertpanel.h"
89 #include "manage_window.h"
91 #include "addr_compl.h"
92 #include "quote_fmt.h"
94 #include "foldersel.h"
97 #include "message_search.h"
102 #include "autofaces.h"
103 #include "spell_entry.h"
116 #define N_ATTACH_COLS (N_COL_COLUMNS)
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
132 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
134 } ComposeCallAdvancedAction;
138 PRIORITY_HIGHEST = 1,
147 COMPOSE_INSERT_SUCCESS,
148 COMPOSE_INSERT_READ_ERROR,
149 COMPOSE_INSERT_INVALID_CHARACTER,
150 COMPOSE_INSERT_NO_FILE
151 } ComposeInsertResult;
155 COMPOSE_WRITE_FOR_SEND,
156 COMPOSE_WRITE_FOR_STORE
161 COMPOSE_QUOTE_FORCED,
168 SUBJECT_FIELD_PRESENT,
173 #define B64_LINE_SIZE 57
174 #define B64_BUFFSIZE 77
176 #define MAX_REFERENCES_LEN 999
178 static GList *compose_list = NULL;
180 static Compose *compose_generic_new (PrefsAccount *account,
183 GPtrArray *attach_files,
184 GList *listAddress );
186 static Compose *compose_create (PrefsAccount *account,
191 static void compose_entry_mark_default_to (Compose *compose,
192 const gchar *address);
193 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
194 ComposeQuoteMode quote_mode,
198 static Compose *compose_forward_multiple (PrefsAccount *account,
199 GSList *msginfo_list);
200 static Compose *compose_reply (MsgInfo *msginfo,
201 ComposeQuoteMode quote_mode,
206 static Compose *compose_reply_mode (ComposeMode mode,
207 GSList *msginfo_list,
209 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
210 static void compose_update_privacy_systems_menu(Compose *compose);
212 static GtkWidget *compose_account_option_menu_create
214 static void compose_set_out_encoding (Compose *compose);
215 static void compose_set_template_menu (Compose *compose);
216 static void compose_destroy (Compose *compose);
218 static MailField compose_entries_set (Compose *compose,
220 ComposeEntryType to_type);
221 static gint compose_parse_header (Compose *compose,
223 static gchar *compose_parse_references (const gchar *ref,
226 static gchar *compose_quote_fmt (Compose *compose,
232 gboolean need_unescape,
233 const gchar *err_msg);
235 static void compose_reply_set_entry (Compose *compose,
241 followup_and_reply_to);
242 static void compose_reedit_set_entry (Compose *compose,
245 static void compose_insert_sig (Compose *compose,
247 static ComposeInsertResult compose_insert_file (Compose *compose,
250 static gboolean compose_attach_append (Compose *compose,
253 const gchar *content_type,
254 const gchar *charset);
255 static void compose_attach_parts (Compose *compose,
258 static gboolean compose_beautify_paragraph (Compose *compose,
259 GtkTextIter *par_iter,
261 static void compose_wrap_all (Compose *compose);
262 static void compose_wrap_all_full (Compose *compose,
265 static void compose_set_title (Compose *compose);
266 static void compose_select_account (Compose *compose,
267 PrefsAccount *account,
270 static PrefsAccount *compose_current_mail_account(void);
271 /* static gint compose_send (Compose *compose); */
272 static gboolean compose_check_for_valid_recipient
274 static gboolean compose_check_entries (Compose *compose,
275 gboolean check_everything);
276 static gint compose_write_to_file (Compose *compose,
279 gboolean attach_parts);
280 static gint compose_write_body_to_file (Compose *compose,
282 static gint compose_remove_reedit_target (Compose *compose,
284 static void compose_remove_draft (Compose *compose);
285 static gint compose_queue_sub (Compose *compose,
289 gboolean check_subject,
290 gboolean remove_reedit_target);
291 static int compose_add_attachments (Compose *compose,
293 static gchar *compose_get_header (Compose *compose);
295 static void compose_convert_header (Compose *compose,
300 gboolean addr_field);
302 static void compose_attach_info_free (AttachInfo *ainfo);
303 static void compose_attach_remove_selected (GtkAction *action,
306 static void compose_template_apply (Compose *compose,
309 static void compose_attach_property (GtkAction *action,
311 static void compose_attach_property_create (gboolean *cancelled);
312 static void attach_property_ok (GtkWidget *widget,
313 gboolean *cancelled);
314 static void attach_property_cancel (GtkWidget *widget,
315 gboolean *cancelled);
316 static gint attach_property_delete_event (GtkWidget *widget,
318 gboolean *cancelled);
319 static gboolean attach_property_key_pressed (GtkWidget *widget,
321 gboolean *cancelled);
323 static void compose_exec_ext_editor (Compose *compose);
325 static gint compose_exec_ext_editor_real (const gchar *file);
326 static gboolean compose_ext_editor_kill (Compose *compose);
327 static gboolean compose_input_cb (GIOChannel *source,
328 GIOCondition condition,
330 static void compose_set_ext_editor_sensitive (Compose *compose,
332 #endif /* G_OS_UNIX */
334 static void compose_undo_state_changed (UndoMain *undostruct,
339 static void compose_create_header_entry (Compose *compose);
340 static void compose_add_header_entry (Compose *compose, const gchar *header,
341 gchar *text, ComposePrefType pref_type);
342 static void compose_remove_header_entries(Compose *compose);
344 static void compose_update_priority_menu_item(Compose * compose);
346 static void compose_spell_menu_changed (void *data);
347 static void compose_dict_changed (void *data);
349 static void compose_add_field_list ( Compose *compose,
350 GList *listAddress );
352 /* callback functions */
354 static gboolean compose_edit_size_alloc (GtkEditable *widget,
355 GtkAllocation *allocation,
356 GtkSHRuler *shruler);
357 static void account_activated (GtkComboBox *optmenu,
359 static void attach_selected (GtkTreeView *tree_view,
360 GtkTreePath *tree_path,
361 GtkTreeViewColumn *column,
363 static gboolean attach_button_pressed (GtkWidget *widget,
364 GdkEventButton *event,
366 static gboolean attach_key_pressed (GtkWidget *widget,
369 static void compose_send_cb (GtkAction *action, gpointer data);
370 static void compose_send_later_cb (GtkAction *action, gpointer data);
372 static void compose_save_cb (GtkAction *action,
375 static void compose_attach_cb (GtkAction *action,
377 static void compose_insert_file_cb (GtkAction *action,
379 static void compose_insert_sig_cb (GtkAction *action,
382 static void compose_close_cb (GtkAction *action,
384 static void compose_print_cb (GtkAction *action,
387 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
389 static void compose_address_cb (GtkAction *action,
391 static void about_show_cb (GtkAction *action,
393 static void compose_template_activate_cb(GtkWidget *widget,
396 static void compose_ext_editor_cb (GtkAction *action,
399 static gint compose_delete_cb (GtkWidget *widget,
403 static void compose_undo_cb (GtkAction *action,
405 static void compose_redo_cb (GtkAction *action,
407 static void compose_cut_cb (GtkAction *action,
409 static void compose_copy_cb (GtkAction *action,
411 static void compose_paste_cb (GtkAction *action,
413 static void compose_paste_as_quote_cb (GtkAction *action,
415 static void compose_paste_no_wrap_cb (GtkAction *action,
417 static void compose_paste_wrap_cb (GtkAction *action,
419 static void compose_allsel_cb (GtkAction *action,
422 static void compose_advanced_action_cb (GtkAction *action,
425 static void compose_grab_focus_cb (GtkWidget *widget,
428 static void compose_changed_cb (GtkTextBuffer *textbuf,
431 static void compose_wrap_cb (GtkAction *action,
433 static void compose_wrap_all_cb (GtkAction *action,
435 static void compose_find_cb (GtkAction *action,
437 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
439 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
442 static void compose_toggle_ruler_cb (GtkToggleAction *action,
444 static void compose_toggle_sign_cb (GtkToggleAction *action,
446 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
448 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
449 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
450 static void activate_privacy_system (Compose *compose,
451 PrefsAccount *account,
453 static void compose_use_signing(Compose *compose, gboolean use_signing);
454 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
455 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
457 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
459 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
460 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
461 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
463 static void compose_attach_drag_received_cb (GtkWidget *widget,
464 GdkDragContext *drag_context,
467 GtkSelectionData *data,
471 static void compose_insert_drag_received_cb (GtkWidget *widget,
472 GdkDragContext *drag_context,
475 GtkSelectionData *data,
479 static void compose_header_drag_received_cb (GtkWidget *widget,
480 GdkDragContext *drag_context,
483 GtkSelectionData *data,
488 static gboolean compose_drag_drop (GtkWidget *widget,
489 GdkDragContext *drag_context,
491 guint time, gpointer user_data);
493 static void text_inserted (GtkTextBuffer *buffer,
498 static Compose *compose_generic_reply(MsgInfo *msginfo,
499 ComposeQuoteMode quote_mode,
503 gboolean followup_and_reply_to,
506 static void compose_headerentry_changed_cb (GtkWidget *entry,
507 ComposeHeaderEntry *headerentry);
508 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
510 ComposeHeaderEntry *headerentry);
511 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
512 ComposeHeaderEntry *headerentry);
514 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
516 static void compose_allow_user_actions (Compose *compose, gboolean allow);
518 static void compose_nothing_cb (GtkAction *action, gpointer data)
524 static void compose_check_all (GtkAction *action, gpointer data);
525 static void compose_highlight_all (GtkAction *action, gpointer data);
526 static void compose_check_backwards (GtkAction *action, gpointer data);
527 static void compose_check_forwards_go (GtkAction *action, gpointer data);
530 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
532 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
535 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
536 FolderItem *folder_item);
538 static void compose_attach_update_label(Compose *compose);
539 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
540 gboolean respect_default_to);
542 static GtkActionEntry compose_popup_entries[] =
544 {"Compose", NULL, "Compose" },
545 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
546 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
547 {"Compose/---", NULL, "---", NULL, NULL, NULL },
548 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
551 static GtkActionEntry compose_entries[] =
553 {"Menu", NULL, "Menu" },
555 {"Message", NULL, N_("_Message") },
556 {"Edit", NULL, N_("_Edit") },
558 {"Spelling", NULL, N_("_Spelling") },
560 {"Options", NULL, N_("_Options") },
561 {"Tools", NULL, N_("_Tools") },
562 {"Help", NULL, N_("_Help") },
564 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
565 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
566 {"Message/---", NULL, "---" },
568 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
569 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
570 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
571 /* {"Message/---", NULL, "---" }, */
572 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
573 /* {"Message/---", NULL, "---" }, */
574 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
575 /* {"Message/---", NULL, "---" }, */
576 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
579 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
580 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
581 {"Edit/---", NULL, "---" },
583 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
584 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
585 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
587 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
588 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
589 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
590 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
592 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
594 {"Edit/Advanced", NULL, N_("A_dvanced") },
595 {"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*/
596 {"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*/
597 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
598 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
599 {"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*/
600 {"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*/
601 {"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*/
602 {"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*/
603 {"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*/
604 {"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*/
605 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
606 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
607 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
608 {"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*/
610 /* {"Edit/---", NULL, "---" }, */
611 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
613 /* {"Edit/---", NULL, "---" }, */
614 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
615 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
616 /* {"Edit/---", NULL, "---" }, */
617 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
620 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
621 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
622 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
623 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
625 {"Spelling/---", NULL, "---" },
626 {"Spelling/Options", NULL, N_("_Options") },
631 {"Options/ReplyMode", NULL, N_("Reply _mode") },
632 {"Options/---", NULL, "---" },
633 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
634 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
636 /* {"Options/---", NULL, "---" }, */
638 {"Options/Priority", NULL, N_("_Priority") },
640 {"Options/Encoding", NULL, N_("Character _encoding") },
641 {"Options/Encoding/---", NULL, "---" },
642 #define ENC_ACTION(cs_char,c_char,string) \
643 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
645 {"Options/Encoding/Western", NULL, N_("Western European") },
646 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
647 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
648 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
649 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
650 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
651 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
652 {"Options/Encoding/Korean", NULL, N_("Korean") },
653 {"Options/Encoding/Thai", NULL, N_("Thai") },
656 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
658 {"Tools/Template", NULL, N_("_Template") },
659 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
660 {"Tools/Actions", NULL, N_("Actio_ns") },
661 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
664 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
667 static GtkToggleActionEntry compose_toggle_entries[] =
669 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
670 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
671 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
672 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
673 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
674 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
675 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
678 static GtkRadioActionEntry compose_radio_rm_entries[] =
680 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
681 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
682 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
683 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
686 static GtkRadioActionEntry compose_radio_prio_entries[] =
688 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
689 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
690 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
691 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
692 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
695 static GtkRadioActionEntry compose_radio_enc_entries[] =
697 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
731 static GtkTargetEntry compose_mime_types[] =
733 {"text/uri-list", 0, 0},
734 {"UTF8_STRING", 0, 0},
738 static gboolean compose_put_existing_to_front(MsgInfo *info)
740 GList *compose_list = compose_get_compose_list();
744 for (elem = compose_list; elem != NULL && elem->data != NULL;
746 Compose *c = (Compose*)elem->data;
748 if (!c->targetinfo || !c->targetinfo->msgid ||
752 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
753 gtkut_window_popup(c->window);
761 static GdkColor quote_color1 =
762 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
763 static GdkColor quote_color2 =
764 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
765 static GdkColor quote_color3 =
766 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
768 static GdkColor quote_bgcolor1 =
769 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
770 static GdkColor quote_bgcolor2 =
771 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
772 static GdkColor quote_bgcolor3 =
773 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
775 static GdkColor signature_color = {
782 static GdkColor uri_color = {
789 static void compose_create_tags(GtkTextView *text, Compose *compose)
791 GtkTextBuffer *buffer;
792 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
793 #if !GTK_CHECK_VERSION(2, 24, 0)
800 buffer = gtk_text_view_get_buffer(text);
802 if (prefs_common.enable_color) {
803 /* grab the quote colors, converting from an int to a GdkColor */
804 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
806 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
808 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
810 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
812 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
814 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
816 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
818 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
821 signature_color = quote_color1 = quote_color2 = quote_color3 =
822 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
825 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
826 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
827 "foreground-gdk", "e_color1,
828 "paragraph-background-gdk", "e_bgcolor1,
830 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
831 "foreground-gdk", "e_color2,
832 "paragraph-background-gdk", "e_bgcolor2,
834 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
835 "foreground-gdk", "e_color3,
836 "paragraph-background-gdk", "e_bgcolor3,
839 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
840 "foreground-gdk", "e_color1,
842 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
843 "foreground-gdk", "e_color2,
845 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
846 "foreground-gdk", "e_color3,
850 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
851 "foreground-gdk", &signature_color,
854 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
855 "foreground-gdk", &uri_color,
857 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
858 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
860 color[0] = quote_color1;
861 color[1] = quote_color2;
862 color[2] = quote_color3;
863 color[3] = quote_bgcolor1;
864 color[4] = quote_bgcolor2;
865 color[5] = quote_bgcolor3;
866 color[6] = signature_color;
867 color[7] = uri_color;
868 #if !GTK_CHECK_VERSION(2, 24, 0)
869 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
870 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
872 for (i = 0; i < 8; i++) {
873 if (success[i] == FALSE) {
876 g_warning("Compose: color allocation failed.\n");
877 style = gtk_widget_get_style(GTK_WIDGET(text));
878 quote_color1 = quote_color2 = quote_color3 =
879 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
880 signature_color = uri_color = black;
886 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
887 GPtrArray *attach_files)
889 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
892 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
894 return compose_generic_new(account, mailto, item, NULL, NULL);
897 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
899 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
902 #define SCROLL_TO_CURSOR(compose) { \
903 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
904 gtk_text_view_get_buffer( \
905 GTK_TEXT_VIEW(compose->text))); \
906 gtk_text_view_scroll_mark_onscreen( \
907 GTK_TEXT_VIEW(compose->text), \
911 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
914 if (folderidentifier) {
915 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
916 prefs_common.compose_save_to_history = add_history(
917 prefs_common.compose_save_to_history, folderidentifier);
918 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
919 prefs_common.compose_save_to_history);
922 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
923 if (folderidentifier)
924 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
926 gtk_entry_set_text(GTK_ENTRY(entry), "");
929 static gchar *compose_get_save_to(Compose *compose)
932 gchar *result = NULL;
933 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
934 result = gtk_editable_get_chars(entry, 0, -1);
937 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
938 prefs_common.compose_save_to_history = add_history(
939 prefs_common.compose_save_to_history, result);
940 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
941 prefs_common.compose_save_to_history);
946 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
947 GPtrArray *attach_files, GList *listAddress )
950 GtkTextView *textview;
951 GtkTextBuffer *textbuf;
953 const gchar *subject_format = NULL;
954 const gchar *body_format = NULL;
955 gchar *mailto_from = NULL;
956 PrefsAccount *mailto_account = NULL;
957 MsgInfo* dummyinfo = NULL;
958 gint cursor_pos = -1;
959 MailField mfield = NO_FIELD_PRESENT;
963 /* check if mailto defines a from */
964 if (mailto && *mailto != '\0') {
965 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
966 /* mailto defines a from, check if we can get account prefs from it,
967 if not, the account prefs will be guessed using other ways, but we'll keep
970 mailto_account = account_find_from_address(mailto_from, TRUE);
972 account = mailto_account;
975 /* if no account prefs set from mailto, set if from folder prefs (if any) */
976 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
977 account = account_find_from_id(item->prefs->default_account);
979 /* if no account prefs set, fallback to the current one */
980 if (!account) account = cur_account;
981 cm_return_val_if_fail(account != NULL, NULL);
983 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
985 /* override from name if mailto asked for it */
987 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
990 /* override from name according to folder properties */
991 if (item && item->prefs &&
992 item->prefs->compose_with_format &&
993 item->prefs->compose_override_from_format &&
994 *item->prefs->compose_override_from_format != '\0') {
999 dummyinfo = compose_msginfo_new_from_compose(compose);
1001 /* decode \-escape sequences in the internal representation of the quote format */
1002 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1003 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1006 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1007 compose->gtkaspell);
1009 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1011 quote_fmt_scan_string(tmp);
1014 buf = quote_fmt_get_buffer();
1016 alertpanel_error(_("New message From format error."));
1018 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1019 quote_fmt_reset_vartable();
1024 compose->replyinfo = NULL;
1025 compose->fwdinfo = NULL;
1027 textview = GTK_TEXT_VIEW(compose->text);
1028 textbuf = gtk_text_view_get_buffer(textview);
1029 compose_create_tags(textview, compose);
1031 undo_block(compose->undostruct);
1033 compose_set_dictionaries_from_folder_prefs(compose, item);
1036 if (account->auto_sig)
1037 compose_insert_sig(compose, FALSE);
1038 gtk_text_buffer_get_start_iter(textbuf, &iter);
1039 gtk_text_buffer_place_cursor(textbuf, &iter);
1041 if (account->protocol != A_NNTP) {
1042 if (mailto && *mailto != '\0') {
1043 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1046 compose_set_folder_prefs(compose, item, TRUE);
1048 if (item && item->ret_rcpt) {
1049 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1052 if (mailto && *mailto != '\0') {
1053 if (!strchr(mailto, '@'))
1054 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1056 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1057 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1058 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1059 mfield = TO_FIELD_PRESENT;
1062 * CLAWS: just don't allow return receipt request, even if the user
1063 * may want to send an email. simple but foolproof.
1065 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1067 compose_add_field_list( compose, listAddress );
1069 if (item && item->prefs && item->prefs->compose_with_format) {
1070 subject_format = item->prefs->compose_subject_format;
1071 body_format = item->prefs->compose_body_format;
1072 } else if (account->compose_with_format) {
1073 subject_format = account->compose_subject_format;
1074 body_format = account->compose_body_format;
1075 } else if (prefs_common.compose_with_format) {
1076 subject_format = prefs_common.compose_subject_format;
1077 body_format = prefs_common.compose_body_format;
1080 if (subject_format || body_format) {
1083 && *subject_format != '\0' )
1085 gchar *subject = NULL;
1090 dummyinfo = compose_msginfo_new_from_compose(compose);
1092 /* decode \-escape sequences in the internal representation of the quote format */
1093 tmp = g_malloc(strlen(subject_format)+1);
1094 pref_get_unescaped_pref(tmp, subject_format);
1096 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1098 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1099 compose->gtkaspell);
1101 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1103 quote_fmt_scan_string(tmp);
1106 buf = quote_fmt_get_buffer();
1108 alertpanel_error(_("New message subject format error."));
1110 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1111 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1112 quote_fmt_reset_vartable();
1116 mfield = SUBJECT_FIELD_PRESENT;
1120 && *body_format != '\0' )
1123 GtkTextBuffer *buffer;
1124 GtkTextIter start, end;
1128 dummyinfo = compose_msginfo_new_from_compose(compose);
1130 text = GTK_TEXT_VIEW(compose->text);
1131 buffer = gtk_text_view_get_buffer(text);
1132 gtk_text_buffer_get_start_iter(buffer, &start);
1133 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1134 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1136 compose_quote_fmt(compose, dummyinfo,
1138 NULL, tmp, FALSE, TRUE,
1139 _("The body of the \"New message\" template has an error at line %d."));
1140 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1141 quote_fmt_reset_vartable();
1145 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1146 gtkaspell_highlight_all(compose->gtkaspell);
1148 mfield = BODY_FIELD_PRESENT;
1152 procmsg_msginfo_free( dummyinfo );
1158 for (i = 0; i < attach_files->len; i++) {
1159 file = g_ptr_array_index(attach_files, i);
1160 compose_attach_append(compose, file, file, NULL, NULL);
1164 compose_show_first_last_header(compose, TRUE);
1166 /* Set save folder */
1167 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1168 gchar *folderidentifier;
1170 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1171 folderidentifier = folder_item_get_identifier(item);
1172 compose_set_save_to(compose, folderidentifier);
1173 g_free(folderidentifier);
1176 /* Place cursor according to provided input (mfield) */
1178 case NO_FIELD_PRESENT:
1179 if (compose->header_last)
1180 gtk_widget_grab_focus(compose->header_last->entry);
1182 case TO_FIELD_PRESENT:
1183 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1185 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1188 gtk_widget_grab_focus(compose->subject_entry);
1190 case SUBJECT_FIELD_PRESENT:
1191 textview = GTK_TEXT_VIEW(compose->text);
1194 textbuf = gtk_text_view_get_buffer(textview);
1197 mark = gtk_text_buffer_get_insert(textbuf);
1198 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1199 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1201 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1202 * only defers where it comes to the variable body
1203 * is not null. If no body is present compose->text
1204 * will be null in which case you cannot place the
1205 * cursor inside the component so. An empty component
1206 * is therefore created before placing the cursor
1208 case BODY_FIELD_PRESENT:
1209 cursor_pos = quote_fmt_get_cursor_pos();
1210 if (cursor_pos == -1)
1211 gtk_widget_grab_focus(compose->header_last->entry);
1213 gtk_widget_grab_focus(compose->text);
1217 undo_unblock(compose->undostruct);
1219 if (prefs_common.auto_exteditor)
1220 compose_exec_ext_editor(compose);
1222 compose->draft_timeout_tag = -1;
1223 SCROLL_TO_CURSOR(compose);
1225 compose->modified = FALSE;
1226 compose_set_title(compose);
1228 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1233 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1234 gboolean override_pref, const gchar *system)
1236 const gchar *privacy = NULL;
1238 cm_return_if_fail(compose != NULL);
1239 cm_return_if_fail(account != NULL);
1241 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1246 else if (account->default_privacy_system
1247 && strlen(account->default_privacy_system)) {
1248 privacy = account->default_privacy_system;
1250 GSList *privacy_avail = privacy_get_system_ids();
1251 if (privacy_avail && g_slist_length(privacy_avail)) {
1252 privacy = (gchar *)(privacy_avail->data);
1255 if (privacy != NULL) {
1257 g_free(compose->privacy_system);
1258 compose->privacy_system = NULL;
1260 if (compose->privacy_system == NULL)
1261 compose->privacy_system = g_strdup(privacy);
1262 else if (*(compose->privacy_system) == '\0') {
1263 g_free(compose->privacy_system);
1264 compose->privacy_system = g_strdup(privacy);
1266 compose_update_privacy_system_menu_item(compose, FALSE);
1267 compose_use_encryption(compose, TRUE);
1271 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1273 const gchar *privacy = NULL;
1277 else if (account->default_privacy_system
1278 && strlen(account->default_privacy_system)) {
1279 privacy = account->default_privacy_system;
1281 GSList *privacy_avail = privacy_get_system_ids();
1282 if (privacy_avail && g_slist_length(privacy_avail)) {
1283 privacy = (gchar *)(privacy_avail->data);
1287 if (privacy != NULL) {
1289 g_free(compose->privacy_system);
1290 compose->privacy_system = NULL;
1292 if (compose->privacy_system == NULL)
1293 compose->privacy_system = g_strdup(privacy);
1294 compose_update_privacy_system_menu_item(compose, FALSE);
1295 compose_use_signing(compose, TRUE);
1299 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1303 Compose *compose = NULL;
1305 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1307 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1308 cm_return_val_if_fail(msginfo != NULL, NULL);
1310 list_len = g_slist_length(msginfo_list);
1314 case COMPOSE_REPLY_TO_ADDRESS:
1315 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1316 FALSE, prefs_common.default_reply_list, FALSE, body);
1318 case COMPOSE_REPLY_WITH_QUOTE:
1319 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1320 FALSE, prefs_common.default_reply_list, FALSE, body);
1322 case COMPOSE_REPLY_WITHOUT_QUOTE:
1323 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1324 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1326 case COMPOSE_REPLY_TO_SENDER:
1327 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1328 FALSE, FALSE, TRUE, body);
1330 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1331 compose = compose_followup_and_reply_to(msginfo,
1332 COMPOSE_QUOTE_CHECK,
1333 FALSE, FALSE, body);
1335 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1336 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1337 FALSE, FALSE, TRUE, body);
1339 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1340 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1341 FALSE, FALSE, TRUE, NULL);
1343 case COMPOSE_REPLY_TO_ALL:
1344 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1345 TRUE, FALSE, FALSE, body);
1347 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1348 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1349 TRUE, FALSE, FALSE, body);
1351 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1352 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1353 TRUE, FALSE, FALSE, NULL);
1355 case COMPOSE_REPLY_TO_LIST:
1356 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1357 FALSE, TRUE, FALSE, body);
1359 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1360 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1361 FALSE, TRUE, FALSE, body);
1363 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1364 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1365 FALSE, TRUE, FALSE, NULL);
1367 case COMPOSE_FORWARD:
1368 if (prefs_common.forward_as_attachment) {
1369 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1372 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1376 case COMPOSE_FORWARD_INLINE:
1377 /* check if we reply to more than one Message */
1378 if (list_len == 1) {
1379 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1382 /* more messages FALL THROUGH */
1383 case COMPOSE_FORWARD_AS_ATTACH:
1384 compose = compose_forward_multiple(NULL, msginfo_list);
1386 case COMPOSE_REDIRECT:
1387 compose = compose_redirect(NULL, msginfo, FALSE);
1390 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1393 if (compose == NULL) {
1394 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1398 compose->rmode = mode;
1399 switch (compose->rmode) {
1401 case COMPOSE_REPLY_WITH_QUOTE:
1402 case COMPOSE_REPLY_WITHOUT_QUOTE:
1403 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1404 debug_print("reply mode Normal\n");
1405 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1406 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1408 case COMPOSE_REPLY_TO_SENDER:
1409 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1410 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1411 debug_print("reply mode Sender\n");
1412 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1414 case COMPOSE_REPLY_TO_ALL:
1415 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1416 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1417 debug_print("reply mode All\n");
1418 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1420 case COMPOSE_REPLY_TO_LIST:
1421 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1422 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1423 debug_print("reply mode List\n");
1424 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1426 case COMPOSE_REPLY_TO_ADDRESS:
1427 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1435 static Compose *compose_reply(MsgInfo *msginfo,
1436 ComposeQuoteMode quote_mode,
1442 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1443 to_sender, FALSE, body);
1446 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1447 ComposeQuoteMode quote_mode,
1452 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1453 to_sender, TRUE, body);
1456 static void compose_extract_original_charset(Compose *compose)
1458 MsgInfo *info = NULL;
1459 if (compose->replyinfo) {
1460 info = compose->replyinfo;
1461 } else if (compose->fwdinfo) {
1462 info = compose->fwdinfo;
1463 } else if (compose->targetinfo) {
1464 info = compose->targetinfo;
1467 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1468 MimeInfo *partinfo = mimeinfo;
1469 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1470 partinfo = procmime_mimeinfo_next(partinfo);
1472 compose->orig_charset =
1473 g_strdup(procmime_mimeinfo_get_parameter(
1474 partinfo, "charset"));
1476 procmime_mimeinfo_free_all(mimeinfo);
1480 #define SIGNAL_BLOCK(buffer) { \
1481 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1482 G_CALLBACK(compose_changed_cb), \
1484 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1485 G_CALLBACK(text_inserted), \
1489 #define SIGNAL_UNBLOCK(buffer) { \
1490 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1491 G_CALLBACK(compose_changed_cb), \
1493 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1494 G_CALLBACK(text_inserted), \
1498 static Compose *compose_generic_reply(MsgInfo *msginfo,
1499 ComposeQuoteMode quote_mode,
1500 gboolean to_all, gboolean to_ml,
1502 gboolean followup_and_reply_to,
1506 PrefsAccount *account = NULL;
1507 GtkTextView *textview;
1508 GtkTextBuffer *textbuf;
1509 gboolean quote = FALSE;
1510 const gchar *qmark = NULL;
1511 const gchar *body_fmt = NULL;
1512 gchar *s_system = NULL;
1514 cm_return_val_if_fail(msginfo != NULL, NULL);
1515 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1517 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1519 cm_return_val_if_fail(account != NULL, NULL);
1521 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1523 compose->updating = TRUE;
1525 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1526 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1528 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1529 if (!compose->replyinfo)
1530 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1532 compose_extract_original_charset(compose);
1534 if (msginfo->folder && msginfo->folder->ret_rcpt)
1535 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1537 /* Set save folder */
1538 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1539 gchar *folderidentifier;
1541 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1542 folderidentifier = folder_item_get_identifier(msginfo->folder);
1543 compose_set_save_to(compose, folderidentifier);
1544 g_free(folderidentifier);
1547 if (compose_parse_header(compose, msginfo) < 0) {
1548 compose->updating = FALSE;
1549 compose_destroy(compose);
1553 /* override from name according to folder properties */
1554 if (msginfo->folder && msginfo->folder->prefs &&
1555 msginfo->folder->prefs->reply_with_format &&
1556 msginfo->folder->prefs->reply_override_from_format &&
1557 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1562 /* decode \-escape sequences in the internal representation of the quote format */
1563 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1564 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1567 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1568 compose->gtkaspell);
1570 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1572 quote_fmt_scan_string(tmp);
1575 buf = quote_fmt_get_buffer();
1577 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1579 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1580 quote_fmt_reset_vartable();
1585 textview = (GTK_TEXT_VIEW(compose->text));
1586 textbuf = gtk_text_view_get_buffer(textview);
1587 compose_create_tags(textview, compose);
1589 undo_block(compose->undostruct);
1591 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1592 gtkaspell_block_check(compose->gtkaspell);
1595 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1596 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1597 /* use the reply format of folder (if enabled), or the account's one
1598 (if enabled) or fallback to the global reply format, which is always
1599 enabled (even if empty), and use the relevant quotemark */
1601 if (msginfo->folder && msginfo->folder->prefs &&
1602 msginfo->folder->prefs->reply_with_format) {
1603 qmark = msginfo->folder->prefs->reply_quotemark;
1604 body_fmt = msginfo->folder->prefs->reply_body_format;
1606 } else if (account->reply_with_format) {
1607 qmark = account->reply_quotemark;
1608 body_fmt = account->reply_body_format;
1611 qmark = prefs_common.quotemark;
1612 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1613 body_fmt = gettext(prefs_common.quotefmt);
1620 /* empty quotemark is not allowed */
1621 if (qmark == NULL || *qmark == '\0')
1623 compose_quote_fmt(compose, compose->replyinfo,
1624 body_fmt, qmark, body, FALSE, TRUE,
1625 _("The body of the \"Reply\" template has an error at line %d."));
1626 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1627 quote_fmt_reset_vartable();
1630 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1631 compose_force_encryption(compose, account, FALSE, s_system);
1634 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1635 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1636 compose_force_signing(compose, account, s_system);
1640 SIGNAL_BLOCK(textbuf);
1642 if (account->auto_sig)
1643 compose_insert_sig(compose, FALSE);
1645 compose_wrap_all(compose);
1648 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1649 gtkaspell_highlight_all(compose->gtkaspell);
1650 gtkaspell_unblock_check(compose->gtkaspell);
1652 SIGNAL_UNBLOCK(textbuf);
1654 gtk_widget_grab_focus(compose->text);
1656 undo_unblock(compose->undostruct);
1658 if (prefs_common.auto_exteditor)
1659 compose_exec_ext_editor(compose);
1661 compose->modified = FALSE;
1662 compose_set_title(compose);
1664 compose->updating = FALSE;
1665 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1666 SCROLL_TO_CURSOR(compose);
1668 if (compose->deferred_destroy) {
1669 compose_destroy(compose);
1677 #define INSERT_FW_HEADER(var, hdr) \
1678 if (msginfo->var && *msginfo->var) { \
1679 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1680 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1681 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1684 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1685 gboolean as_attach, const gchar *body,
1686 gboolean no_extedit,
1690 GtkTextView *textview;
1691 GtkTextBuffer *textbuf;
1692 gint cursor_pos = -1;
1695 cm_return_val_if_fail(msginfo != NULL, NULL);
1696 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1699 !(account = compose_guess_forward_account_from_msginfo
1701 account = cur_account;
1703 if (!prefs_common.forward_as_attachment)
1704 mode = COMPOSE_FORWARD_INLINE;
1706 mode = COMPOSE_FORWARD;
1707 compose = compose_create(account, msginfo->folder, mode, batch);
1709 compose->updating = TRUE;
1710 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1711 if (!compose->fwdinfo)
1712 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1714 compose_extract_original_charset(compose);
1716 if (msginfo->subject && *msginfo->subject) {
1717 gchar *buf, *buf2, *p;
1719 buf = p = g_strdup(msginfo->subject);
1720 p += subject_get_prefix_length(p);
1721 memmove(buf, p, strlen(p) + 1);
1723 buf2 = g_strdup_printf("Fw: %s", buf);
1724 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1730 /* override from name according to folder properties */
1731 if (msginfo->folder && msginfo->folder->prefs &&
1732 msginfo->folder->prefs->forward_with_format &&
1733 msginfo->folder->prefs->forward_override_from_format &&
1734 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1738 MsgInfo *full_msginfo = NULL;
1741 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1743 full_msginfo = procmsg_msginfo_copy(msginfo);
1745 /* decode \-escape sequences in the internal representation of the quote format */
1746 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1747 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1750 gtkaspell_block_check(compose->gtkaspell);
1751 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1752 compose->gtkaspell);
1754 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1756 quote_fmt_scan_string(tmp);
1759 buf = quote_fmt_get_buffer();
1761 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1763 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1764 quote_fmt_reset_vartable();
1767 procmsg_msginfo_free(full_msginfo);
1770 textview = GTK_TEXT_VIEW(compose->text);
1771 textbuf = gtk_text_view_get_buffer(textview);
1772 compose_create_tags(textview, compose);
1774 undo_block(compose->undostruct);
1778 msgfile = procmsg_get_message_file(msginfo);
1779 if (!is_file_exist(msgfile))
1780 g_warning("%s: file not exist\n", msgfile);
1782 compose_attach_append(compose, msgfile, msgfile,
1783 "message/rfc822", NULL);
1787 const gchar *qmark = NULL;
1788 const gchar *body_fmt = NULL;
1789 MsgInfo *full_msginfo;
1791 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1792 body_fmt = gettext(prefs_common.fw_quotefmt);
1796 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1798 full_msginfo = procmsg_msginfo_copy(msginfo);
1800 /* use the forward format of folder (if enabled), or the account's one
1801 (if enabled) or fallback to the global forward format, which is always
1802 enabled (even if empty), and use the relevant quotemark */
1803 if (msginfo->folder && msginfo->folder->prefs &&
1804 msginfo->folder->prefs->forward_with_format) {
1805 qmark = msginfo->folder->prefs->forward_quotemark;
1806 body_fmt = msginfo->folder->prefs->forward_body_format;
1808 } else if (account->forward_with_format) {
1809 qmark = account->forward_quotemark;
1810 body_fmt = account->forward_body_format;
1813 qmark = prefs_common.fw_quotemark;
1814 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1815 body_fmt = gettext(prefs_common.fw_quotefmt);
1820 /* empty quotemark is not allowed */
1821 if (qmark == NULL || *qmark == '\0')
1824 compose_quote_fmt(compose, full_msginfo,
1825 body_fmt, qmark, body, FALSE, TRUE,
1826 _("The body of the \"Forward\" template has an error at line %d."));
1827 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1828 quote_fmt_reset_vartable();
1829 compose_attach_parts(compose, msginfo);
1831 procmsg_msginfo_free(full_msginfo);
1834 SIGNAL_BLOCK(textbuf);
1836 if (account->auto_sig)
1837 compose_insert_sig(compose, FALSE);
1839 compose_wrap_all(compose);
1842 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1843 gtkaspell_highlight_all(compose->gtkaspell);
1844 gtkaspell_unblock_check(compose->gtkaspell);
1846 SIGNAL_UNBLOCK(textbuf);
1848 cursor_pos = quote_fmt_get_cursor_pos();
1849 if (cursor_pos == -1)
1850 gtk_widget_grab_focus(compose->header_last->entry);
1852 gtk_widget_grab_focus(compose->text);
1854 if (!no_extedit && prefs_common.auto_exteditor)
1855 compose_exec_ext_editor(compose);
1858 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1859 gchar *folderidentifier;
1861 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1862 folderidentifier = folder_item_get_identifier(msginfo->folder);
1863 compose_set_save_to(compose, folderidentifier);
1864 g_free(folderidentifier);
1867 undo_unblock(compose->undostruct);
1869 compose->modified = FALSE;
1870 compose_set_title(compose);
1872 compose->updating = FALSE;
1873 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1874 SCROLL_TO_CURSOR(compose);
1876 if (compose->deferred_destroy) {
1877 compose_destroy(compose);
1881 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1886 #undef INSERT_FW_HEADER
1888 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1891 GtkTextView *textview;
1892 GtkTextBuffer *textbuf;
1896 gboolean single_mail = TRUE;
1898 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1900 if (g_slist_length(msginfo_list) > 1)
1901 single_mail = FALSE;
1903 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1904 if (((MsgInfo *)msginfo->data)->folder == NULL)
1907 /* guess account from first selected message */
1909 !(account = compose_guess_forward_account_from_msginfo
1910 (msginfo_list->data)))
1911 account = cur_account;
1913 cm_return_val_if_fail(account != NULL, NULL);
1915 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1916 if (msginfo->data) {
1917 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1918 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1922 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1923 g_warning("no msginfo_list");
1927 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1929 compose->updating = TRUE;
1931 /* override from name according to folder properties */
1932 if (msginfo_list->data) {
1933 MsgInfo *msginfo = msginfo_list->data;
1935 if (msginfo->folder && msginfo->folder->prefs &&
1936 msginfo->folder->prefs->forward_with_format &&
1937 msginfo->folder->prefs->forward_override_from_format &&
1938 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1943 /* decode \-escape sequences in the internal representation of the quote format */
1944 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1945 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1948 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1949 compose->gtkaspell);
1951 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1953 quote_fmt_scan_string(tmp);
1956 buf = quote_fmt_get_buffer();
1958 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1960 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1961 quote_fmt_reset_vartable();
1967 textview = GTK_TEXT_VIEW(compose->text);
1968 textbuf = gtk_text_view_get_buffer(textview);
1969 compose_create_tags(textview, compose);
1971 undo_block(compose->undostruct);
1972 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1973 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1975 if (!is_file_exist(msgfile))
1976 g_warning("%s: file not exist\n", msgfile);
1978 compose_attach_append(compose, msgfile, msgfile,
1979 "message/rfc822", NULL);
1984 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1985 if (info->subject && *info->subject) {
1986 gchar *buf, *buf2, *p;
1988 buf = p = g_strdup(info->subject);
1989 p += subject_get_prefix_length(p);
1990 memmove(buf, p, strlen(p) + 1);
1992 buf2 = g_strdup_printf("Fw: %s", buf);
1993 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1999 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2000 _("Fw: multiple emails"));
2003 SIGNAL_BLOCK(textbuf);
2005 if (account->auto_sig)
2006 compose_insert_sig(compose, FALSE);
2008 compose_wrap_all(compose);
2010 SIGNAL_UNBLOCK(textbuf);
2012 gtk_text_buffer_get_start_iter(textbuf, &iter);
2013 gtk_text_buffer_place_cursor(textbuf, &iter);
2015 gtk_widget_grab_focus(compose->header_last->entry);
2016 undo_unblock(compose->undostruct);
2017 compose->modified = FALSE;
2018 compose_set_title(compose);
2020 compose->updating = FALSE;
2021 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2022 SCROLL_TO_CURSOR(compose);
2024 if (compose->deferred_destroy) {
2025 compose_destroy(compose);
2029 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2034 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2036 GtkTextIter start = *iter;
2037 GtkTextIter end_iter;
2038 int start_pos = gtk_text_iter_get_offset(&start);
2040 if (!compose->account->sig_sep)
2043 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2044 start_pos+strlen(compose->account->sig_sep));
2046 /* check sig separator */
2047 str = gtk_text_iter_get_text(&start, &end_iter);
2048 if (!strcmp(str, compose->account->sig_sep)) {
2050 /* check end of line (\n) */
2051 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2052 start_pos+strlen(compose->account->sig_sep));
2053 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2054 start_pos+strlen(compose->account->sig_sep)+1);
2055 tmp = gtk_text_iter_get_text(&start, &end_iter);
2056 if (!strcmp(tmp,"\n")) {
2068 static void compose_colorize_signature(Compose *compose)
2070 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2072 GtkTextIter end_iter;
2073 gtk_text_buffer_get_start_iter(buffer, &iter);
2074 while (gtk_text_iter_forward_line(&iter))
2075 if (compose_is_sig_separator(compose, buffer, &iter)) {
2076 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2077 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2081 #define BLOCK_WRAP() { \
2082 prev_autowrap = compose->autowrap; \
2083 buffer = gtk_text_view_get_buffer( \
2084 GTK_TEXT_VIEW(compose->text)); \
2085 compose->autowrap = FALSE; \
2087 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2088 G_CALLBACK(compose_changed_cb), \
2090 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2091 G_CALLBACK(text_inserted), \
2094 #define UNBLOCK_WRAP() { \
2095 compose->autowrap = prev_autowrap; \
2096 if (compose->autowrap) { \
2097 gint old = compose->draft_timeout_tag; \
2098 compose->draft_timeout_tag = -2; \
2099 compose_wrap_all(compose); \
2100 compose->draft_timeout_tag = old; \
2103 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2104 G_CALLBACK(compose_changed_cb), \
2106 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2107 G_CALLBACK(text_inserted), \
2111 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2113 Compose *compose = NULL;
2114 PrefsAccount *account = NULL;
2115 GtkTextView *textview;
2116 GtkTextBuffer *textbuf;
2120 gchar buf[BUFFSIZE];
2121 gboolean use_signing = FALSE;
2122 gboolean use_encryption = FALSE;
2123 gchar *privacy_system = NULL;
2124 int priority = PRIORITY_NORMAL;
2125 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2126 gboolean autowrap = prefs_common.autowrap;
2127 gboolean autoindent = prefs_common.auto_indent;
2129 cm_return_val_if_fail(msginfo != NULL, NULL);
2130 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2132 if (compose_put_existing_to_front(msginfo)) {
2136 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2137 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2138 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2139 gchar queueheader_buf[BUFFSIZE];
2142 /* Select Account from queue headers */
2143 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2144 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2145 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2146 account = account_find_from_id(id);
2148 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2149 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2150 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2151 account = account_find_from_id(id);
2153 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2154 sizeof(queueheader_buf), "NAID:")) {
2155 id = atoi(&queueheader_buf[strlen("NAID:")]);
2156 account = account_find_from_id(id);
2158 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2159 sizeof(queueheader_buf), "MAID:")) {
2160 id = atoi(&queueheader_buf[strlen("MAID:")]);
2161 account = account_find_from_id(id);
2163 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2164 sizeof(queueheader_buf), "S:")) {
2165 account = account_find_from_address(queueheader_buf, FALSE);
2167 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2168 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2169 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2170 use_signing = param;
2173 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2174 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2175 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2176 use_signing = param;
2179 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2180 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2181 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2182 use_encryption = param;
2184 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2185 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2186 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2187 use_encryption = param;
2189 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2190 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2191 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2194 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2195 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2196 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2199 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2200 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2201 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2203 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2204 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2205 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2207 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2208 sizeof(queueheader_buf), "X-Priority: ")) {
2209 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2212 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2213 sizeof(queueheader_buf), "RMID:")) {
2214 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2215 if (tokens[0] && tokens[1] && tokens[2]) {
2216 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2217 if (orig_item != NULL) {
2218 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2223 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2224 sizeof(queueheader_buf), "FMID:")) {
2225 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2226 if (tokens[0] && tokens[1] && tokens[2]) {
2227 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2228 if (orig_item != NULL) {
2229 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2235 account = msginfo->folder->folder->account;
2238 if (!account && prefs_common.reedit_account_autosel) {
2239 gchar from[BUFFSIZE];
2240 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2241 extract_address(from);
2242 account = account_find_from_address(from, FALSE);
2246 account = cur_account;
2248 cm_return_val_if_fail(account != NULL, NULL);
2250 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2252 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2253 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2254 compose->autowrap = autowrap;
2255 compose->replyinfo = replyinfo;
2256 compose->fwdinfo = fwdinfo;
2258 compose->updating = TRUE;
2259 compose->priority = priority;
2261 if (privacy_system != NULL) {
2262 compose->privacy_system = privacy_system;
2263 compose_use_signing(compose, use_signing);
2264 compose_use_encryption(compose, use_encryption);
2265 compose_update_privacy_system_menu_item(compose, FALSE);
2267 activate_privacy_system(compose, account, FALSE);
2270 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2272 compose_extract_original_charset(compose);
2274 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2275 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2276 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2277 gchar queueheader_buf[BUFFSIZE];
2279 /* Set message save folder */
2280 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2281 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2282 compose_set_save_to(compose, &queueheader_buf[4]);
2284 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2285 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2287 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2292 if (compose_parse_header(compose, msginfo) < 0) {
2293 compose->updating = FALSE;
2294 compose_destroy(compose);
2297 compose_reedit_set_entry(compose, msginfo);
2299 textview = GTK_TEXT_VIEW(compose->text);
2300 textbuf = gtk_text_view_get_buffer(textview);
2301 compose_create_tags(textview, compose);
2303 mark = gtk_text_buffer_get_insert(textbuf);
2304 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2306 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2307 G_CALLBACK(compose_changed_cb),
2310 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2311 fp = procmime_get_first_encrypted_text_content(msginfo);
2313 compose_force_encryption(compose, account, TRUE, NULL);
2316 fp = procmime_get_first_text_content(msginfo);
2319 g_warning("Can't get text part\n");
2323 gboolean prev_autowrap = compose->autowrap;
2324 GtkTextBuffer *buffer = textbuf;
2326 while (fgets(buf, sizeof(buf), fp) != NULL) {
2328 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2334 compose_attach_parts(compose, msginfo);
2336 compose_colorize_signature(compose);
2338 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2339 G_CALLBACK(compose_changed_cb),
2342 gtk_widget_grab_focus(compose->text);
2344 if (prefs_common.auto_exteditor) {
2345 compose_exec_ext_editor(compose);
2347 compose->modified = FALSE;
2348 compose_set_title(compose);
2350 compose->updating = FALSE;
2351 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2352 SCROLL_TO_CURSOR(compose);
2354 if (compose->deferred_destroy) {
2355 compose_destroy(compose);
2359 compose->sig_str = account_get_signature_str(compose->account);
2361 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2366 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2373 cm_return_val_if_fail(msginfo != NULL, NULL);
2376 account = account_get_reply_account(msginfo,
2377 prefs_common.reply_account_autosel);
2378 cm_return_val_if_fail(account != NULL, NULL);
2380 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2382 compose->updating = TRUE;
2384 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2385 compose->replyinfo = NULL;
2386 compose->fwdinfo = NULL;
2388 compose_show_first_last_header(compose, TRUE);
2390 gtk_widget_grab_focus(compose->header_last->entry);
2392 filename = procmsg_get_message_file(msginfo);
2394 if (filename == NULL) {
2395 compose->updating = FALSE;
2396 compose_destroy(compose);
2401 compose->redirect_filename = filename;
2403 /* Set save folder */
2404 item = msginfo->folder;
2405 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2406 gchar *folderidentifier;
2408 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2409 folderidentifier = folder_item_get_identifier(item);
2410 compose_set_save_to(compose, folderidentifier);
2411 g_free(folderidentifier);
2414 compose_attach_parts(compose, msginfo);
2416 if (msginfo->subject)
2417 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2419 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2421 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2422 _("The body of the \"Redirect\" template has an error at line %d."));
2423 quote_fmt_reset_vartable();
2424 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2426 compose_colorize_signature(compose);
2429 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2430 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2431 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2433 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2434 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2435 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2436 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2437 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2438 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2439 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2440 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2442 if (compose->toolbar->draft_btn)
2443 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2444 if (compose->toolbar->insert_btn)
2445 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2446 if (compose->toolbar->attach_btn)
2447 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2448 if (compose->toolbar->sig_btn)
2449 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2450 if (compose->toolbar->exteditor_btn)
2451 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2452 if (compose->toolbar->linewrap_current_btn)
2453 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2454 if (compose->toolbar->linewrap_all_btn)
2455 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2457 compose->modified = FALSE;
2458 compose_set_title(compose);
2459 compose->updating = FALSE;
2460 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2461 SCROLL_TO_CURSOR(compose);
2463 if (compose->deferred_destroy) {
2464 compose_destroy(compose);
2468 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2473 GList *compose_get_compose_list(void)
2475 return compose_list;
2478 void compose_entry_append(Compose *compose, const gchar *address,
2479 ComposeEntryType type, ComposePrefType pref_type)
2481 const gchar *header;
2483 gboolean in_quote = FALSE;
2484 if (!address || *address == '\0') return;
2491 header = N_("Bcc:");
2493 case COMPOSE_REPLYTO:
2494 header = N_("Reply-To:");
2496 case COMPOSE_NEWSGROUPS:
2497 header = N_("Newsgroups:");
2499 case COMPOSE_FOLLOWUPTO:
2500 header = N_( "Followup-To:");
2502 case COMPOSE_INREPLYTO:
2503 header = N_( "In-Reply-To:");
2510 header = prefs_common_translated_header_name(header);
2512 cur = begin = (gchar *)address;
2514 /* we separate the line by commas, but not if we're inside a quoted
2516 while (*cur != '\0') {
2518 in_quote = !in_quote;
2519 if (*cur == ',' && !in_quote) {
2520 gchar *tmp = g_strdup(begin);
2522 tmp[cur-begin]='\0';
2525 while (*tmp == ' ' || *tmp == '\t')
2527 compose_add_header_entry(compose, header, tmp, pref_type);
2534 gchar *tmp = g_strdup(begin);
2536 tmp[cur-begin]='\0';
2539 while (*tmp == ' ' || *tmp == '\t')
2541 compose_add_header_entry(compose, header, tmp, pref_type);
2546 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2548 #if !GTK_CHECK_VERSION(3, 0, 0)
2549 static GdkColor yellow;
2550 static GdkColor black;
2551 static gboolean yellow_initialised = FALSE;
2553 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2554 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2559 #if !GTK_CHECK_VERSION(3, 0, 0)
2560 if (!yellow_initialised) {
2561 gdk_color_parse("#f5f6be", &yellow);
2562 gdk_color_parse("#000000", &black);
2563 yellow_initialised = gdk_colormap_alloc_color(
2564 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2565 yellow_initialised &= gdk_colormap_alloc_color(
2566 gdk_colormap_get_system(), &black, FALSE, TRUE);
2570 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2571 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2572 if (gtk_entry_get_text(entry) &&
2573 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2574 #if !GTK_CHECK_VERSION(3, 0, 0)
2575 if (yellow_initialised) {
2577 gtk_widget_modify_base(
2578 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2579 GTK_STATE_NORMAL, &yellow);
2580 gtk_widget_modify_text(
2581 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2582 GTK_STATE_NORMAL, &black);
2583 #if !GTK_CHECK_VERSION(3, 0, 0)
2590 void compose_toolbar_cb(gint action, gpointer data)
2592 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2593 Compose *compose = (Compose*)toolbar_item->parent;
2595 cm_return_if_fail(compose != NULL);
2599 compose_send_cb(NULL, compose);
2602 compose_send_later_cb(NULL, compose);
2605 compose_draft(compose, COMPOSE_QUIT_EDITING);
2608 compose_insert_file_cb(NULL, compose);
2611 compose_attach_cb(NULL, compose);
2614 compose_insert_sig(compose, FALSE);
2617 compose_ext_editor_cb(NULL, compose);
2619 case A_LINEWRAP_CURRENT:
2620 compose_beautify_paragraph(compose, NULL, TRUE);
2622 case A_LINEWRAP_ALL:
2623 compose_wrap_all_full(compose, TRUE);
2626 compose_address_cb(NULL, compose);
2629 case A_CHECK_SPELLING:
2630 compose_check_all(NULL, compose);
2638 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2643 gchar *subject = NULL;
2647 gchar **attach = NULL;
2648 gchar *inreplyto = NULL;
2649 MailField mfield = NO_FIELD_PRESENT;
2651 /* get mailto parts but skip from */
2652 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2655 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2656 mfield = TO_FIELD_PRESENT;
2659 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2661 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2663 if (!g_utf8_validate (subject, -1, NULL)) {
2664 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2665 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2668 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2670 mfield = SUBJECT_FIELD_PRESENT;
2673 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2674 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2677 gboolean prev_autowrap = compose->autowrap;
2679 compose->autowrap = FALSE;
2681 mark = gtk_text_buffer_get_insert(buffer);
2682 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2684 if (!g_utf8_validate (body, -1, NULL)) {
2685 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2686 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2689 gtk_text_buffer_insert(buffer, &iter, body, -1);
2691 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2693 compose->autowrap = prev_autowrap;
2694 if (compose->autowrap)
2695 compose_wrap_all(compose);
2696 mfield = BODY_FIELD_PRESENT;
2700 gint i = 0, att = 0;
2701 gchar *warn_files = NULL;
2702 while (attach[i] != NULL) {
2703 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2704 if (utf8_filename) {
2705 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2706 gchar *tmp = g_strdup_printf("%s%s\n",
2707 warn_files?warn_files:"",
2713 g_free(utf8_filename);
2715 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2720 alertpanel_notice(ngettext(
2721 "The following file has been attached: \n%s",
2722 "The following files have been attached: \n%s", att), warn_files);
2727 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2740 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2742 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2743 {"Cc:", NULL, TRUE},
2744 {"References:", NULL, FALSE},
2745 {"Bcc:", NULL, TRUE},
2746 {"Newsgroups:", NULL, TRUE},
2747 {"Followup-To:", NULL, TRUE},
2748 {"List-Post:", NULL, FALSE},
2749 {"X-Priority:", NULL, FALSE},
2750 {NULL, NULL, FALSE}};
2766 cm_return_val_if_fail(msginfo != NULL, -1);
2768 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2769 procheader_get_header_fields(fp, hentry);
2772 if (hentry[H_REPLY_TO].body != NULL) {
2773 if (hentry[H_REPLY_TO].body[0] != '\0') {
2775 conv_unmime_header(hentry[H_REPLY_TO].body,
2778 g_free(hentry[H_REPLY_TO].body);
2779 hentry[H_REPLY_TO].body = NULL;
2781 if (hentry[H_CC].body != NULL) {
2782 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2783 g_free(hentry[H_CC].body);
2784 hentry[H_CC].body = NULL;
2786 if (hentry[H_REFERENCES].body != NULL) {
2787 if (compose->mode == COMPOSE_REEDIT)
2788 compose->references = hentry[H_REFERENCES].body;
2790 compose->references = compose_parse_references
2791 (hentry[H_REFERENCES].body, msginfo->msgid);
2792 g_free(hentry[H_REFERENCES].body);
2794 hentry[H_REFERENCES].body = NULL;
2796 if (hentry[H_BCC].body != NULL) {
2797 if (compose->mode == COMPOSE_REEDIT)
2799 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2800 g_free(hentry[H_BCC].body);
2801 hentry[H_BCC].body = NULL;
2803 if (hentry[H_NEWSGROUPS].body != NULL) {
2804 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2805 hentry[H_NEWSGROUPS].body = NULL;
2807 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2808 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2809 compose->followup_to =
2810 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2813 g_free(hentry[H_FOLLOWUP_TO].body);
2814 hentry[H_FOLLOWUP_TO].body = NULL;
2816 if (hentry[H_LIST_POST].body != NULL) {
2817 gchar *to = NULL, *start = NULL;
2819 extract_address(hentry[H_LIST_POST].body);
2820 if (hentry[H_LIST_POST].body[0] != '\0') {
2821 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2823 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2824 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2827 g_free(compose->ml_post);
2828 compose->ml_post = to;
2831 g_free(hentry[H_LIST_POST].body);
2832 hentry[H_LIST_POST].body = NULL;
2835 /* CLAWS - X-Priority */
2836 if (compose->mode == COMPOSE_REEDIT)
2837 if (hentry[H_X_PRIORITY].body != NULL) {
2840 priority = atoi(hentry[H_X_PRIORITY].body);
2841 g_free(hentry[H_X_PRIORITY].body);
2843 hentry[H_X_PRIORITY].body = NULL;
2845 if (priority < PRIORITY_HIGHEST ||
2846 priority > PRIORITY_LOWEST)
2847 priority = PRIORITY_NORMAL;
2849 compose->priority = priority;
2852 if (compose->mode == COMPOSE_REEDIT) {
2853 if (msginfo->inreplyto && *msginfo->inreplyto)
2854 compose->inreplyto = g_strdup(msginfo->inreplyto);
2858 if (msginfo->msgid && *msginfo->msgid)
2859 compose->inreplyto = g_strdup(msginfo->msgid);
2861 if (!compose->references) {
2862 if (msginfo->msgid && *msginfo->msgid) {
2863 if (msginfo->inreplyto && *msginfo->inreplyto)
2864 compose->references =
2865 g_strdup_printf("<%s>\n\t<%s>",
2869 compose->references =
2870 g_strconcat("<", msginfo->msgid, ">",
2872 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2873 compose->references =
2874 g_strconcat("<", msginfo->inreplyto, ">",
2882 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2884 GSList *ref_id_list, *cur;
2888 ref_id_list = references_list_append(NULL, ref);
2889 if (!ref_id_list) return NULL;
2890 if (msgid && *msgid)
2891 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2896 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2897 /* "<" + Message-ID + ">" + CR+LF+TAB */
2898 len += strlen((gchar *)cur->data) + 5;
2900 if (len > MAX_REFERENCES_LEN) {
2901 /* remove second message-ID */
2902 if (ref_id_list && ref_id_list->next &&
2903 ref_id_list->next->next) {
2904 g_free(ref_id_list->next->data);
2905 ref_id_list = g_slist_remove
2906 (ref_id_list, ref_id_list->next->data);
2908 slist_free_strings(ref_id_list);
2909 g_slist_free(ref_id_list);
2916 new_ref = g_string_new("");
2917 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2918 if (new_ref->len > 0)
2919 g_string_append(new_ref, "\n\t");
2920 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2923 slist_free_strings(ref_id_list);
2924 g_slist_free(ref_id_list);
2926 new_ref_str = new_ref->str;
2927 g_string_free(new_ref, FALSE);
2932 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2933 const gchar *fmt, const gchar *qmark,
2934 const gchar *body, gboolean rewrap,
2935 gboolean need_unescape,
2936 const gchar *err_msg)
2938 MsgInfo* dummyinfo = NULL;
2939 gchar *quote_str = NULL;
2941 gboolean prev_autowrap;
2942 const gchar *trimmed_body = body;
2943 gint cursor_pos = -1;
2944 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2945 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2950 SIGNAL_BLOCK(buffer);
2953 dummyinfo = compose_msginfo_new_from_compose(compose);
2954 msginfo = dummyinfo;
2957 if (qmark != NULL) {
2959 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2960 compose->gtkaspell);
2962 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2964 quote_fmt_scan_string(qmark);
2967 buf = quote_fmt_get_buffer();
2969 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2971 Xstrdup_a(quote_str, buf, goto error)
2974 if (fmt && *fmt != '\0') {
2977 while (*trimmed_body == '\n')
2981 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2982 compose->gtkaspell);
2984 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2986 if (need_unescape) {
2989 /* decode \-escape sequences in the internal representation of the quote format */
2990 tmp = g_malloc(strlen(fmt)+1);
2991 pref_get_unescaped_pref(tmp, fmt);
2992 quote_fmt_scan_string(tmp);
2996 quote_fmt_scan_string(fmt);
3000 buf = quote_fmt_get_buffer();
3002 gint line = quote_fmt_get_line();
3003 alertpanel_error(err_msg, line);
3009 prev_autowrap = compose->autowrap;
3010 compose->autowrap = FALSE;
3012 mark = gtk_text_buffer_get_insert(buffer);
3013 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3014 if (g_utf8_validate(buf, -1, NULL)) {
3015 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3017 gchar *tmpout = NULL;
3018 tmpout = conv_codeset_strdup
3019 (buf, conv_get_locale_charset_str_no_utf8(),
3021 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3023 tmpout = g_malloc(strlen(buf)*2+1);
3024 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3026 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3030 cursor_pos = quote_fmt_get_cursor_pos();
3031 if (cursor_pos == -1)
3032 cursor_pos = gtk_text_iter_get_offset(&iter);
3033 compose->set_cursor_pos = cursor_pos;
3035 gtk_text_buffer_get_start_iter(buffer, &iter);
3036 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3037 gtk_text_buffer_place_cursor(buffer, &iter);
3039 compose->autowrap = prev_autowrap;
3040 if (compose->autowrap && rewrap)
3041 compose_wrap_all(compose);
3048 SIGNAL_UNBLOCK(buffer);
3050 procmsg_msginfo_free( dummyinfo );
3055 /* if ml_post is of type addr@host and from is of type
3056 * addr-anything@host, return TRUE
3058 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3060 gchar *left_ml = NULL;
3061 gchar *right_ml = NULL;
3062 gchar *left_from = NULL;
3063 gchar *right_from = NULL;
3064 gboolean result = FALSE;
3066 if (!ml_post || !from)
3069 left_ml = g_strdup(ml_post);
3070 if (strstr(left_ml, "@")) {
3071 right_ml = strstr(left_ml, "@")+1;
3072 *(strstr(left_ml, "@")) = '\0';
3075 left_from = g_strdup(from);
3076 if (strstr(left_from, "@")) {
3077 right_from = strstr(left_from, "@")+1;
3078 *(strstr(left_from, "@")) = '\0';
3081 if (left_ml && left_from && right_ml && right_from
3082 && !strncmp(left_from, left_ml, strlen(left_ml))
3083 && !strcmp(right_from, right_ml)) {
3092 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3093 gboolean respect_default_to)
3097 if (!folder || !folder->prefs)
3100 if (respect_default_to && folder->prefs->enable_default_to) {
3101 compose_entry_append(compose, folder->prefs->default_to,
3102 COMPOSE_TO, PREF_FOLDER);
3103 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3105 if (folder->prefs->enable_default_cc)
3106 compose_entry_append(compose, folder->prefs->default_cc,
3107 COMPOSE_CC, PREF_FOLDER);
3108 if (folder->prefs->enable_default_bcc)
3109 compose_entry_append(compose, folder->prefs->default_bcc,
3110 COMPOSE_BCC, PREF_FOLDER);
3111 if (folder->prefs->enable_default_replyto)
3112 compose_entry_append(compose, folder->prefs->default_replyto,
3113 COMPOSE_REPLYTO, PREF_FOLDER);
3116 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3121 if (!compose || !msginfo)
3124 if (msginfo->subject && *msginfo->subject) {
3125 buf = p = g_strdup(msginfo->subject);
3126 p += subject_get_prefix_length(p);
3127 memmove(buf, p, strlen(p) + 1);
3129 buf2 = g_strdup_printf("Re: %s", buf);
3130 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3135 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3138 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3139 gboolean to_all, gboolean to_ml,
3141 gboolean followup_and_reply_to)
3143 GSList *cc_list = NULL;
3146 gchar *replyto = NULL;
3147 gchar *ac_email = NULL;
3149 gboolean reply_to_ml = FALSE;
3150 gboolean default_reply_to = FALSE;
3152 cm_return_if_fail(compose->account != NULL);
3153 cm_return_if_fail(msginfo != NULL);
3155 reply_to_ml = to_ml && compose->ml_post;
3157 default_reply_to = msginfo->folder &&
3158 msginfo->folder->prefs->enable_default_reply_to;
3160 if (compose->account->protocol != A_NNTP) {
3161 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3163 if (reply_to_ml && !default_reply_to) {
3165 gboolean is_subscr = is_subscription(compose->ml_post,
3168 /* normal answer to ml post with a reply-to */
3169 compose_entry_append(compose,
3171 COMPOSE_TO, PREF_ML);
3172 if (compose->replyto)
3173 compose_entry_append(compose,
3175 COMPOSE_CC, PREF_ML);
3177 /* answer to subscription confirmation */
3178 if (compose->replyto)
3179 compose_entry_append(compose,
3181 COMPOSE_TO, PREF_ML);
3182 else if (msginfo->from)
3183 compose_entry_append(compose,
3185 COMPOSE_TO, PREF_ML);
3188 else if (!(to_all || to_sender) && default_reply_to) {
3189 compose_entry_append(compose,
3190 msginfo->folder->prefs->default_reply_to,
3191 COMPOSE_TO, PREF_FOLDER);
3192 compose_entry_mark_default_to(compose,
3193 msginfo->folder->prefs->default_reply_to);
3198 Xstrdup_a(tmp1, msginfo->from, return);
3199 extract_address(tmp1);
3200 if (to_all || to_sender ||
3201 !account_find_from_address(tmp1, FALSE))
3202 compose_entry_append(compose,
3203 (compose->replyto && !to_sender)
3204 ? compose->replyto :
3205 msginfo->from ? msginfo->from : "",
3206 COMPOSE_TO, PREF_NONE);
3207 else if (!to_all && !to_sender) {
3208 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3209 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3210 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3211 if (compose->replyto) {
3212 compose_entry_append(compose,
3214 COMPOSE_TO, PREF_NONE);
3216 compose_entry_append(compose,
3217 msginfo->from ? msginfo->from : "",
3218 COMPOSE_TO, PREF_NONE);
3221 /* replying to own mail, use original recp */
3222 compose_entry_append(compose,
3223 msginfo->to ? msginfo->to : "",
3224 COMPOSE_TO, PREF_NONE);
3225 compose_entry_append(compose,
3226 msginfo->cc ? msginfo->cc : "",
3227 COMPOSE_CC, PREF_NONE);
3232 if (to_sender || (compose->followup_to &&
3233 !strncmp(compose->followup_to, "poster", 6)))
3234 compose_entry_append
3236 (compose->replyto ? compose->replyto :
3237 msginfo->from ? msginfo->from : ""),
3238 COMPOSE_TO, PREF_NONE);
3240 else if (followup_and_reply_to || to_all) {
3241 compose_entry_append
3243 (compose->replyto ? compose->replyto :
3244 msginfo->from ? msginfo->from : ""),
3245 COMPOSE_TO, PREF_NONE);
3247 compose_entry_append
3249 compose->followup_to ? compose->followup_to :
3250 compose->newsgroups ? compose->newsgroups : "",
3251 COMPOSE_NEWSGROUPS, PREF_NONE);
3254 compose_entry_append
3256 compose->followup_to ? compose->followup_to :
3257 compose->newsgroups ? compose->newsgroups : "",
3258 COMPOSE_NEWSGROUPS, PREF_NONE);
3260 compose_reply_set_subject(compose, msginfo);
3262 if (to_ml && compose->ml_post) return;
3263 if (!to_all || compose->account->protocol == A_NNTP) return;
3265 if (compose->replyto) {
3266 Xstrdup_a(replyto, compose->replyto, return);
3267 extract_address(replyto);
3269 if (msginfo->from) {
3270 Xstrdup_a(from, msginfo->from, return);
3271 extract_address(from);
3274 if (replyto && from)
3275 cc_list = address_list_append_with_comments(cc_list, from);
3276 if (to_all && msginfo->folder &&
3277 msginfo->folder->prefs->enable_default_reply_to)
3278 cc_list = address_list_append_with_comments(cc_list,
3279 msginfo->folder->prefs->default_reply_to);
3280 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3281 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3283 ac_email = g_utf8_strdown(compose->account->address, -1);
3286 for (cur = cc_list; cur != NULL; cur = cur->next) {
3287 gchar *addr = g_utf8_strdown(cur->data, -1);
3288 extract_address(addr);
3290 if (strcmp(ac_email, addr))
3291 compose_entry_append(compose, (gchar *)cur->data,
3292 COMPOSE_CC, PREF_NONE);
3294 debug_print("Cc address same as compose account's, ignoring\n");
3299 slist_free_strings(cc_list);
3300 g_slist_free(cc_list);
3306 #define SET_ENTRY(entry, str) \
3309 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3312 #define SET_ADDRESS(type, str) \
3315 compose_entry_append(compose, str, type, PREF_NONE); \
3318 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3320 cm_return_if_fail(msginfo != NULL);
3322 SET_ENTRY(subject_entry, msginfo->subject);
3323 SET_ENTRY(from_name, msginfo->from);
3324 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3325 SET_ADDRESS(COMPOSE_CC, compose->cc);
3326 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3327 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3328 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3329 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3331 compose_update_priority_menu_item(compose);
3332 compose_update_privacy_system_menu_item(compose, FALSE);
3333 compose_show_first_last_header(compose, TRUE);
3339 static void compose_insert_sig(Compose *compose, gboolean replace)
3341 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3342 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3344 GtkTextIter iter, iter_end;
3345 gint cur_pos, ins_pos;
3346 gboolean prev_autowrap;
3347 gboolean found = FALSE;
3348 gboolean exists = FALSE;
3350 cm_return_if_fail(compose->account != NULL);
3354 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3355 G_CALLBACK(compose_changed_cb),
3358 mark = gtk_text_buffer_get_insert(buffer);
3359 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3360 cur_pos = gtk_text_iter_get_offset (&iter);
3363 gtk_text_buffer_get_end_iter(buffer, &iter);
3365 exists = (compose->sig_str != NULL);
3368 GtkTextIter first_iter, start_iter, end_iter;
3370 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3372 if (!exists || compose->sig_str[0] == '\0')
3375 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3376 compose->signature_tag);
3379 /* include previous \n\n */
3380 gtk_text_iter_backward_chars(&first_iter, 1);
3381 start_iter = first_iter;
3382 end_iter = first_iter;
3384 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3385 compose->signature_tag);
3386 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3387 compose->signature_tag);
3389 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3395 g_free(compose->sig_str);
3396 compose->sig_str = account_get_signature_str(compose->account);
3398 cur_pos = gtk_text_iter_get_offset(&iter);
3400 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3401 g_free(compose->sig_str);
3402 compose->sig_str = NULL;
3404 if (compose->sig_inserted == FALSE)
3405 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3406 compose->sig_inserted = TRUE;
3408 cur_pos = gtk_text_iter_get_offset(&iter);
3409 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3411 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3412 gtk_text_iter_forward_chars(&iter, 1);
3413 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3414 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3416 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3417 cur_pos = gtk_text_buffer_get_char_count (buffer);
3420 /* put the cursor where it should be
3421 * either where the quote_fmt says, either where it was */
3422 if (compose->set_cursor_pos < 0)
3423 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3425 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3426 compose->set_cursor_pos);
3428 compose->set_cursor_pos = -1;
3429 gtk_text_buffer_place_cursor(buffer, &iter);
3430 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3431 G_CALLBACK(compose_changed_cb),
3437 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3440 GtkTextBuffer *buffer;
3443 const gchar *cur_encoding;
3444 gchar buf[BUFFSIZE];
3447 gboolean prev_autowrap;
3448 gboolean badtxt = FALSE;
3449 struct stat file_stat;
3452 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3454 /* get the size of the file we are about to insert */
3455 ret = g_stat(file, &file_stat);
3457 gchar *shortfile = g_path_get_basename(file);
3458 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3460 return COMPOSE_INSERT_NO_FILE;
3461 } else if (prefs_common.warn_large_insert == TRUE) {
3463 /* ask user for confirmation if the file is large */
3464 if (prefs_common.warn_large_insert_size < 0 ||
3465 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3469 msg = g_strdup_printf(_("You are about to insert a file of %s "
3470 "in the message body. Are you sure you want to do that?"),
3471 to_human_readable(file_stat.st_size));
3472 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3473 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3476 /* do we ask for confirmation next time? */
3477 if (aval & G_ALERTDISABLE) {
3478 /* no confirmation next time, disable feature in preferences */
3479 aval &= ~G_ALERTDISABLE;
3480 prefs_common.warn_large_insert = FALSE;
3483 /* abort file insertion if user canceled action */
3484 if (aval != G_ALERTALTERNATE) {
3485 return COMPOSE_INSERT_NO_FILE;
3491 if ((fp = g_fopen(file, "rb")) == NULL) {
3492 FILE_OP_ERROR(file, "fopen");
3493 return COMPOSE_INSERT_READ_ERROR;
3496 prev_autowrap = compose->autowrap;
3497 compose->autowrap = FALSE;
3499 text = GTK_TEXT_VIEW(compose->text);
3500 buffer = gtk_text_view_get_buffer(text);
3501 mark = gtk_text_buffer_get_insert(buffer);
3502 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3504 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3505 G_CALLBACK(text_inserted),
3508 cur_encoding = conv_get_locale_charset_str_no_utf8();
3510 while (fgets(buf, sizeof(buf), fp) != NULL) {
3513 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3514 str = g_strdup(buf);
3516 str = conv_codeset_strdup
3517 (buf, cur_encoding, CS_INTERNAL);
3520 /* strip <CR> if DOS/Windows file,
3521 replace <CR> with <LF> if Macintosh file. */
3524 if (len > 0 && str[len - 1] != '\n') {
3526 if (str[len] == '\r') str[len] = '\n';
3529 gtk_text_buffer_insert(buffer, &iter, str, -1);
3533 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3534 G_CALLBACK(text_inserted),
3536 compose->autowrap = prev_autowrap;
3537 if (compose->autowrap)
3538 compose_wrap_all(compose);
3543 return COMPOSE_INSERT_INVALID_CHARACTER;
3545 return COMPOSE_INSERT_SUCCESS;
3548 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3549 const gchar *filename,
3550 const gchar *content_type,
3551 const gchar *charset)
3559 GtkListStore *store;
3561 gboolean has_binary = FALSE;
3563 if (!is_file_exist(file)) {
3564 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3565 gboolean result = FALSE;
3566 if (file_from_uri && is_file_exist(file_from_uri)) {
3567 result = compose_attach_append(
3568 compose, file_from_uri,
3569 filename, content_type,
3572 g_free(file_from_uri);
3575 alertpanel_error("File %s doesn't exist\n", filename);
3578 if ((size = get_file_size(file)) < 0) {
3579 alertpanel_error("Can't get file size of %s\n", filename);
3583 alertpanel_error(_("File %s is empty."), filename);
3586 if ((fp = g_fopen(file, "rb")) == NULL) {
3587 alertpanel_error(_("Can't read %s."), filename);
3592 ainfo = g_new0(AttachInfo, 1);
3593 auto_ainfo = g_auto_pointer_new_with_free
3594 (ainfo, (GFreeFunc) compose_attach_info_free);
3595 ainfo->file = g_strdup(file);
3598 ainfo->content_type = g_strdup(content_type);
3599 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3601 MsgFlags flags = {0, 0};
3603 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3604 ainfo->encoding = ENC_7BIT;
3606 ainfo->encoding = ENC_8BIT;
3608 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3609 if (msginfo && msginfo->subject)
3610 name = g_strdup(msginfo->subject);
3612 name = g_path_get_basename(filename ? filename : file);
3614 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3616 procmsg_msginfo_free(msginfo);
3618 if (!g_ascii_strncasecmp(content_type, "text/", 5)) {
3619 ainfo->charset = g_strdup(charset);
3620 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3622 ainfo->encoding = ENC_BASE64;
3624 name = g_path_get_basename(filename ? filename : file);
3625 ainfo->name = g_strdup(name);
3629 ainfo->content_type = procmime_get_mime_type(file);
3630 if (!ainfo->content_type) {
3631 ainfo->content_type =
3632 g_strdup("application/octet-stream");
3633 ainfo->encoding = ENC_BASE64;
3634 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text/", 5))
3636 procmime_get_encoding_for_text_file(file, &has_binary);
3638 ainfo->encoding = ENC_BASE64;
3639 name = g_path_get_basename(filename ? filename : file);
3640 ainfo->name = g_strdup(name);
3644 if (ainfo->name != NULL
3645 && !strcmp(ainfo->name, ".")) {
3646 g_free(ainfo->name);
3650 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3651 g_free(ainfo->content_type);
3652 ainfo->content_type = g_strdup("application/octet-stream");
3653 g_free(ainfo->charset);
3654 ainfo->charset = NULL;
3657 ainfo->size = (goffset)size;
3658 size_text = to_human_readable((goffset)size);
3660 store = GTK_LIST_STORE(gtk_tree_view_get_model
3661 (GTK_TREE_VIEW(compose->attach_clist)));
3663 gtk_list_store_append(store, &iter);
3664 gtk_list_store_set(store, &iter,
3665 COL_MIMETYPE, ainfo->content_type,
3666 COL_SIZE, size_text,
3667 COL_NAME, ainfo->name,
3668 COL_CHARSET, ainfo->charset,
3670 COL_AUTODATA, auto_ainfo,
3673 g_auto_pointer_free(auto_ainfo);
3674 compose_attach_update_label(compose);
3678 static void compose_use_signing(Compose *compose, gboolean use_signing)
3680 compose->use_signing = use_signing;
3681 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3684 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3686 compose->use_encryption = use_encryption;
3687 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3690 #define NEXT_PART_NOT_CHILD(info) \
3692 node = info->node; \
3693 while (node->children) \
3694 node = g_node_last_child(node); \
3695 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3698 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3702 MimeInfo *firsttext = NULL;
3703 MimeInfo *encrypted = NULL;
3706 const gchar *partname = NULL;
3708 mimeinfo = procmime_scan_message(msginfo);
3709 if (!mimeinfo) return;
3711 if (mimeinfo->node->children == NULL) {
3712 procmime_mimeinfo_free_all(mimeinfo);
3716 /* find first content part */
3717 child = (MimeInfo *) mimeinfo->node->children->data;
3718 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3719 child = (MimeInfo *)child->node->children->data;
3722 if (child->type == MIMETYPE_TEXT) {
3724 debug_print("First text part found\n");
3725 } else if (compose->mode == COMPOSE_REEDIT &&
3726 child->type == MIMETYPE_APPLICATION &&
3727 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3728 encrypted = (MimeInfo *)child->node->parent->data;
3731 child = (MimeInfo *) mimeinfo->node->children->data;
3732 while (child != NULL) {
3735 if (child == encrypted) {
3736 /* skip this part of tree */
3737 NEXT_PART_NOT_CHILD(child);
3741 if (child->type == MIMETYPE_MULTIPART) {
3742 /* get the actual content */
3743 child = procmime_mimeinfo_next(child);
3747 if (child == firsttext) {
3748 child = procmime_mimeinfo_next(child);
3752 outfile = procmime_get_tmp_file_name(child);
3753 if ((err = procmime_get_part(outfile, child)) < 0)
3754 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3756 gchar *content_type;
3758 content_type = procmime_get_content_type_str(child->type, child->subtype);
3760 /* if we meet a pgp signature, we don't attach it, but
3761 * we force signing. */
3762 if ((strcmp(content_type, "application/pgp-signature") &&
3763 strcmp(content_type, "application/pkcs7-signature") &&
3764 strcmp(content_type, "application/x-pkcs7-signature"))
3765 || compose->mode == COMPOSE_REDIRECT) {
3766 partname = procmime_mimeinfo_get_parameter(child, "filename");
3767 if (partname == NULL)
3768 partname = procmime_mimeinfo_get_parameter(child, "name");
3769 if (partname == NULL)
3771 compose_attach_append(compose, outfile,
3772 partname, content_type,
3773 procmime_mimeinfo_get_parameter(child, "charset"));
3775 compose_force_signing(compose, compose->account, NULL);
3777 g_free(content_type);
3780 NEXT_PART_NOT_CHILD(child);
3782 procmime_mimeinfo_free_all(mimeinfo);
3785 #undef NEXT_PART_NOT_CHILD
3790 WAIT_FOR_INDENT_CHAR,
3791 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3794 /* return indent length, we allow:
3795 indent characters followed by indent characters or spaces/tabs,
3796 alphabets and numbers immediately followed by indent characters,
3797 and the repeating sequences of the above
3798 If quote ends with multiple spaces, only the first one is included. */
3799 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3800 const GtkTextIter *start, gint *len)
3802 GtkTextIter iter = *start;
3806 IndentState state = WAIT_FOR_INDENT_CHAR;
3809 gint alnum_count = 0;
3810 gint space_count = 0;
3813 if (prefs_common.quote_chars == NULL) {
3817 while (!gtk_text_iter_ends_line(&iter)) {
3818 wc = gtk_text_iter_get_char(&iter);
3819 if (g_unichar_iswide(wc))
3821 clen = g_unichar_to_utf8(wc, ch);
3825 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3826 is_space = g_unichar_isspace(wc);
3828 if (state == WAIT_FOR_INDENT_CHAR) {
3829 if (!is_indent && !g_unichar_isalnum(wc))
3832 quote_len += alnum_count + space_count + 1;
3833 alnum_count = space_count = 0;
3834 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3837 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3838 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3842 else if (is_indent) {
3843 quote_len += alnum_count + space_count + 1;
3844 alnum_count = space_count = 0;
3847 state = WAIT_FOR_INDENT_CHAR;
3851 gtk_text_iter_forward_char(&iter);
3854 if (quote_len > 0 && space_count > 0)
3860 if (quote_len > 0) {
3862 gtk_text_iter_forward_chars(&iter, quote_len);
3863 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3869 /* return >0 if the line is itemized */
3870 static int compose_itemized_length(GtkTextBuffer *buffer,
3871 const GtkTextIter *start)
3873 GtkTextIter iter = *start;
3878 if (gtk_text_iter_ends_line(&iter))
3883 wc = gtk_text_iter_get_char(&iter);
3884 if (!g_unichar_isspace(wc))
3886 gtk_text_iter_forward_char(&iter);
3887 if (gtk_text_iter_ends_line(&iter))
3891 clen = g_unichar_to_utf8(wc, ch);
3895 if (!strchr("*-+", ch[0]))
3898 gtk_text_iter_forward_char(&iter);
3899 if (gtk_text_iter_ends_line(&iter))
3901 wc = gtk_text_iter_get_char(&iter);
3902 if (g_unichar_isspace(wc)) {
3908 /* return the string at the start of the itemization */
3909 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3910 const GtkTextIter *start)
3912 GtkTextIter iter = *start;
3915 GString *item_chars = g_string_new("");
3918 if (gtk_text_iter_ends_line(&iter))
3923 wc = gtk_text_iter_get_char(&iter);
3924 if (!g_unichar_isspace(wc))
3926 gtk_text_iter_forward_char(&iter);
3927 if (gtk_text_iter_ends_line(&iter))
3929 g_string_append_unichar(item_chars, wc);
3932 str = item_chars->str;
3933 g_string_free(item_chars, FALSE);
3937 /* return the number of spaces at a line's start */
3938 static int compose_left_offset_length(GtkTextBuffer *buffer,
3939 const GtkTextIter *start)
3941 GtkTextIter iter = *start;
3944 if (gtk_text_iter_ends_line(&iter))
3948 wc = gtk_text_iter_get_char(&iter);
3949 if (!g_unichar_isspace(wc))
3952 gtk_text_iter_forward_char(&iter);
3953 if (gtk_text_iter_ends_line(&iter))
3957 gtk_text_iter_forward_char(&iter);
3958 if (gtk_text_iter_ends_line(&iter))
3963 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3964 const GtkTextIter *start,
3965 GtkTextIter *break_pos,
3969 GtkTextIter iter = *start, line_end = *start;
3970 PangoLogAttr *attrs;
3977 gboolean can_break = FALSE;
3978 gboolean do_break = FALSE;
3979 gboolean was_white = FALSE;
3980 gboolean prev_dont_break = FALSE;
3982 gtk_text_iter_forward_to_line_end(&line_end);
3983 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3984 len = g_utf8_strlen(str, -1);
3988 g_warning("compose_get_line_break_pos: len = 0!\n");
3992 /* g_print("breaking line: %d: %s (len = %d)\n",
3993 gtk_text_iter_get_line(&iter), str, len); */
3995 attrs = g_new(PangoLogAttr, len + 1);
3997 pango_default_break(str, -1, NULL, attrs, len + 1);
4001 /* skip quote and leading spaces */
4002 for (i = 0; *p != '\0' && i < len; i++) {
4005 wc = g_utf8_get_char(p);
4006 if (i >= quote_len && !g_unichar_isspace(wc))
4008 if (g_unichar_iswide(wc))
4010 else if (*p == '\t')
4014 p = g_utf8_next_char(p);
4017 for (; *p != '\0' && i < len; i++) {
4018 PangoLogAttr *attr = attrs + i;
4022 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
4025 was_white = attr->is_white;
4027 /* don't wrap URI */
4028 if ((uri_len = get_uri_len(p)) > 0) {
4030 if (pos > 0 && col > max_col) {
4040 wc = g_utf8_get_char(p);
4041 if (g_unichar_iswide(wc)) {
4043 if (prev_dont_break && can_break && attr->is_line_break)
4045 } else if (*p == '\t')
4049 if (pos > 0 && col > max_col) {
4054 if (*p == '-' || *p == '/')
4055 prev_dont_break = TRUE;
4057 prev_dont_break = FALSE;
4059 p = g_utf8_next_char(p);
4063 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4068 *break_pos = *start;
4069 gtk_text_iter_set_line_offset(break_pos, pos);
4074 static gboolean compose_join_next_line(Compose *compose,
4075 GtkTextBuffer *buffer,
4077 const gchar *quote_str)
4079 GtkTextIter iter_ = *iter, cur, prev, next, end;
4080 PangoLogAttr attrs[3];
4082 gchar *next_quote_str;
4085 gboolean keep_cursor = FALSE;
4087 if (!gtk_text_iter_forward_line(&iter_) ||
4088 gtk_text_iter_ends_line(&iter_)) {
4091 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4093 if ((quote_str || next_quote_str) &&
4094 strcmp2(quote_str, next_quote_str) != 0) {
4095 g_free(next_quote_str);
4098 g_free(next_quote_str);
4101 if (quote_len > 0) {
4102 gtk_text_iter_forward_chars(&end, quote_len);
4103 if (gtk_text_iter_ends_line(&end)) {
4108 /* don't join itemized lines */
4109 if (compose_itemized_length(buffer, &end) > 0) {
4113 /* don't join signature separator */
4114 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4117 /* delete quote str */
4119 gtk_text_buffer_delete(buffer, &iter_, &end);
4121 /* don't join line breaks put by the user */
4123 gtk_text_iter_backward_char(&cur);
4124 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4125 gtk_text_iter_forward_char(&cur);
4129 gtk_text_iter_forward_char(&cur);
4130 /* delete linebreak and extra spaces */
4131 while (gtk_text_iter_backward_char(&cur)) {
4132 wc1 = gtk_text_iter_get_char(&cur);
4133 if (!g_unichar_isspace(wc1))
4138 while (!gtk_text_iter_ends_line(&cur)) {
4139 wc1 = gtk_text_iter_get_char(&cur);
4140 if (!g_unichar_isspace(wc1))
4142 gtk_text_iter_forward_char(&cur);
4145 if (!gtk_text_iter_equal(&prev, &next)) {
4148 mark = gtk_text_buffer_get_insert(buffer);
4149 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4150 if (gtk_text_iter_equal(&prev, &cur))
4152 gtk_text_buffer_delete(buffer, &prev, &next);
4156 /* insert space if required */
4157 gtk_text_iter_backward_char(&prev);
4158 wc1 = gtk_text_iter_get_char(&prev);
4159 wc2 = gtk_text_iter_get_char(&next);
4160 gtk_text_iter_forward_char(&next);
4161 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4162 pango_default_break(str, -1, NULL, attrs, 3);
4163 if (!attrs[1].is_line_break ||
4164 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4165 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4167 gtk_text_iter_backward_char(&iter_);
4168 gtk_text_buffer_place_cursor(buffer, &iter_);
4177 #define ADD_TXT_POS(bp_, ep_, pti_) \
4178 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4179 last = last->next; \
4180 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4181 last->next = NULL; \
4183 g_warning("alloc error scanning URIs\n"); \
4186 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4188 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4189 GtkTextBuffer *buffer;
4190 GtkTextIter iter, break_pos, end_of_line;
4191 gchar *quote_str = NULL;
4193 gboolean wrap_quote = prefs_common.linewrap_quote;
4194 gboolean prev_autowrap = compose->autowrap;
4195 gint startq_offset = -1, noq_offset = -1;
4196 gint uri_start = -1, uri_stop = -1;
4197 gint nouri_start = -1, nouri_stop = -1;
4198 gint num_blocks = 0;
4199 gint quotelevel = -1;
4200 gboolean modified = force;
4201 gboolean removed = FALSE;
4202 gboolean modified_before_remove = FALSE;
4204 gboolean start = TRUE;
4205 gint itemized_len = 0, rem_item_len = 0;
4206 gchar *itemized_chars = NULL;
4207 gboolean item_continuation = FALSE;
4212 if (compose->draft_timeout_tag == -2) {
4216 compose->autowrap = FALSE;
4218 buffer = gtk_text_view_get_buffer(text);
4219 undo_wrapping(compose->undostruct, TRUE);
4224 mark = gtk_text_buffer_get_insert(buffer);
4225 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4229 if (compose->draft_timeout_tag == -2) {
4230 if (gtk_text_iter_ends_line(&iter)) {
4231 while (gtk_text_iter_ends_line(&iter) &&
4232 gtk_text_iter_forward_line(&iter))
4235 while (gtk_text_iter_backward_line(&iter)) {
4236 if (gtk_text_iter_ends_line(&iter)) {
4237 gtk_text_iter_forward_line(&iter);
4243 /* move to line start */
4244 gtk_text_iter_set_line_offset(&iter, 0);
4247 itemized_len = compose_itemized_length(buffer, &iter);
4249 if (!itemized_len) {
4250 itemized_len = compose_left_offset_length(buffer, &iter);
4251 item_continuation = TRUE;
4255 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4257 /* go until paragraph end (empty line) */
4258 while (start || !gtk_text_iter_ends_line(&iter)) {
4259 gchar *scanpos = NULL;
4260 /* parse table - in order of priority */
4262 const gchar *needle; /* token */
4264 /* token search function */
4265 gchar *(*search) (const gchar *haystack,
4266 const gchar *needle);
4267 /* part parsing function */
4268 gboolean (*parse) (const gchar *start,
4269 const gchar *scanpos,
4273 /* part to URI function */
4274 gchar *(*build_uri) (const gchar *bp,
4278 static struct table parser[] = {
4279 {"http://", strcasestr, get_uri_part, make_uri_string},
4280 {"https://", strcasestr, get_uri_part, make_uri_string},
4281 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4282 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4283 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4284 {"www.", strcasestr, get_uri_part, make_http_string},
4285 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4286 {"@", strcasestr, get_email_part, make_email_string}
4288 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4289 gint last_index = PARSE_ELEMS;
4291 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4295 if (!prev_autowrap && num_blocks == 0) {
4297 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4298 G_CALLBACK(text_inserted),
4301 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4304 uri_start = uri_stop = -1;
4306 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4309 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4310 if (startq_offset == -1)
4311 startq_offset = gtk_text_iter_get_offset(&iter);
4312 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4313 if (quotelevel > 2) {
4314 /* recycle colors */
4315 if (prefs_common.recycle_quote_colors)
4324 if (startq_offset == -1)
4325 noq_offset = gtk_text_iter_get_offset(&iter);
4329 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4332 if (gtk_text_iter_ends_line(&iter)) {
4334 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4335 prefs_common.linewrap_len,
4337 GtkTextIter prev, next, cur;
4338 if (prev_autowrap != FALSE || force) {
4339 compose->automatic_break = TRUE;
4341 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4342 compose->automatic_break = FALSE;
4343 if (itemized_len && compose->autoindent) {
4344 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4345 if (!item_continuation)
4346 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4348 } else if (quote_str && wrap_quote) {
4349 compose->automatic_break = TRUE;
4351 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4352 compose->automatic_break = FALSE;
4353 if (itemized_len && compose->autoindent) {
4354 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4355 if (!item_continuation)
4356 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4360 /* remove trailing spaces */
4362 rem_item_len = itemized_len;
4363 while (compose->autoindent && rem_item_len-- > 0)
4364 gtk_text_iter_backward_char(&cur);
4365 gtk_text_iter_backward_char(&cur);
4368 while (!gtk_text_iter_starts_line(&cur)) {
4371 gtk_text_iter_backward_char(&cur);
4372 wc = gtk_text_iter_get_char(&cur);
4373 if (!g_unichar_isspace(wc))
4377 if (!gtk_text_iter_equal(&prev, &next)) {
4378 gtk_text_buffer_delete(buffer, &prev, &next);
4380 gtk_text_iter_forward_char(&break_pos);
4384 gtk_text_buffer_insert(buffer, &break_pos,
4388 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4390 /* move iter to current line start */
4391 gtk_text_iter_set_line_offset(&iter, 0);
4398 /* move iter to next line start */
4404 if (!prev_autowrap && num_blocks > 0) {
4406 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4407 G_CALLBACK(text_inserted),
4411 while (!gtk_text_iter_ends_line(&end_of_line)) {
4412 gtk_text_iter_forward_char(&end_of_line);
4414 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4416 nouri_start = gtk_text_iter_get_offset(&iter);
4417 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4419 walk_pos = gtk_text_iter_get_offset(&iter);
4420 /* FIXME: this looks phony. scanning for anything in the parse table */
4421 for (n = 0; n < PARSE_ELEMS; n++) {
4424 tmp = parser[n].search(walk, parser[n].needle);
4426 if (scanpos == NULL || tmp < scanpos) {
4435 /* check if URI can be parsed */
4436 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4437 (const gchar **)&ep, FALSE)
4438 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4442 strlen(parser[last_index].needle);
4445 uri_start = walk_pos + (bp - o_walk);
4446 uri_stop = walk_pos + (ep - o_walk);
4450 gtk_text_iter_forward_line(&iter);
4453 if (startq_offset != -1) {
4454 GtkTextIter startquote, endquote;
4455 gtk_text_buffer_get_iter_at_offset(
4456 buffer, &startquote, startq_offset);
4459 switch (quotelevel) {
4461 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4462 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4463 gtk_text_buffer_apply_tag_by_name(
4464 buffer, "quote0", &startquote, &endquote);
4465 gtk_text_buffer_remove_tag_by_name(
4466 buffer, "quote1", &startquote, &endquote);
4467 gtk_text_buffer_remove_tag_by_name(
4468 buffer, "quote2", &startquote, &endquote);
4473 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4474 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4475 gtk_text_buffer_apply_tag_by_name(
4476 buffer, "quote1", &startquote, &endquote);
4477 gtk_text_buffer_remove_tag_by_name(
4478 buffer, "quote0", &startquote, &endquote);
4479 gtk_text_buffer_remove_tag_by_name(
4480 buffer, "quote2", &startquote, &endquote);
4485 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4486 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4487 gtk_text_buffer_apply_tag_by_name(
4488 buffer, "quote2", &startquote, &endquote);
4489 gtk_text_buffer_remove_tag_by_name(
4490 buffer, "quote0", &startquote, &endquote);
4491 gtk_text_buffer_remove_tag_by_name(
4492 buffer, "quote1", &startquote, &endquote);
4498 } else if (noq_offset != -1) {
4499 GtkTextIter startnoquote, endnoquote;
4500 gtk_text_buffer_get_iter_at_offset(
4501 buffer, &startnoquote, noq_offset);
4504 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4505 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4506 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4507 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4508 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4509 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4510 gtk_text_buffer_remove_tag_by_name(
4511 buffer, "quote0", &startnoquote, &endnoquote);
4512 gtk_text_buffer_remove_tag_by_name(
4513 buffer, "quote1", &startnoquote, &endnoquote);
4514 gtk_text_buffer_remove_tag_by_name(
4515 buffer, "quote2", &startnoquote, &endnoquote);
4521 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4522 GtkTextIter nouri_start_iter, nouri_end_iter;
4523 gtk_text_buffer_get_iter_at_offset(
4524 buffer, &nouri_start_iter, nouri_start);
4525 gtk_text_buffer_get_iter_at_offset(
4526 buffer, &nouri_end_iter, nouri_stop);
4527 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4528 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4529 gtk_text_buffer_remove_tag_by_name(
4530 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4531 modified_before_remove = modified;
4536 if (uri_start >= 0 && uri_stop > 0) {
4537 GtkTextIter uri_start_iter, uri_end_iter, back;
4538 gtk_text_buffer_get_iter_at_offset(
4539 buffer, &uri_start_iter, uri_start);
4540 gtk_text_buffer_get_iter_at_offset(
4541 buffer, &uri_end_iter, uri_stop);
4542 back = uri_end_iter;
4543 gtk_text_iter_backward_char(&back);
4544 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4545 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4546 gtk_text_buffer_apply_tag_by_name(
4547 buffer, "link", &uri_start_iter, &uri_end_iter);
4549 if (removed && !modified_before_remove) {
4555 // debug_print("not modified, out after %d lines\n", lines);
4559 // debug_print("modified, out after %d lines\n", lines);
4561 g_free(itemized_chars);
4564 undo_wrapping(compose->undostruct, FALSE);
4565 compose->autowrap = prev_autowrap;
4570 void compose_action_cb(void *data)
4572 Compose *compose = (Compose *)data;
4573 compose_wrap_all(compose);
4576 static void compose_wrap_all(Compose *compose)
4578 compose_wrap_all_full(compose, FALSE);
4581 static void compose_wrap_all_full(Compose *compose, gboolean force)
4583 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4584 GtkTextBuffer *buffer;
4586 gboolean modified = TRUE;
4588 buffer = gtk_text_view_get_buffer(text);
4590 gtk_text_buffer_get_start_iter(buffer, &iter);
4591 while (!gtk_text_iter_is_end(&iter) && modified)
4592 modified = compose_beautify_paragraph(compose, &iter, force);
4596 static void compose_set_title(Compose *compose)
4602 edited = compose->modified ? _(" [Edited]") : "";
4604 subject = gtk_editable_get_chars(
4605 GTK_EDITABLE(compose->subject_entry), 0, -1);
4607 #ifndef GENERIC_UMPC
4608 if (subject && strlen(subject))
4609 str = g_strdup_printf(_("%s - Compose message%s"),
4612 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4614 str = g_strdup(_("Compose message"));
4617 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4623 * compose_current_mail_account:
4625 * Find a current mail account (the currently selected account, or the
4626 * default account, if a news account is currently selected). If a
4627 * mail account cannot be found, display an error message.
4629 * Return value: Mail account, or NULL if not found.
4631 static PrefsAccount *
4632 compose_current_mail_account(void)
4636 if (cur_account && cur_account->protocol != A_NNTP)
4639 ac = account_get_default();
4640 if (!ac || ac->protocol == A_NNTP) {
4641 alertpanel_error(_("Account for sending mail is not specified.\n"
4642 "Please select a mail account before sending."));
4649 #define QUOTE_IF_REQUIRED(out, str) \
4651 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4655 len = strlen(str) + 3; \
4656 if ((__tmp = alloca(len)) == NULL) { \
4657 g_warning("can't allocate memory\n"); \
4658 g_string_free(header, TRUE); \
4661 g_snprintf(__tmp, len, "\"%s\"", str); \
4666 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4667 g_warning("can't allocate memory\n"); \
4668 g_string_free(header, TRUE); \
4671 strcpy(__tmp, str); \
4677 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4679 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4683 len = strlen(str) + 3; \
4684 if ((__tmp = alloca(len)) == NULL) { \
4685 g_warning("can't allocate memory\n"); \
4688 g_snprintf(__tmp, len, "\"%s\"", str); \
4693 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4694 g_warning("can't allocate memory\n"); \
4697 strcpy(__tmp, str); \
4703 static void compose_select_account(Compose *compose, PrefsAccount *account,
4706 gchar *from = NULL, *header;
4707 ComposeHeaderEntry *header_entry;
4709 cm_return_if_fail(account != NULL);
4711 compose->account = account;
4712 if (account->name && *account->name) {
4714 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4715 from = g_strdup_printf("%s <%s>",
4716 buf, account->address);
4717 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4719 from = g_strdup_printf("<%s>",
4721 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4726 compose_set_title(compose);
4728 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4729 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4731 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4732 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4733 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4735 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4737 activate_privacy_system(compose, account, FALSE);
4739 if (!init && compose->mode != COMPOSE_REDIRECT) {
4740 undo_block(compose->undostruct);
4741 compose_insert_sig(compose, TRUE);
4742 undo_unblock(compose->undostruct);
4745 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4746 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4748 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4749 if (account->protocol == A_NNTP) {
4750 if (!strcmp(header, _("To:")))
4751 combobox_select_by_text(
4752 GTK_COMBO_BOX(header_entry->combo),
4755 if (!strcmp(header, _("Newsgroups:")))
4756 combobox_select_by_text(
4757 GTK_COMBO_BOX(header_entry->combo),
4765 /* use account's dict info if set */
4766 if (compose->gtkaspell) {
4767 if (account->enable_default_dictionary)
4768 gtkaspell_change_dict(compose->gtkaspell,
4769 account->default_dictionary, FALSE);
4770 if (account->enable_default_alt_dictionary)
4771 gtkaspell_change_alt_dict(compose->gtkaspell,
4772 account->default_alt_dictionary);
4773 if (account->enable_default_dictionary
4774 || account->enable_default_alt_dictionary)
4775 compose_spell_menu_changed(compose);
4780 gboolean compose_check_for_valid_recipient(Compose *compose) {
4781 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4782 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4783 gboolean recipient_found = FALSE;
4787 /* free to and newsgroup list */
4788 slist_free_strings(compose->to_list);
4789 g_slist_free(compose->to_list);
4790 compose->to_list = NULL;
4792 slist_free_strings(compose->newsgroup_list);
4793 g_slist_free(compose->newsgroup_list);
4794 compose->newsgroup_list = NULL;
4796 /* search header entries for to and newsgroup entries */
4797 for (list = compose->header_list; list; list = list->next) {
4800 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4801 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4804 if (entry[0] != '\0') {
4805 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4806 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4807 compose->to_list = address_list_append(compose->to_list, entry);
4808 recipient_found = TRUE;
4811 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4812 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4813 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4814 recipient_found = TRUE;
4821 return recipient_found;
4824 static gboolean compose_check_for_set_recipients(Compose *compose)
4826 if (compose->account->set_autocc && compose->account->auto_cc) {
4827 gboolean found_other = FALSE;
4829 /* search header entries for to and newsgroup entries */
4830 for (list = compose->header_list; list; list = list->next) {
4833 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4834 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4837 if (strcmp(entry, compose->account->auto_cc)
4838 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4848 if (compose->batch) {
4849 gtk_widget_show_all(compose->window);
4851 aval = alertpanel(_("Send"),
4852 _("The only recipient is the default CC address. Send anyway?"),
4853 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4854 if (aval != G_ALERTALTERNATE)
4858 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4859 gboolean found_other = FALSE;
4861 /* search header entries for to and newsgroup entries */
4862 for (list = compose->header_list; list; list = list->next) {
4865 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4866 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4869 if (strcmp(entry, compose->account->auto_bcc)
4870 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4880 if (compose->batch) {
4881 gtk_widget_show_all(compose->window);
4883 aval = alertpanel(_("Send"),
4884 _("The only recipient is the default BCC address. Send anyway?"),
4885 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4886 if (aval != G_ALERTALTERNATE)
4893 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4897 if (compose_check_for_valid_recipient(compose) == FALSE) {
4898 if (compose->batch) {
4899 gtk_widget_show_all(compose->window);
4901 alertpanel_error(_("Recipient is not specified."));
4905 if (compose_check_for_set_recipients(compose) == FALSE) {
4909 if (!compose->batch) {
4910 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4911 if (*str == '\0' && check_everything == TRUE &&
4912 compose->mode != COMPOSE_REDIRECT) {
4914 gchar *button_label;
4917 if (compose->sending)
4918 button_label = _("+_Send");
4920 button_label = _("+_Queue");
4921 message = g_strdup_printf(_("Subject is empty. %s"),
4922 compose->sending?_("Send it anyway?"):
4923 _("Queue it anyway?"));
4925 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4926 GTK_STOCK_CANCEL, button_label, NULL);
4928 if (aval != G_ALERTALTERNATE)
4933 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4939 gint compose_send(Compose *compose)
4942 FolderItem *folder = NULL;
4944 gchar *msgpath = NULL;
4945 gboolean discard_window = FALSE;
4946 gchar *errstr = NULL;
4947 gchar *tmsgid = NULL;
4948 MainWindow *mainwin = mainwindow_get_mainwindow();
4949 gboolean queued_removed = FALSE;
4951 if (prefs_common.send_dialog_invisible
4952 || compose->batch == TRUE)
4953 discard_window = TRUE;
4955 compose_allow_user_actions (compose, FALSE);
4956 compose->sending = TRUE;
4958 if (compose_check_entries(compose, TRUE) == FALSE) {
4959 if (compose->batch) {
4960 gtk_widget_show_all(compose->window);
4966 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4969 if (compose->batch) {
4970 gtk_widget_show_all(compose->window);
4973 alertpanel_error(_("Could not queue message for sending:\n\n"
4974 "Charset conversion failed."));
4975 } else if (val == -5) {
4976 alertpanel_error(_("Could not queue message for sending:\n\n"
4977 "Couldn't get recipient encryption key."));
4978 } else if (val == -6) {
4980 } else if (val == -3) {
4981 if (privacy_peek_error())
4982 alertpanel_error(_("Could not queue message for sending:\n\n"
4983 "Signature failed: %s"), privacy_get_error());
4984 } else if (val == -2 && errno != 0) {
4985 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4987 alertpanel_error(_("Could not queue message for sending."));
4992 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4993 if (discard_window) {
4994 compose->sending = FALSE;
4995 compose_close(compose);
4996 /* No more compose access in the normal codepath
4997 * after this point! */
5002 alertpanel_error(_("The message was queued but could not be "
5003 "sent.\nUse \"Send queued messages\" from "
5004 "the main window to retry."));
5005 if (!discard_window) {
5012 if (msgpath == NULL) {
5013 msgpath = folder_item_fetch_msg(folder, msgnum);
5014 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5017 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5018 claws_unlink(msgpath);
5021 if (!discard_window) {
5023 if (!queued_removed)
5024 folder_item_remove_msg(folder, msgnum);
5025 folder_item_scan(folder);
5027 /* make sure we delete that */
5028 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5030 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5031 folder_item_remove_msg(folder, tmp->msgnum);
5032 procmsg_msginfo_free(tmp);
5039 if (!queued_removed)
5040 folder_item_remove_msg(folder, msgnum);
5041 folder_item_scan(folder);
5043 /* make sure we delete that */
5044 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5046 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5047 folder_item_remove_msg(folder, tmp->msgnum);
5048 procmsg_msginfo_free(tmp);
5051 if (!discard_window) {
5052 compose->sending = FALSE;
5053 compose_allow_user_actions (compose, TRUE);
5054 compose_close(compose);
5058 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5059 "the main window to retry."), errstr);
5062 alertpanel_error_log(_("The message was queued but could not be "
5063 "sent.\nUse \"Send queued messages\" from "
5064 "the main window to retry."));
5066 if (!discard_window) {
5075 toolbar_main_set_sensitive(mainwin);
5076 main_window_set_menu_sensitive(mainwin);
5082 compose_allow_user_actions (compose, TRUE);
5083 compose->sending = FALSE;
5084 compose->modified = TRUE;
5085 toolbar_main_set_sensitive(mainwin);
5086 main_window_set_menu_sensitive(mainwin);
5091 static gboolean compose_use_attach(Compose *compose)
5093 GtkTreeModel *model = gtk_tree_view_get_model
5094 (GTK_TREE_VIEW(compose->attach_clist));
5095 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5098 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5101 gchar buf[BUFFSIZE];
5103 gboolean first_to_address;
5104 gboolean first_cc_address;
5106 ComposeHeaderEntry *headerentry;
5107 const gchar *headerentryname;
5108 const gchar *cc_hdr;
5109 const gchar *to_hdr;
5110 gboolean err = FALSE;
5112 debug_print("Writing redirect header\n");
5114 cc_hdr = prefs_common_translated_header_name("Cc:");
5115 to_hdr = prefs_common_translated_header_name("To:");
5117 first_to_address = TRUE;
5118 for (list = compose->header_list; list; list = list->next) {
5119 headerentry = ((ComposeHeaderEntry *)list->data);
5120 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5122 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5123 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5124 Xstrdup_a(str, entstr, return -1);
5126 if (str[0] != '\0') {
5127 compose_convert_header
5128 (compose, buf, sizeof(buf), str,
5129 strlen("Resent-To") + 2, TRUE);
5131 if (first_to_address) {
5132 err |= (fprintf(fp, "Resent-To: ") < 0);
5133 first_to_address = FALSE;
5135 err |= (fprintf(fp, ",") < 0);
5137 err |= (fprintf(fp, "%s", buf) < 0);
5141 if (!first_to_address) {
5142 err |= (fprintf(fp, "\n") < 0);
5145 first_cc_address = TRUE;
5146 for (list = compose->header_list; list; list = list->next) {
5147 headerentry = ((ComposeHeaderEntry *)list->data);
5148 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5150 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5151 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5152 Xstrdup_a(str, strg, return -1);
5154 if (str[0] != '\0') {
5155 compose_convert_header
5156 (compose, buf, sizeof(buf), str,
5157 strlen("Resent-Cc") + 2, TRUE);
5159 if (first_cc_address) {
5160 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5161 first_cc_address = FALSE;
5163 err |= (fprintf(fp, ",") < 0);
5165 err |= (fprintf(fp, "%s", buf) < 0);
5169 if (!first_cc_address) {
5170 err |= (fprintf(fp, "\n") < 0);
5173 return (err ? -1:0);
5176 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5178 gchar buf[BUFFSIZE];
5180 const gchar *entstr;
5181 /* struct utsname utsbuf; */
5182 gboolean err = FALSE;
5184 cm_return_val_if_fail(fp != NULL, -1);
5185 cm_return_val_if_fail(compose->account != NULL, -1);
5186 cm_return_val_if_fail(compose->account->address != NULL, -1);
5189 get_rfc822_date(buf, sizeof(buf));
5190 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5193 if (compose->account->name && *compose->account->name) {
5194 compose_convert_header
5195 (compose, buf, sizeof(buf), compose->account->name,
5196 strlen("From: "), TRUE);
5197 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5198 buf, compose->account->address) < 0);
5200 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5203 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5204 if (*entstr != '\0') {
5205 Xstrdup_a(str, entstr, return -1);
5208 compose_convert_header(compose, buf, sizeof(buf), str,
5209 strlen("Subject: "), FALSE);
5210 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5214 /* Resent-Message-ID */
5215 if (compose->account->set_domain && compose->account->domain) {
5216 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5217 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5218 g_snprintf(buf, sizeof(buf), "%s",
5219 strchr(compose->account->address, '@') ?
5220 strchr(compose->account->address, '@')+1 :
5221 compose->account->address);
5223 g_snprintf(buf, sizeof(buf), "%s", "");
5226 if (compose->account->gen_msgid) {
5228 if (compose->account->msgid_with_addr) {
5229 addr = compose->account->address;
5231 generate_msgid(buf, sizeof(buf), addr);
5232 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5233 compose->msgid = g_strdup(buf);
5235 compose->msgid = NULL;
5238 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5241 /* separator between header and body */
5242 err |= (fputs("\n", fp) == EOF);
5244 return (err ? -1:0);
5247 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5251 gchar buf[BUFFSIZE];
5253 gboolean skip = FALSE;
5254 gboolean err = FALSE;
5255 gchar *not_included[]={
5256 "Return-Path:", "Delivered-To:", "Received:",
5257 "Subject:", "X-UIDL:", "AF:",
5258 "NF:", "PS:", "SRH:",
5259 "SFN:", "DSR:", "MID:",
5260 "CFG:", "PT:", "S:",
5261 "RQ:", "SSV:", "NSV:",
5262 "SSH:", "R:", "MAID:",
5263 "NAID:", "RMID:", "FMID:",
5264 "SCF:", "RRCPT:", "NG:",
5265 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5266 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5267 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5268 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5269 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5272 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5273 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5277 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5279 for (i = 0; not_included[i] != NULL; i++) {
5280 if (g_ascii_strncasecmp(buf, not_included[i],
5281 strlen(not_included[i])) == 0) {
5288 if (fputs(buf, fdest) == -1)
5291 if (!prefs_common.redirect_keep_from) {
5292 if (g_ascii_strncasecmp(buf, "From:",
5293 strlen("From:")) == 0) {
5294 err |= (fputs(" (by way of ", fdest) == EOF);
5295 if (compose->account->name
5296 && *compose->account->name) {
5297 compose_convert_header
5298 (compose, buf, sizeof(buf),
5299 compose->account->name,
5302 err |= (fprintf(fdest, "%s <%s>",
5304 compose->account->address) < 0);
5306 err |= (fprintf(fdest, "%s",
5307 compose->account->address) < 0);
5308 err |= (fputs(")", fdest) == EOF);
5312 if (fputs("\n", fdest) == -1)
5319 if (compose_redirect_write_headers(compose, fdest))
5322 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5323 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5336 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5338 GtkTextBuffer *buffer;
5339 GtkTextIter start, end;
5342 const gchar *out_codeset;
5343 EncodingType encoding = ENC_UNKNOWN;
5344 MimeInfo *mimemsg, *mimetext;
5346 const gchar *src_codeset = CS_INTERNAL;
5347 gchar *from_addr = NULL;
5348 gchar *from_name = NULL;
5350 if (action == COMPOSE_WRITE_FOR_SEND)
5351 attach_parts = TRUE;
5353 /* create message MimeInfo */
5354 mimemsg = procmime_mimeinfo_new();
5355 mimemsg->type = MIMETYPE_MESSAGE;
5356 mimemsg->subtype = g_strdup("rfc822");
5357 mimemsg->content = MIMECONTENT_MEM;
5358 mimemsg->tmp = TRUE; /* must free content later */
5359 mimemsg->data.mem = compose_get_header(compose);
5361 /* Create text part MimeInfo */
5362 /* get all composed text */
5363 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5364 gtk_text_buffer_get_start_iter(buffer, &start);
5365 gtk_text_buffer_get_end_iter(buffer, &end);
5366 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5368 out_codeset = conv_get_charset_str(compose->out_encoding);
5370 if (!out_codeset && is_ascii_str(chars)) {
5371 out_codeset = CS_US_ASCII;
5372 } else if (prefs_common.outgoing_fallback_to_ascii &&
5373 is_ascii_str(chars)) {
5374 out_codeset = CS_US_ASCII;
5375 encoding = ENC_7BIT;
5379 gchar *test_conv_global_out = NULL;
5380 gchar *test_conv_reply = NULL;
5382 /* automatic mode. be automatic. */
5383 codeconv_set_strict(TRUE);
5385 out_codeset = conv_get_outgoing_charset_str();
5387 debug_print("trying to convert to %s\n", out_codeset);
5388 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5391 if (!test_conv_global_out && compose->orig_charset
5392 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5393 out_codeset = compose->orig_charset;
5394 debug_print("failure; trying to convert to %s\n", out_codeset);
5395 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5398 if (!test_conv_global_out && !test_conv_reply) {
5400 out_codeset = CS_INTERNAL;
5401 debug_print("failure; finally using %s\n", out_codeset);
5403 g_free(test_conv_global_out);
5404 g_free(test_conv_reply);
5405 codeconv_set_strict(FALSE);
5408 if (encoding == ENC_UNKNOWN) {
5409 if (prefs_common.encoding_method == CTE_BASE64)
5410 encoding = ENC_BASE64;
5411 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5412 encoding = ENC_QUOTED_PRINTABLE;
5413 else if (prefs_common.encoding_method == CTE_8BIT)
5414 encoding = ENC_8BIT;
5416 encoding = procmime_get_encoding_for_charset(out_codeset);
5419 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5420 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5422 if (action == COMPOSE_WRITE_FOR_SEND) {
5423 codeconv_set_strict(TRUE);
5424 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5425 codeconv_set_strict(FALSE);
5431 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5432 "to the specified %s charset.\n"
5433 "Send it as %s?"), out_codeset, src_codeset);
5434 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5435 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5438 if (aval != G_ALERTALTERNATE) {
5443 out_codeset = src_codeset;
5449 out_codeset = src_codeset;
5454 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5455 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5456 strstr(buf, "\nFrom ") != NULL) {
5457 encoding = ENC_QUOTED_PRINTABLE;
5461 mimetext = procmime_mimeinfo_new();
5462 mimetext->content = MIMECONTENT_MEM;
5463 mimetext->tmp = TRUE; /* must free content later */
5464 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5465 * and free the data, which we need later. */
5466 mimetext->data.mem = g_strdup(buf);
5467 mimetext->type = MIMETYPE_TEXT;
5468 mimetext->subtype = g_strdup("plain");
5469 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5470 g_strdup(out_codeset));
5472 /* protect trailing spaces when signing message */
5473 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5474 privacy_system_can_sign(compose->privacy_system)) {
5475 encoding = ENC_QUOTED_PRINTABLE;
5478 debug_print("main text: %zd bytes encoded as %s in %d\n",
5479 strlen(buf), out_codeset, encoding);
5481 /* check for line length limit */
5482 if (action == COMPOSE_WRITE_FOR_SEND &&
5483 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5484 check_line_length(buf, 1000, &line) < 0) {
5488 msg = g_strdup_printf
5489 (_("Line %d exceeds the line length limit (998 bytes).\n"
5490 "The contents of the message might be broken on the way to the delivery.\n"
5492 "Send it anyway?"), line + 1);
5493 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5495 if (aval != G_ALERTALTERNATE) {
5501 if (encoding != ENC_UNKNOWN)
5502 procmime_encode_content(mimetext, encoding);
5504 /* append attachment parts */
5505 if (compose_use_attach(compose) && attach_parts) {
5506 MimeInfo *mimempart;
5507 gchar *boundary = NULL;
5508 mimempart = procmime_mimeinfo_new();
5509 mimempart->content = MIMECONTENT_EMPTY;
5510 mimempart->type = MIMETYPE_MULTIPART;
5511 mimempart->subtype = g_strdup("mixed");
5515 boundary = generate_mime_boundary(NULL);
5516 } while (strstr(buf, boundary) != NULL);
5518 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5521 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5523 g_node_append(mimempart->node, mimetext->node);
5524 g_node_append(mimemsg->node, mimempart->node);
5526 if (compose_add_attachments(compose, mimempart) < 0)
5529 g_node_append(mimemsg->node, mimetext->node);
5533 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5534 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5535 /* extract name and address */
5536 if (strstr(spec, " <") && strstr(spec, ">")) {
5537 from_addr = g_strdup(strrchr(spec, '<')+1);
5538 *(strrchr(from_addr, '>')) = '\0';
5539 from_name = g_strdup(spec);
5540 *(strrchr(from_name, '<')) = '\0';
5547 /* sign message if sending */
5548 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5549 privacy_system_can_sign(compose->privacy_system))
5550 if (!privacy_sign(compose->privacy_system, mimemsg,
5551 compose->account, from_addr)) {
5558 procmime_write_mimeinfo(mimemsg, fp);
5560 procmime_mimeinfo_free_all(mimemsg);
5565 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5567 GtkTextBuffer *buffer;
5568 GtkTextIter start, end;
5573 if ((fp = g_fopen(file, "wb")) == NULL) {
5574 FILE_OP_ERROR(file, "fopen");
5578 /* chmod for security */
5579 if (change_file_mode_rw(fp, file) < 0) {
5580 FILE_OP_ERROR(file, "chmod");
5581 g_warning("can't change file mode\n");
5584 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5585 gtk_text_buffer_get_start_iter(buffer, &start);
5586 gtk_text_buffer_get_end_iter(buffer, &end);
5587 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5589 chars = conv_codeset_strdup
5590 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5593 if (!chars) return -1;
5596 len = strlen(chars);
5597 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5598 FILE_OP_ERROR(file, "fwrite");
5607 if (fclose(fp) == EOF) {
5608 FILE_OP_ERROR(file, "fclose");
5615 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5618 MsgInfo *msginfo = compose->targetinfo;
5620 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5621 if (!msginfo) return -1;
5623 if (!force && MSG_IS_LOCKED(msginfo->flags))
5626 item = msginfo->folder;
5627 cm_return_val_if_fail(item != NULL, -1);
5629 if (procmsg_msg_exist(msginfo) &&
5630 (folder_has_parent_of_type(item, F_QUEUE) ||
5631 folder_has_parent_of_type(item, F_DRAFT)
5632 || msginfo == compose->autosaved_draft)) {
5633 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5634 g_warning("can't remove the old message\n");
5637 debug_print("removed reedit target %d\n", msginfo->msgnum);
5644 static void compose_remove_draft(Compose *compose)
5647 MsgInfo *msginfo = compose->targetinfo;
5648 drafts = account_get_special_folder(compose->account, F_DRAFT);
5650 if (procmsg_msg_exist(msginfo)) {
5651 folder_item_remove_msg(drafts, msginfo->msgnum);
5656 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5657 gboolean remove_reedit_target)
5659 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5662 static gboolean compose_warn_encryption(Compose *compose)
5664 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5665 AlertValue val = G_ALERTALTERNATE;
5667 if (warning == NULL)
5670 val = alertpanel_full(_("Encryption warning"), warning,
5671 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5672 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5673 if (val & G_ALERTDISABLE) {
5674 val &= ~G_ALERTDISABLE;
5675 if (val == G_ALERTALTERNATE)
5676 privacy_inhibit_encrypt_warning(compose->privacy_system,
5680 if (val == G_ALERTALTERNATE) {
5687 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5688 gchar **msgpath, gboolean check_subject,
5689 gboolean remove_reedit_target)
5696 static gboolean lock = FALSE;
5697 PrefsAccount *mailac = NULL, *newsac = NULL;
5698 gboolean err = FALSE;
5700 debug_print("queueing message...\n");
5701 cm_return_val_if_fail(compose->account != NULL, -1);
5705 if (compose_check_entries(compose, check_subject) == FALSE) {
5707 if (compose->batch) {
5708 gtk_widget_show_all(compose->window);
5713 if (!compose->to_list && !compose->newsgroup_list) {
5714 g_warning("can't get recipient list.");
5719 if (compose->to_list) {
5720 if (compose->account->protocol != A_NNTP)
5721 mailac = compose->account;
5722 else if (cur_account && cur_account->protocol != A_NNTP)
5723 mailac = cur_account;
5724 else if (!(mailac = compose_current_mail_account())) {
5726 alertpanel_error(_("No account for sending mails available!"));
5731 if (compose->newsgroup_list) {
5732 if (compose->account->protocol == A_NNTP)
5733 newsac = compose->account;
5736 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5741 /* write queue header */
5742 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5743 G_DIR_SEPARATOR, compose, (guint) rand());
5744 debug_print("queuing to %s\n", tmp);
5745 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5746 FILE_OP_ERROR(tmp, "fopen");
5752 if (change_file_mode_rw(fp, tmp) < 0) {
5753 FILE_OP_ERROR(tmp, "chmod");
5754 g_warning("can't change file mode\n");
5757 /* queueing variables */
5758 err |= (fprintf(fp, "AF:\n") < 0);
5759 err |= (fprintf(fp, "NF:0\n") < 0);
5760 err |= (fprintf(fp, "PS:10\n") < 0);
5761 err |= (fprintf(fp, "SRH:1\n") < 0);
5762 err |= (fprintf(fp, "SFN:\n") < 0);
5763 err |= (fprintf(fp, "DSR:\n") < 0);
5765 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5767 err |= (fprintf(fp, "MID:\n") < 0);
5768 err |= (fprintf(fp, "CFG:\n") < 0);
5769 err |= (fprintf(fp, "PT:0\n") < 0);
5770 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5771 err |= (fprintf(fp, "RQ:\n") < 0);
5773 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5775 err |= (fprintf(fp, "SSV:\n") < 0);
5777 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5779 err |= (fprintf(fp, "NSV:\n") < 0);
5780 err |= (fprintf(fp, "SSH:\n") < 0);
5781 /* write recepient list */
5782 if (compose->to_list) {
5783 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5784 for (cur = compose->to_list->next; cur != NULL;
5786 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5787 err |= (fprintf(fp, "\n") < 0);
5789 /* write newsgroup list */
5790 if (compose->newsgroup_list) {
5791 err |= (fprintf(fp, "NG:") < 0);
5792 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5793 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5794 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5795 err |= (fprintf(fp, "\n") < 0);
5797 /* Sylpheed account IDs */
5799 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5801 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5804 if (compose->privacy_system != NULL) {
5805 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5806 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5807 if (compose->use_encryption) {
5809 if (!compose_warn_encryption(compose)) {
5816 if (mailac && mailac->encrypt_to_self) {
5817 GSList *tmp_list = g_slist_copy(compose->to_list);
5818 tmp_list = g_slist_append(tmp_list, compose->account->address);
5819 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5820 g_slist_free(tmp_list);
5822 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5824 if (encdata != NULL) {
5825 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5826 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5827 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5829 } /* else we finally dont want to encrypt */
5831 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5832 /* and if encdata was null, it means there's been a problem in
5844 /* Save copy folder */
5845 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5846 gchar *savefolderid;
5848 savefolderid = compose_get_save_to(compose);
5849 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5850 g_free(savefolderid);
5852 /* Save copy folder */
5853 if (compose->return_receipt) {
5854 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5856 /* Message-ID of message replying to */
5857 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5860 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5861 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5864 /* Message-ID of message forwarding to */
5865 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5868 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5869 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5873 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5874 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5876 /* end of headers */
5877 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5879 if (compose->redirect_filename != NULL) {
5880 if (compose_redirect_write_to_file(compose, fp) < 0) {
5889 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5894 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5898 g_warning("failed to write queue message\n");
5905 if (fclose(fp) == EOF) {
5906 FILE_OP_ERROR(tmp, "fclose");
5913 if (item && *item) {
5916 queue = account_get_special_folder(compose->account, F_QUEUE);
5919 g_warning("can't find queue folder\n");
5925 folder_item_scan(queue);
5926 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5927 g_warning("can't queue the message\n");
5934 if (msgpath == NULL) {
5940 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5941 compose_remove_reedit_target(compose, FALSE);
5944 if ((msgnum != NULL) && (item != NULL)) {
5952 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5955 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5957 struct stat statbuf;
5958 gchar *type, *subtype;
5959 GtkTreeModel *model;
5962 model = gtk_tree_view_get_model(tree_view);
5964 if (!gtk_tree_model_get_iter_first(model, &iter))
5967 gtk_tree_model_get(model, &iter,
5971 if (!is_file_exist(ainfo->file)) {
5972 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5973 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5974 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5976 if (val == G_ALERTDEFAULT) {
5981 mimepart = procmime_mimeinfo_new();
5982 mimepart->content = MIMECONTENT_FILE;
5983 mimepart->data.filename = g_strdup(ainfo->file);
5984 mimepart->tmp = FALSE; /* or we destroy our attachment */
5985 mimepart->offset = 0;
5987 g_stat(ainfo->file, &statbuf);
5988 mimepart->length = statbuf.st_size;
5990 type = g_strdup(ainfo->content_type);
5992 if (!strchr(type, '/')) {
5994 type = g_strdup("application/octet-stream");
5997 subtype = strchr(type, '/') + 1;
5998 *(subtype - 1) = '\0';
5999 mimepart->type = procmime_get_media_type(type);
6000 mimepart->subtype = g_strdup(subtype);
6003 if (mimepart->type == MIMETYPE_MESSAGE &&
6004 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
6005 mimepart->disposition = DISPOSITIONTYPE_INLINE;
6006 } else if (mimepart->type == MIMETYPE_TEXT) {
6007 if (!ainfo->name && compose->mode == COMPOSE_FORWARD_INLINE) {
6008 /* Text parts with no name come from multipart/alternative
6009 * forwards. Make sure the recipient won't look at the
6010 * original HTML part by mistake. */
6011 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6012 ainfo->name = g_strdup_printf(_("Original %s part"),
6016 g_hash_table_insert(mimepart->typeparameters,
6017 g_strdup("charset"), g_strdup(ainfo->charset));
6019 if (ainfo->name && mimepart->type != MIMETYPE_MESSAGE) {
6020 if (mimepart->type == MIMETYPE_APPLICATION &&
6021 !strcmp2(mimepart->subtype, "octet-stream"))
6022 g_hash_table_insert(mimepart->typeparameters,
6023 g_strdup("name"), g_strdup(ainfo->name));
6024 g_hash_table_insert(mimepart->dispositionparameters,
6025 g_strdup("filename"), g_strdup(ainfo->name));
6026 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6029 if (mimepart->type == MIMETYPE_MESSAGE
6030 || mimepart->type == MIMETYPE_MULTIPART)
6031 ainfo->encoding = ENC_BINARY;
6032 else if (compose->use_signing) {
6033 if (ainfo->encoding == ENC_7BIT)
6034 ainfo->encoding = ENC_QUOTED_PRINTABLE;
6035 else if (ainfo->encoding == ENC_8BIT)
6036 ainfo->encoding = ENC_BASE64;
6041 procmime_encode_content(mimepart, ainfo->encoding);
6043 g_node_append(parent->node, mimepart->node);
6044 } while (gtk_tree_model_iter_next(model, &iter));
6049 #define IS_IN_CUSTOM_HEADER(header) \
6050 (compose->account->add_customhdr && \
6051 custom_header_find(compose->account->customhdr_list, header) != NULL)
6053 static void compose_add_headerfield_from_headerlist(Compose *compose,
6055 const gchar *fieldname,
6056 const gchar *seperator)
6058 gchar *str, *fieldname_w_colon;
6059 gboolean add_field = FALSE;
6061 ComposeHeaderEntry *headerentry;
6062 const gchar *headerentryname;
6063 const gchar *trans_fieldname;
6066 if (IS_IN_CUSTOM_HEADER(fieldname))
6069 debug_print("Adding %s-fields\n", fieldname);
6071 fieldstr = g_string_sized_new(64);
6073 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6074 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6076 for (list = compose->header_list; list; list = list->next) {
6077 headerentry = ((ComposeHeaderEntry *)list->data);
6078 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6080 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6081 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6083 if (str[0] != '\0') {
6085 g_string_append(fieldstr, seperator);
6086 g_string_append(fieldstr, str);
6095 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6096 compose_convert_header
6097 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6098 strlen(fieldname) + 2, TRUE);
6099 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6103 g_free(fieldname_w_colon);
6104 g_string_free(fieldstr, TRUE);
6109 static gchar *compose_get_header(Compose *compose)
6111 gchar buf[BUFFSIZE];
6112 const gchar *entry_str;
6116 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6118 gchar *from_name = NULL, *from_address = NULL;
6121 cm_return_val_if_fail(compose->account != NULL, NULL);
6122 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6124 header = g_string_sized_new(64);
6127 get_rfc822_date(buf, sizeof(buf));
6128 g_string_append_printf(header, "Date: %s\n", buf);
6132 if (compose->account->name && *compose->account->name) {
6134 QUOTE_IF_REQUIRED(buf, compose->account->name);
6135 tmp = g_strdup_printf("%s <%s>",
6136 buf, compose->account->address);
6138 tmp = g_strdup_printf("%s",
6139 compose->account->address);
6141 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6142 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6144 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6145 from_address = g_strdup(compose->account->address);
6147 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6148 /* extract name and address */
6149 if (strstr(spec, " <") && strstr(spec, ">")) {
6150 from_address = g_strdup(strrchr(spec, '<')+1);
6151 *(strrchr(from_address, '>')) = '\0';
6152 from_name = g_strdup(spec);
6153 *(strrchr(from_name, '<')) = '\0';
6156 from_address = g_strdup(spec);
6163 if (from_name && *from_name) {
6164 compose_convert_header
6165 (compose, buf, sizeof(buf), from_name,
6166 strlen("From: "), TRUE);
6167 QUOTE_IF_REQUIRED(name, buf);
6169 g_string_append_printf(header, "From: %s <%s>\n",
6170 name, from_address);
6172 g_string_append_printf(header, "From: %s\n", from_address);
6175 g_free(from_address);
6178 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6181 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6184 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6188 * If this account is a NNTP account remove Bcc header from
6189 * message body since it otherwise will be publicly shown
6191 if (compose->account->protocol != A_NNTP)
6192 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6195 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6197 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6200 compose_convert_header(compose, buf, sizeof(buf), str,
6201 strlen("Subject: "), FALSE);
6202 g_string_append_printf(header, "Subject: %s\n", buf);
6208 if (compose->account->set_domain && compose->account->domain) {
6209 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6210 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6211 g_snprintf(buf, sizeof(buf), "%s",
6212 strchr(compose->account->address, '@') ?
6213 strchr(compose->account->address, '@')+1 :
6214 compose->account->address);
6216 g_snprintf(buf, sizeof(buf), "%s", "");
6219 if (compose->account->gen_msgid) {
6221 if (compose->account->msgid_with_addr) {
6222 addr = compose->account->address;
6224 generate_msgid(buf, sizeof(buf), addr);
6225 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6226 compose->msgid = g_strdup(buf);
6228 compose->msgid = NULL;
6231 if (compose->remove_references == FALSE) {
6233 if (compose->inreplyto && compose->to_list)
6234 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6237 if (compose->references)
6238 g_string_append_printf(header, "References: %s\n", compose->references);
6242 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6245 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6248 if (compose->account->organization &&
6249 strlen(compose->account->organization) &&
6250 !IS_IN_CUSTOM_HEADER("Organization")) {
6251 compose_convert_header(compose, buf, sizeof(buf),
6252 compose->account->organization,
6253 strlen("Organization: "), FALSE);
6254 g_string_append_printf(header, "Organization: %s\n", buf);
6257 /* Program version and system info */
6258 if (compose->account->gen_xmailer &&
6259 g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6260 !compose->newsgroup_list) {
6261 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6263 gtk_major_version, gtk_minor_version, gtk_micro_version,
6266 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6267 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6269 gtk_major_version, gtk_minor_version, gtk_micro_version,
6273 /* custom headers */
6274 if (compose->account->add_customhdr) {
6277 for (cur = compose->account->customhdr_list; cur != NULL;
6279 CustomHeader *chdr = (CustomHeader *)cur->data;
6281 if (custom_header_is_allowed(chdr->name)
6282 && chdr->value != NULL
6283 && *(chdr->value) != '\0') {
6284 compose_convert_header
6285 (compose, buf, sizeof(buf),
6287 strlen(chdr->name) + 2, FALSE);
6288 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6293 /* Automatic Faces and X-Faces */
6294 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6295 g_string_append_printf(header, "X-Face: %s\n", buf);
6297 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6298 g_string_append_printf(header, "X-Face: %s\n", buf);
6300 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6301 g_string_append_printf(header, "Face: %s\n", buf);
6303 else if (get_default_face (buf, sizeof(buf)) == 0) {
6304 g_string_append_printf(header, "Face: %s\n", buf);
6308 switch (compose->priority) {
6309 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6310 "X-Priority: 1 (Highest)\n");
6312 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6313 "X-Priority: 2 (High)\n");
6315 case PRIORITY_NORMAL: break;
6316 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6317 "X-Priority: 4 (Low)\n");
6319 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6320 "X-Priority: 5 (Lowest)\n");
6322 default: debug_print("compose: priority unknown : %d\n",
6326 /* Request Return Receipt */
6327 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6328 if (compose->return_receipt) {
6329 if (compose->account->name
6330 && *compose->account->name) {
6331 compose_convert_header(compose, buf, sizeof(buf),
6332 compose->account->name,
6333 strlen("Disposition-Notification-To: "),
6335 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6337 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6341 /* get special headers */
6342 for (list = compose->header_list; list; list = list->next) {
6343 ComposeHeaderEntry *headerentry;
6346 gchar *headername_wcolon;
6347 const gchar *headername_trans;
6350 gboolean standard_header = FALSE;
6352 headerentry = ((ComposeHeaderEntry *)list->data);
6354 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6356 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6361 if (!strstr(tmp, ":")) {
6362 headername_wcolon = g_strconcat(tmp, ":", NULL);
6363 headername = g_strdup(tmp);
6365 headername_wcolon = g_strdup(tmp);
6366 headername = g_strdup(strtok(tmp, ":"));
6370 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6371 Xstrdup_a(headervalue, entry_str, return NULL);
6372 subst_char(headervalue, '\r', ' ');
6373 subst_char(headervalue, '\n', ' ');
6374 string = std_headers;
6375 while (*string != NULL) {
6376 headername_trans = prefs_common_translated_header_name(*string);
6377 if (!strcmp(headername_trans, headername_wcolon))
6378 standard_header = TRUE;
6381 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6382 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6385 g_free(headername_wcolon);
6389 g_string_free(header, FALSE);
6394 #undef IS_IN_CUSTOM_HEADER
6396 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6397 gint header_len, gboolean addr_field)
6399 gchar *tmpstr = NULL;
6400 const gchar *out_codeset = NULL;
6402 cm_return_if_fail(src != NULL);
6403 cm_return_if_fail(dest != NULL);
6405 if (len < 1) return;
6407 tmpstr = g_strdup(src);
6409 subst_char(tmpstr, '\n', ' ');
6410 subst_char(tmpstr, '\r', ' ');
6413 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6414 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6415 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6420 codeconv_set_strict(TRUE);
6421 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6422 conv_get_charset_str(compose->out_encoding));
6423 codeconv_set_strict(FALSE);
6425 if (!dest || *dest == '\0') {
6426 gchar *test_conv_global_out = NULL;
6427 gchar *test_conv_reply = NULL;
6429 /* automatic mode. be automatic. */
6430 codeconv_set_strict(TRUE);
6432 out_codeset = conv_get_outgoing_charset_str();
6434 debug_print("trying to convert to %s\n", out_codeset);
6435 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6438 if (!test_conv_global_out && compose->orig_charset
6439 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6440 out_codeset = compose->orig_charset;
6441 debug_print("failure; trying to convert to %s\n", out_codeset);
6442 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6445 if (!test_conv_global_out && !test_conv_reply) {
6447 out_codeset = CS_INTERNAL;
6448 debug_print("finally using %s\n", out_codeset);
6450 g_free(test_conv_global_out);
6451 g_free(test_conv_reply);
6452 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6454 codeconv_set_strict(FALSE);
6459 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6463 cm_return_if_fail(user_data != NULL);
6465 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6466 g_strstrip(address);
6467 if (*address != '\0') {
6468 gchar *name = procheader_get_fromname(address);
6469 extract_address(address);
6470 addressbook_add_contact(name, address, NULL, NULL);
6475 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6477 GtkWidget *menuitem;
6480 cm_return_if_fail(menu != NULL);
6481 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6483 menuitem = gtk_separator_menu_item_new();
6484 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6485 gtk_widget_show(menuitem);
6487 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6488 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6490 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6491 g_strstrip(address);
6492 if (*address == '\0') {
6493 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6496 g_signal_connect(G_OBJECT(menuitem), "activate",
6497 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6498 gtk_widget_show(menuitem);
6501 static void compose_create_header_entry(Compose *compose)
6503 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6510 const gchar *header = NULL;
6511 ComposeHeaderEntry *headerentry;
6512 gboolean standard_header = FALSE;
6513 GtkListStore *model;
6515 #if !(GTK_CHECK_VERSION(2,12,0))
6516 GtkTooltips *tips = compose->tooltips;
6519 headerentry = g_new0(ComposeHeaderEntry, 1);
6522 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6523 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6524 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6526 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6528 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6530 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6531 COMPOSE_NEWSGROUPS);
6532 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6534 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6535 COMPOSE_FOLLOWUPTO);
6537 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6538 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6539 G_CALLBACK(compose_grab_focus_cb), compose);
6540 gtk_widget_show(combo);
6541 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6542 compose->header_nextrow, compose->header_nextrow+1,
6543 GTK_SHRINK, GTK_FILL, 0, 0);
6544 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6545 const gchar *last_header_entry = gtk_entry_get_text(
6546 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6548 while (*string != NULL) {
6549 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6550 standard_header = TRUE;
6553 if (standard_header)
6554 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6556 if (!compose->header_last || !standard_header) {
6557 switch(compose->account->protocol) {
6559 header = prefs_common_translated_header_name("Newsgroups:");
6562 header = prefs_common_translated_header_name("To:");
6567 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6569 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6570 G_CALLBACK(compose_grab_focus_cb), compose);
6572 /* Entry field with cleanup button */
6573 button = gtk_button_new();
6574 gtk_button_set_image(GTK_BUTTON(button),
6575 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6576 gtk_widget_show(button);
6577 CLAWS_SET_TIP(button,
6578 _("Delete entry contents"));
6579 entry = gtk_entry_new();
6580 gtk_widget_show(entry);
6581 CLAWS_SET_TIP(entry,
6582 _("Use <tab> to autocomplete from addressbook"));
6583 hbox = gtk_hbox_new (FALSE, 0);
6584 gtk_widget_show(hbox);
6585 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6586 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6587 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6588 compose->header_nextrow, compose->header_nextrow+1,
6589 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6591 g_signal_connect(G_OBJECT(entry), "key-press-event",
6592 G_CALLBACK(compose_headerentry_key_press_event_cb),
6594 g_signal_connect(G_OBJECT(entry), "changed",
6595 G_CALLBACK(compose_headerentry_changed_cb),
6597 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6598 G_CALLBACK(compose_grab_focus_cb), compose);
6600 g_signal_connect(G_OBJECT(button), "clicked",
6601 G_CALLBACK(compose_headerentry_button_clicked_cb),
6605 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6606 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6607 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6608 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6609 G_CALLBACK(compose_header_drag_received_cb),
6611 g_signal_connect(G_OBJECT(entry), "drag-drop",
6612 G_CALLBACK(compose_drag_drop),
6614 g_signal_connect(G_OBJECT(entry), "populate-popup",
6615 G_CALLBACK(compose_entry_popup_extend),
6618 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6620 headerentry->compose = compose;
6621 headerentry->combo = combo;
6622 headerentry->entry = entry;
6623 headerentry->button = button;
6624 headerentry->hbox = hbox;
6625 headerentry->headernum = compose->header_nextrow;
6626 headerentry->type = PREF_NONE;
6628 compose->header_nextrow++;
6629 compose->header_last = headerentry;
6630 compose->header_list =
6631 g_slist_append(compose->header_list,
6635 static void compose_add_header_entry(Compose *compose, const gchar *header,
6636 gchar *text, ComposePrefType pref_type)
6638 ComposeHeaderEntry *last_header = compose->header_last;
6639 gchar *tmp = g_strdup(text), *email;
6640 gboolean replyto_hdr;
6642 replyto_hdr = (!strcasecmp(header,
6643 prefs_common_translated_header_name("Reply-To:")) ||
6645 prefs_common_translated_header_name("Followup-To:")) ||
6647 prefs_common_translated_header_name("In-Reply-To:")));
6649 extract_address(tmp);
6650 email = g_utf8_strdown(tmp, -1);
6652 if (replyto_hdr == FALSE &&
6653 g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6655 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6656 header, text, (gint) pref_type);
6662 if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6663 gtk_entry_set_text(GTK_ENTRY(
6664 gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6666 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6667 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6668 last_header->type = pref_type;
6670 if (replyto_hdr == FALSE)
6671 g_hash_table_insert(compose->email_hashtable, email,
6672 GUINT_TO_POINTER(1));
6679 static void compose_destroy_headerentry(Compose *compose,
6680 ComposeHeaderEntry *headerentry)
6682 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6685 extract_address(text);
6686 email = g_utf8_strdown(text, -1);
6687 g_hash_table_remove(compose->email_hashtable, email);
6691 gtk_widget_destroy(headerentry->combo);
6692 gtk_widget_destroy(headerentry->entry);
6693 gtk_widget_destroy(headerentry->button);
6694 gtk_widget_destroy(headerentry->hbox);
6695 g_free(headerentry);
6698 static void compose_remove_header_entries(Compose *compose)
6701 for (list = compose->header_list; list; list = list->next)
6702 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6704 compose->header_last = NULL;
6705 g_slist_free(compose->header_list);
6706 compose->header_list = NULL;
6707 compose->header_nextrow = 1;
6708 compose_create_header_entry(compose);
6711 static GtkWidget *compose_create_header(Compose *compose)
6713 GtkWidget *from_optmenu_hbox;
6714 GtkWidget *header_scrolledwin;
6715 GtkWidget *header_table;
6719 /* header labels and entries */
6720 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6721 gtk_widget_show(header_scrolledwin);
6722 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6724 header_table = gtk_table_new(2, 2, FALSE);
6725 gtk_widget_show(header_table);
6726 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6727 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6728 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6731 /* option menu for selecting accounts */
6732 from_optmenu_hbox = compose_account_option_menu_create(compose);
6733 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6734 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6737 compose->header_table = header_table;
6738 compose->header_list = NULL;
6739 compose->header_nextrow = count;
6741 compose_create_header_entry(compose);
6743 compose->table = NULL;
6745 return header_scrolledwin ;
6748 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6750 Compose *compose = (Compose *)data;
6751 GdkEventButton event;
6754 event.time = gtk_get_current_event_time();
6756 return attach_button_pressed(compose->attach_clist, &event, compose);
6759 static GtkWidget *compose_create_attach(Compose *compose)
6761 GtkWidget *attach_scrwin;
6762 GtkWidget *attach_clist;
6764 GtkListStore *store;
6765 GtkCellRenderer *renderer;
6766 GtkTreeViewColumn *column;
6767 GtkTreeSelection *selection;
6769 /* attachment list */
6770 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6771 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6772 GTK_POLICY_AUTOMATIC,
6773 GTK_POLICY_AUTOMATIC);
6774 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6776 store = gtk_list_store_new(N_ATTACH_COLS,
6782 G_TYPE_AUTO_POINTER,
6784 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6785 (GTK_TREE_MODEL(store)));
6786 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6787 g_object_unref(store);
6789 renderer = gtk_cell_renderer_text_new();
6790 column = gtk_tree_view_column_new_with_attributes
6791 (_("Mime type"), renderer, "text",
6792 COL_MIMETYPE, NULL);
6793 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6795 renderer = gtk_cell_renderer_text_new();
6796 column = gtk_tree_view_column_new_with_attributes
6797 (_("Size"), renderer, "text",
6799 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6801 renderer = gtk_cell_renderer_text_new();
6802 column = gtk_tree_view_column_new_with_attributes
6803 (_("Name"), renderer, "text",
6805 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6807 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6808 prefs_common.use_stripes_everywhere);
6809 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6810 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6812 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6813 G_CALLBACK(attach_selected), compose);
6814 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6815 G_CALLBACK(attach_button_pressed), compose);
6817 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6818 G_CALLBACK(popup_attach_button_pressed), compose);
6820 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6821 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6822 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6823 G_CALLBACK(popup_attach_button_pressed), compose);
6825 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6826 G_CALLBACK(attach_key_pressed), compose);
6829 gtk_drag_dest_set(attach_clist,
6830 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6831 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6832 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6833 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6834 G_CALLBACK(compose_attach_drag_received_cb),
6836 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6837 G_CALLBACK(compose_drag_drop),
6840 compose->attach_scrwin = attach_scrwin;
6841 compose->attach_clist = attach_clist;
6843 return attach_scrwin;
6846 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6847 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6849 static GtkWidget *compose_create_others(Compose *compose)
6852 GtkWidget *savemsg_checkbtn;
6853 GtkWidget *savemsg_combo;
6854 GtkWidget *savemsg_select;
6857 gchar *folderidentifier;
6859 /* Table for settings */
6860 table = gtk_table_new(3, 1, FALSE);
6861 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6862 gtk_widget_show(table);
6863 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6866 /* Save Message to folder */
6867 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6868 gtk_widget_show(savemsg_checkbtn);
6869 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6870 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6871 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6873 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6874 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6876 savemsg_combo = gtk_combo_box_entry_new_text();
6877 compose->savemsg_checkbtn = savemsg_checkbtn;
6878 compose->savemsg_combo = savemsg_combo;
6879 gtk_widget_show(savemsg_combo);
6881 if (prefs_common.compose_save_to_history)
6882 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6883 prefs_common.compose_save_to_history);
6885 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6886 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6887 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6888 G_CALLBACK(compose_grab_focus_cb), compose);
6889 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6890 folderidentifier = folder_item_get_identifier(account_get_special_folder
6891 (compose->account, F_OUTBOX));
6892 compose_set_save_to(compose, folderidentifier);
6893 g_free(folderidentifier);
6896 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6897 gtk_widget_show(savemsg_select);
6898 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6899 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6900 G_CALLBACK(compose_savemsg_select_cb),
6908 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6910 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6911 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6914 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6919 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6922 path = folder_item_get_identifier(dest);
6924 compose_set_save_to(compose, path);
6928 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6929 GdkAtom clip, GtkTextIter *insert_place);
6932 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6936 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
6938 if (event->button == 3) {
6940 GtkTextIter sel_start, sel_end;
6941 gboolean stuff_selected;
6943 /* move the cursor to allow GtkAspell to check the word
6944 * under the mouse */
6945 if (event->x && event->y) {
6946 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6947 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6949 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6952 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6953 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6956 stuff_selected = gtk_text_buffer_get_selection_bounds(
6958 &sel_start, &sel_end);
6960 gtk_text_buffer_place_cursor (buffer, &iter);
6961 /* reselect stuff */
6963 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6964 gtk_text_buffer_select_range(buffer,
6965 &sel_start, &sel_end);
6967 return FALSE; /* pass the event so that the right-click goes through */
6970 if (event->button == 2) {
6975 /* get the middle-click position to paste at the correct place */
6976 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6977 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6979 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6982 entry_paste_clipboard(compose, text,
6983 prefs_common.linewrap_pastes,
6984 GDK_SELECTION_PRIMARY, &iter);
6992 static void compose_spell_menu_changed(void *data)
6994 Compose *compose = (Compose *)data;
6996 GtkWidget *menuitem;
6997 GtkWidget *parent_item;
6998 GtkMenu *menu = GTK_MENU(gtk_menu_new());
7001 if (compose->gtkaspell == NULL)
7004 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
7005 "/Menu/Spelling/Options");
7007 /* setting the submenu removes /Spelling/Options from the factory
7008 * so we need to save it */
7010 if (parent_item == NULL) {
7011 parent_item = compose->aspell_options_menu;
7012 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
7014 compose->aspell_options_menu = parent_item;
7016 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
7018 spell_menu = g_slist_reverse(spell_menu);
7019 for (items = spell_menu;
7020 items; items = items->next) {
7021 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
7022 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
7023 gtk_widget_show(GTK_WIDGET(menuitem));
7025 g_slist_free(spell_menu);
7027 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7028 gtk_widget_show(parent_item);
7031 static void compose_dict_changed(void *data)
7033 Compose *compose = (Compose *) data;
7035 if(compose->gtkaspell &&
7036 compose->gtkaspell->recheck_when_changing_dict == FALSE)
7039 gtkaspell_highlight_all(compose->gtkaspell);
7040 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7044 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7046 Compose *compose = (Compose *)data;
7047 GdkEventButton event;
7050 event.time = gtk_get_current_event_time();
7054 return text_clicked(compose->text, &event, compose);
7057 static gboolean compose_force_window_origin = TRUE;
7058 static Compose *compose_create(PrefsAccount *account,
7067 GtkWidget *handlebox;
7069 GtkWidget *notebook;
7071 GtkWidget *attach_hbox;
7072 GtkWidget *attach_lab1;
7073 GtkWidget *attach_lab2;
7078 GtkWidget *subject_hbox;
7079 GtkWidget *subject_frame;
7080 GtkWidget *subject_entry;
7084 GtkWidget *edit_vbox;
7085 GtkWidget *ruler_hbox;
7087 GtkWidget *scrolledwin;
7089 GtkTextBuffer *buffer;
7090 GtkClipboard *clipboard;
7092 UndoMain *undostruct;
7094 gchar *titles[N_ATTACH_COLS];
7095 GtkWidget *popupmenu;
7096 GtkWidget *tmpl_menu;
7097 GtkActionGroup *action_group = NULL;
7100 GtkAspell * gtkaspell = NULL;
7103 static GdkGeometry geometry;
7105 cm_return_val_if_fail(account != NULL, NULL);
7107 debug_print("Creating compose window...\n");
7108 compose = g_new0(Compose, 1);
7110 titles[COL_MIMETYPE] = _("MIME type");
7111 titles[COL_SIZE] = _("Size");
7112 titles[COL_NAME] = _("Name");
7113 titles[COL_CHARSET] = _("Charset");
7115 compose->batch = batch;
7116 compose->account = account;
7117 compose->folder = folder;
7119 compose->mutex = g_mutex_new();
7120 compose->set_cursor_pos = -1;
7122 #if !(GTK_CHECK_VERSION(2,12,0))
7123 compose->tooltips = tips;
7126 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7128 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7129 gtk_widget_set_size_request(window, prefs_common.compose_width,
7130 prefs_common.compose_height);
7132 if (!geometry.max_width) {
7133 geometry.max_width = gdk_screen_width();
7134 geometry.max_height = gdk_screen_height();
7137 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7138 &geometry, GDK_HINT_MAX_SIZE);
7139 if (!geometry.min_width) {
7140 geometry.min_width = 600;
7141 geometry.min_height = 440;
7143 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7144 &geometry, GDK_HINT_MIN_SIZE);
7146 #ifndef GENERIC_UMPC
7147 if (compose_force_window_origin)
7148 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7149 prefs_common.compose_y);
7151 g_signal_connect(G_OBJECT(window), "delete_event",
7152 G_CALLBACK(compose_delete_cb), compose);
7153 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7154 gtk_widget_realize(window);
7156 gtkut_widget_set_composer_icon(window);
7158 vbox = gtk_vbox_new(FALSE, 0);
7159 gtk_container_add(GTK_CONTAINER(window), vbox);
7161 compose->ui_manager = gtk_ui_manager_new();
7162 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7163 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7164 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7165 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7166 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7167 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7168 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7169 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7170 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7171 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7174 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7176 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7179 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7180 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7182 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7184 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7185 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7186 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7189 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7190 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7191 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7192 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7193 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7194 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7195 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7196 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7197 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7198 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM)
7199 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7200 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7203 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7204 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7205 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7207 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7208 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7209 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7211 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7212 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7213 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7214 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7216 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7218 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7219 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7220 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7221 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7222 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7223 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7224 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7225 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7226 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7227 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7228 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7229 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7230 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7231 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7232 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7234 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7236 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7237 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7238 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7239 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7240 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7242 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7244 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7248 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7249 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7250 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7251 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7252 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7253 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7257 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7258 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7259 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7260 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7261 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7263 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7264 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7265 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7266 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7267 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7270 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7271 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7272 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7273 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7274 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7275 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7276 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7278 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7279 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7280 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7281 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7282 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7284 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7286 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7287 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7288 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7289 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7290 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7292 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7293 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)
7294 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)
7295 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7297 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7299 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7300 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)
7301 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)
7303 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7305 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7306 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)
7307 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7309 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7310 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)
7311 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7313 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7315 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7316 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)
7317 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7318 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7319 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7321 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7322 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)
7323 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)
7324 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7325 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7327 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7328 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7329 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7330 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7331 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7332 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7334 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7335 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7336 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)
7338 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7339 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7340 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7344 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7345 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7346 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7347 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7348 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7349 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7352 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7354 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7355 gtk_widget_show_all(menubar);
7357 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7359 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7361 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7364 if (prefs_common.toolbar_detachable) {
7365 handlebox = gtk_handle_box_new();
7367 handlebox = gtk_hbox_new(FALSE, 0);
7369 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7371 gtk_widget_realize(handlebox);
7373 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7376 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7380 vbox2 = gtk_vbox_new(FALSE, 2);
7381 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7382 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7385 notebook = gtk_notebook_new();
7386 gtk_widget_set_size_request(notebook, -1, 130);
7387 gtk_widget_show(notebook);
7389 /* header labels and entries */
7390 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7391 compose_create_header(compose),
7392 gtk_label_new_with_mnemonic(_("Hea_der")));
7393 /* attachment list */
7394 attach_hbox = gtk_hbox_new(FALSE, 0);
7395 gtk_widget_show(attach_hbox);
7397 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7398 gtk_widget_show(attach_lab1);
7399 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7401 attach_lab2 = gtk_label_new("");
7402 gtk_widget_show(attach_lab2);
7403 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7405 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7406 compose_create_attach(compose),
7409 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7410 compose_create_others(compose),
7411 gtk_label_new_with_mnemonic(_("Othe_rs")));
7414 subject_hbox = gtk_hbox_new(FALSE, 0);
7415 gtk_widget_show(subject_hbox);
7417 subject_frame = gtk_frame_new(NULL);
7418 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7419 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7420 gtk_widget_show(subject_frame);
7422 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7423 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7424 gtk_widget_show(subject);
7426 label = gtk_label_new(_("Subject:"));
7427 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7428 gtk_widget_show(label);
7431 subject_entry = claws_spell_entry_new();
7433 subject_entry = gtk_entry_new();
7435 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7436 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7437 G_CALLBACK(compose_grab_focus_cb), compose);
7438 gtk_widget_show(subject_entry);
7439 compose->subject_entry = subject_entry;
7440 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7442 edit_vbox = gtk_vbox_new(FALSE, 0);
7444 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7447 ruler_hbox = gtk_hbox_new(FALSE, 0);
7448 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7450 ruler = gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL);
7451 gtk_shruler_set_range(GTK_SHRULER(ruler), 0.0, 100.0, 1.0);
7452 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7456 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7457 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7458 GTK_POLICY_AUTOMATIC,
7459 GTK_POLICY_AUTOMATIC);
7460 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7462 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7464 text = gtk_text_view_new();
7465 if (prefs_common.show_compose_margin) {
7466 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7467 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7469 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7470 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7471 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7472 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7473 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7475 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7476 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7477 G_CALLBACK(compose_edit_size_alloc),
7479 g_signal_connect(G_OBJECT(buffer), "changed",
7480 G_CALLBACK(compose_changed_cb), compose);
7481 g_signal_connect(G_OBJECT(text), "grab_focus",
7482 G_CALLBACK(compose_grab_focus_cb), compose);
7483 g_signal_connect(G_OBJECT(buffer), "insert_text",
7484 G_CALLBACK(text_inserted), compose);
7485 g_signal_connect(G_OBJECT(text), "button_press_event",
7486 G_CALLBACK(text_clicked), compose);
7488 g_signal_connect(G_OBJECT(text), "popup-menu",
7489 G_CALLBACK(compose_popup_menu), compose);
7491 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7492 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7493 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7494 G_CALLBACK(compose_popup_menu), compose);
7496 g_signal_connect(G_OBJECT(subject_entry), "changed",
7497 G_CALLBACK(compose_changed_cb), compose);
7500 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7501 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7502 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7503 g_signal_connect(G_OBJECT(text), "drag_data_received",
7504 G_CALLBACK(compose_insert_drag_received_cb),
7506 g_signal_connect(G_OBJECT(text), "drag-drop",
7507 G_CALLBACK(compose_drag_drop),
7509 gtk_widget_show_all(vbox);
7511 /* pane between attach clist and text */
7512 paned = gtk_vpaned_new();
7513 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7515 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7516 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7518 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7520 gtk_paned_add1(GTK_PANED(paned), notebook);
7521 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7522 gtk_widget_show_all(paned);
7525 if (prefs_common.textfont) {
7526 PangoFontDescription *font_desc;
7528 font_desc = pango_font_description_from_string
7529 (prefs_common.textfont);
7531 gtk_widget_modify_font(text, font_desc);
7532 pango_font_description_free(font_desc);
7536 gtk_action_group_add_actions(action_group, compose_popup_entries,
7537 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7538 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7539 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7540 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7541 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7542 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7543 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7545 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7547 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7548 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7549 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7551 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7553 undostruct = undo_init(text);
7554 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7557 address_completion_start(window);
7559 compose->window = window;
7560 compose->vbox = vbox;
7561 compose->menubar = menubar;
7562 compose->handlebox = handlebox;
7564 compose->vbox2 = vbox2;
7566 compose->paned = paned;
7568 compose->attach_label = attach_lab2;
7570 compose->notebook = notebook;
7571 compose->edit_vbox = edit_vbox;
7572 compose->ruler_hbox = ruler_hbox;
7573 compose->ruler = ruler;
7574 compose->scrolledwin = scrolledwin;
7575 compose->text = text;
7577 compose->focused_editable = NULL;
7579 compose->popupmenu = popupmenu;
7581 compose->tmpl_menu = tmpl_menu;
7583 compose->mode = mode;
7584 compose->rmode = mode;
7586 compose->targetinfo = NULL;
7587 compose->replyinfo = NULL;
7588 compose->fwdinfo = NULL;
7590 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7591 g_str_equal, (GDestroyNotify) g_free, NULL);
7593 compose->replyto = NULL;
7595 compose->bcc = NULL;
7596 compose->followup_to = NULL;
7598 compose->ml_post = NULL;
7600 compose->inreplyto = NULL;
7601 compose->references = NULL;
7602 compose->msgid = NULL;
7603 compose->boundary = NULL;
7605 compose->autowrap = prefs_common.autowrap;
7606 compose->autoindent = prefs_common.auto_indent;
7607 compose->use_signing = FALSE;
7608 compose->use_encryption = FALSE;
7609 compose->privacy_system = NULL;
7611 compose->modified = FALSE;
7613 compose->return_receipt = FALSE;
7615 compose->to_list = NULL;
7616 compose->newsgroup_list = NULL;
7618 compose->undostruct = undostruct;
7620 compose->sig_str = NULL;
7622 compose->exteditor_file = NULL;
7623 compose->exteditor_pid = -1;
7624 compose->exteditor_tag = -1;
7625 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7628 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7629 if (mode != COMPOSE_REDIRECT) {
7630 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7631 strcmp(prefs_common.dictionary, "")) {
7632 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7633 prefs_common.alt_dictionary,
7634 conv_get_locale_charset_str(),
7635 prefs_common.misspelled_col,
7636 prefs_common.check_while_typing,
7637 prefs_common.recheck_when_changing_dict,
7638 prefs_common.use_alternate,
7639 prefs_common.use_both_dicts,
7640 GTK_TEXT_VIEW(text),
7641 GTK_WINDOW(compose->window),
7642 compose_dict_changed,
7643 compose_spell_menu_changed,
7646 alertpanel_error(_("Spell checker could not "
7648 gtkaspell_checkers_strerror());
7649 gtkaspell_checkers_reset_error();
7651 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7655 compose->gtkaspell = gtkaspell;
7656 compose_spell_menu_changed(compose);
7657 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7660 compose_select_account(compose, account, TRUE);
7662 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7663 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7665 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7666 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7668 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7669 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7671 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7672 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7674 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7675 if (account->protocol != A_NNTP)
7676 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7677 prefs_common_translated_header_name("To:"));
7679 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7680 prefs_common_translated_header_name("Newsgroups:"));
7682 addressbook_set_target_compose(compose);
7684 if (mode != COMPOSE_REDIRECT)
7685 compose_set_template_menu(compose);
7687 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7690 compose_list = g_list_append(compose_list, compose);
7692 if (!prefs_common.show_ruler)
7693 gtk_widget_hide(ruler_hbox);
7695 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7698 compose->priority = PRIORITY_NORMAL;
7699 compose_update_priority_menu_item(compose);
7701 compose_set_out_encoding(compose);
7704 compose_update_actions_menu(compose);
7706 /* Privacy Systems menu */
7707 compose_update_privacy_systems_menu(compose);
7709 activate_privacy_system(compose, account, TRUE);
7710 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7712 gtk_widget_realize(window);
7714 gtk_widget_show(window);
7716 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7717 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7724 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7729 GtkWidget *optmenubox;
7732 GtkWidget *from_name = NULL;
7733 #if !(GTK_CHECK_VERSION(2,12,0))
7734 GtkTooltips *tips = compose->tooltips;
7737 gint num = 0, def_menu = 0;
7739 accounts = account_get_list();
7740 cm_return_val_if_fail(accounts != NULL, NULL);
7742 optmenubox = gtk_event_box_new();
7743 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7744 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7746 hbox = gtk_hbox_new(FALSE, 6);
7747 from_name = gtk_entry_new();
7749 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7750 G_CALLBACK(compose_grab_focus_cb), compose);
7752 for (; accounts != NULL; accounts = accounts->next, num++) {
7753 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7754 gchar *name, *from = NULL;
7756 if (ac == compose->account) def_menu = num;
7758 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7761 if (ac == compose->account) {
7762 if (ac->name && *ac->name) {
7764 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7765 from = g_strdup_printf("%s <%s>",
7767 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7769 from = g_strdup_printf("%s",
7771 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7774 COMBOBOX_ADD(menu, name, ac->account_id);
7779 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7781 g_signal_connect(G_OBJECT(optmenu), "changed",
7782 G_CALLBACK(account_activated),
7784 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7785 G_CALLBACK(compose_entry_popup_extend),
7788 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7789 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7791 CLAWS_SET_TIP(optmenubox,
7792 _("Account to use for this email"));
7793 CLAWS_SET_TIP(from_name,
7794 _("Sender address to be used"));
7796 compose->account_combo = optmenu;
7797 compose->from_name = from_name;
7802 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7804 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7805 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7806 Compose *compose = (Compose *) data;
7808 compose->priority = value;
7812 static void compose_reply_change_mode(Compose *compose,
7815 gboolean was_modified = compose->modified;
7817 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7819 cm_return_if_fail(compose->replyinfo != NULL);
7821 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7823 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7825 if (action == COMPOSE_REPLY_TO_ALL)
7827 if (action == COMPOSE_REPLY_TO_SENDER)
7829 if (action == COMPOSE_REPLY_TO_LIST)
7832 compose_remove_header_entries(compose);
7833 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7834 if (compose->account->set_autocc && compose->account->auto_cc)
7835 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7837 if (compose->account->set_autobcc && compose->account->auto_bcc)
7838 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7840 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7841 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7842 compose_show_first_last_header(compose, TRUE);
7843 compose->modified = was_modified;
7844 compose_set_title(compose);
7847 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7849 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7850 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7851 Compose *compose = (Compose *) data;
7854 compose_reply_change_mode(compose, value);
7857 static void compose_update_priority_menu_item(Compose * compose)
7859 GtkWidget *menuitem = NULL;
7860 switch (compose->priority) {
7861 case PRIORITY_HIGHEST:
7862 menuitem = gtk_ui_manager_get_widget
7863 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7866 menuitem = gtk_ui_manager_get_widget
7867 (compose->ui_manager, "/Menu/Options/Priority/High");
7869 case PRIORITY_NORMAL:
7870 menuitem = gtk_ui_manager_get_widget
7871 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7874 menuitem = gtk_ui_manager_get_widget
7875 (compose->ui_manager, "/Menu/Options/Priority/Low");
7877 case PRIORITY_LOWEST:
7878 menuitem = gtk_ui_manager_get_widget
7879 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7882 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7885 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7887 Compose *compose = (Compose *) data;
7889 gboolean can_sign = FALSE, can_encrypt = FALSE;
7891 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7893 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
7896 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7897 g_free(compose->privacy_system);
7898 compose->privacy_system = NULL;
7899 if (systemid != NULL) {
7900 compose->privacy_system = g_strdup(systemid);
7902 can_sign = privacy_system_can_sign(systemid);
7903 can_encrypt = privacy_system_can_encrypt(systemid);
7906 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7908 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7909 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7912 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7914 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7915 GtkWidget *menuitem = NULL;
7916 GList *children, *amenu;
7917 gboolean can_sign = FALSE, can_encrypt = FALSE;
7918 gboolean found = FALSE;
7920 if (compose->privacy_system != NULL) {
7922 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7923 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7924 cm_return_if_fail(menuitem != NULL);
7926 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
7929 while (amenu != NULL) {
7930 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7931 if (systemid != NULL) {
7932 if (strcmp(systemid, compose->privacy_system) == 0 &&
7933 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7934 menuitem = GTK_WIDGET(amenu->data);
7936 can_sign = privacy_system_can_sign(systemid);
7937 can_encrypt = privacy_system_can_encrypt(systemid);
7941 } else if (strlen(compose->privacy_system) == 0 &&
7942 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7943 menuitem = GTK_WIDGET(amenu->data);
7946 can_encrypt = FALSE;
7951 amenu = amenu->next;
7953 g_list_free(children);
7954 if (menuitem != NULL)
7955 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7957 if (warn && !found && strlen(compose->privacy_system)) {
7958 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7959 "will not be able to sign or encrypt this message."),
7960 compose->privacy_system);
7964 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7965 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7968 static void compose_set_out_encoding(Compose *compose)
7970 CharSet out_encoding;
7971 const gchar *branch = NULL;
7972 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7974 switch(out_encoding) {
7975 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7976 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7977 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7978 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7979 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7980 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7981 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7982 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7983 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7984 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7985 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7986 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7987 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7988 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7989 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7990 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7991 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7992 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7993 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7994 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7995 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7996 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7997 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7998 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
7999 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
8000 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
8001 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
8002 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
8003 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
8004 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
8005 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
8006 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
8007 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8009 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8012 static void compose_set_template_menu(Compose *compose)
8014 GSList *tmpl_list, *cur;
8018 tmpl_list = template_get_config();
8020 menu = gtk_menu_new();
8022 gtk_menu_set_accel_group (GTK_MENU (menu),
8023 gtk_ui_manager_get_accel_group(compose->ui_manager));
8024 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8025 Template *tmpl = (Template *)cur->data;
8026 gchar *accel_path = NULL;
8027 item = gtk_menu_item_new_with_label(tmpl->name);
8028 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8029 g_signal_connect(G_OBJECT(item), "activate",
8030 G_CALLBACK(compose_template_activate_cb),
8032 g_object_set_data(G_OBJECT(item), "template", tmpl);
8033 gtk_widget_show(item);
8034 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8035 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8039 gtk_widget_show(menu);
8040 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8043 void compose_update_actions_menu(Compose *compose)
8045 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8048 static void compose_update_privacy_systems_menu(Compose *compose)
8050 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8051 GSList *systems, *cur;
8053 GtkWidget *system_none;
8055 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8056 GtkWidget *privacy_menu = gtk_menu_new();
8058 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8059 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8061 g_signal_connect(G_OBJECT(system_none), "activate",
8062 G_CALLBACK(compose_set_privacy_system_cb), compose);
8064 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8065 gtk_widget_show(system_none);
8067 systems = privacy_get_system_ids();
8068 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8069 gchar *systemid = cur->data;
8071 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8072 widget = gtk_radio_menu_item_new_with_label(group,
8073 privacy_system_get_name(systemid));
8074 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8075 g_strdup(systemid), g_free);
8076 g_signal_connect(G_OBJECT(widget), "activate",
8077 G_CALLBACK(compose_set_privacy_system_cb), compose);
8079 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8080 gtk_widget_show(widget);
8083 g_slist_free(systems);
8084 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8085 gtk_widget_show_all(privacy_menu);
8086 gtk_widget_show_all(privacy_menuitem);
8089 void compose_reflect_prefs_all(void)
8094 for (cur = compose_list; cur != NULL; cur = cur->next) {
8095 compose = (Compose *)cur->data;
8096 compose_set_template_menu(compose);
8100 void compose_reflect_prefs_pixmap_theme(void)
8105 for (cur = compose_list; cur != NULL; cur = cur->next) {
8106 compose = (Compose *)cur->data;
8107 toolbar_update(TOOLBAR_COMPOSE, compose);
8111 static const gchar *compose_quote_char_from_context(Compose *compose)
8113 const gchar *qmark = NULL;
8115 cm_return_val_if_fail(compose != NULL, NULL);
8117 switch (compose->mode) {
8118 /* use forward-specific quote char */
8119 case COMPOSE_FORWARD:
8120 case COMPOSE_FORWARD_AS_ATTACH:
8121 case COMPOSE_FORWARD_INLINE:
8122 if (compose->folder && compose->folder->prefs &&
8123 compose->folder->prefs->forward_with_format)
8124 qmark = compose->folder->prefs->forward_quotemark;
8125 else if (compose->account->forward_with_format)
8126 qmark = compose->account->forward_quotemark;
8128 qmark = prefs_common.fw_quotemark;
8131 /* use reply-specific quote char in all other modes */
8133 if (compose->folder && compose->folder->prefs &&
8134 compose->folder->prefs->reply_with_format)
8135 qmark = compose->folder->prefs->reply_quotemark;
8136 else if (compose->account->reply_with_format)
8137 qmark = compose->account->reply_quotemark;
8139 qmark = prefs_common.quotemark;
8143 if (qmark == NULL || *qmark == '\0')
8149 static void compose_template_apply(Compose *compose, Template *tmpl,
8153 GtkTextBuffer *buffer;
8157 gchar *parsed_str = NULL;
8158 gint cursor_pos = 0;
8159 const gchar *err_msg = _("The body of the template has an error at line %d.");
8162 /* process the body */
8164 text = GTK_TEXT_VIEW(compose->text);
8165 buffer = gtk_text_view_get_buffer(text);
8168 qmark = compose_quote_char_from_context(compose);
8170 if (compose->replyinfo != NULL) {
8173 gtk_text_buffer_set_text(buffer, "", -1);
8174 mark = gtk_text_buffer_get_insert(buffer);
8175 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8177 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8178 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8180 } else if (compose->fwdinfo != NULL) {
8183 gtk_text_buffer_set_text(buffer, "", -1);
8184 mark = gtk_text_buffer_get_insert(buffer);
8185 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8187 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8188 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8191 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8193 GtkTextIter start, end;
8196 gtk_text_buffer_get_start_iter(buffer, &start);
8197 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8198 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8200 /* clear the buffer now */
8202 gtk_text_buffer_set_text(buffer, "", -1);
8204 parsed_str = compose_quote_fmt(compose, dummyinfo,
8205 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8206 procmsg_msginfo_free( dummyinfo );
8212 gtk_text_buffer_set_text(buffer, "", -1);
8213 mark = gtk_text_buffer_get_insert(buffer);
8214 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8217 if (replace && parsed_str && compose->account->auto_sig)
8218 compose_insert_sig(compose, FALSE);
8220 if (replace && parsed_str) {
8221 gtk_text_buffer_get_start_iter(buffer, &iter);
8222 gtk_text_buffer_place_cursor(buffer, &iter);
8226 cursor_pos = quote_fmt_get_cursor_pos();
8227 compose->set_cursor_pos = cursor_pos;
8228 if (cursor_pos == -1)
8230 gtk_text_buffer_get_start_iter(buffer, &iter);
8231 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8232 gtk_text_buffer_place_cursor(buffer, &iter);
8235 /* process the other fields */
8237 compose_template_apply_fields(compose, tmpl);
8238 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8239 quote_fmt_reset_vartable();
8240 compose_changed_cb(NULL, compose);
8243 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8244 gtkaspell_highlight_all(compose->gtkaspell);
8248 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8250 MsgInfo* dummyinfo = NULL;
8251 MsgInfo *msginfo = NULL;
8254 if (compose->replyinfo != NULL)
8255 msginfo = compose->replyinfo;
8256 else if (compose->fwdinfo != NULL)
8257 msginfo = compose->fwdinfo;
8259 dummyinfo = compose_msginfo_new_from_compose(compose);
8260 msginfo = dummyinfo;
8263 if (tmpl->from && *tmpl->from != '\0') {
8265 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8266 compose->gtkaspell);
8268 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8270 quote_fmt_scan_string(tmpl->from);
8273 buf = quote_fmt_get_buffer();
8275 alertpanel_error(_("Template From format error."));
8277 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8281 if (tmpl->to && *tmpl->to != '\0') {
8283 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8284 compose->gtkaspell);
8286 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8288 quote_fmt_scan_string(tmpl->to);
8291 buf = quote_fmt_get_buffer();
8293 alertpanel_error(_("Template To format error."));
8295 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8299 if (tmpl->cc && *tmpl->cc != '\0') {
8301 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8302 compose->gtkaspell);
8304 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8306 quote_fmt_scan_string(tmpl->cc);
8309 buf = quote_fmt_get_buffer();
8311 alertpanel_error(_("Template Cc format error."));
8313 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8317 if (tmpl->bcc && *tmpl->bcc != '\0') {
8319 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8320 compose->gtkaspell);
8322 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8324 quote_fmt_scan_string(tmpl->bcc);
8327 buf = quote_fmt_get_buffer();
8329 alertpanel_error(_("Template Bcc format error."));
8331 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8335 /* process the subject */
8336 if (tmpl->subject && *tmpl->subject != '\0') {
8338 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8339 compose->gtkaspell);
8341 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8343 quote_fmt_scan_string(tmpl->subject);
8346 buf = quote_fmt_get_buffer();
8348 alertpanel_error(_("Template subject format error."));
8350 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8354 procmsg_msginfo_free( dummyinfo );
8357 static void compose_destroy(Compose *compose)
8359 GtkAllocation allocation;
8360 GtkTextBuffer *buffer;
8361 GtkClipboard *clipboard;
8363 compose_list = g_list_remove(compose_list, compose);
8365 if (compose->updating) {
8366 debug_print("danger, not destroying anything now\n");
8367 compose->deferred_destroy = TRUE;
8370 /* NOTE: address_completion_end() does nothing with the window
8371 * however this may change. */
8372 address_completion_end(compose->window);
8374 slist_free_strings(compose->to_list);
8375 g_slist_free(compose->to_list);
8376 slist_free_strings(compose->newsgroup_list);
8377 g_slist_free(compose->newsgroup_list);
8378 slist_free_strings(compose->header_list);
8379 g_slist_free(compose->header_list);
8381 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8383 g_hash_table_destroy(compose->email_hashtable);
8385 procmsg_msginfo_free(compose->targetinfo);
8386 procmsg_msginfo_free(compose->replyinfo);
8387 procmsg_msginfo_free(compose->fwdinfo);
8389 g_free(compose->replyto);
8390 g_free(compose->cc);
8391 g_free(compose->bcc);
8392 g_free(compose->newsgroups);
8393 g_free(compose->followup_to);
8395 g_free(compose->ml_post);
8397 g_free(compose->inreplyto);
8398 g_free(compose->references);
8399 g_free(compose->msgid);
8400 g_free(compose->boundary);
8402 g_free(compose->redirect_filename);
8403 if (compose->undostruct)
8404 undo_destroy(compose->undostruct);
8406 g_free(compose->sig_str);
8408 g_free(compose->exteditor_file);
8410 g_free(compose->orig_charset);
8412 g_free(compose->privacy_system);
8414 if (addressbook_get_target_compose() == compose)
8415 addressbook_set_target_compose(NULL);
8418 if (compose->gtkaspell) {
8419 gtkaspell_delete(compose->gtkaspell);
8420 compose->gtkaspell = NULL;
8424 if (!compose->batch) {
8425 gtk_widget_get_allocation(compose->window, &allocation);
8426 prefs_common.compose_width = allocation.width;
8427 prefs_common.compose_height = allocation.height;
8430 if (!gtk_widget_get_parent(compose->paned))
8431 gtk_widget_destroy(compose->paned);
8432 gtk_widget_destroy(compose->popupmenu);
8434 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8435 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8436 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8438 gtk_widget_destroy(compose->window);
8439 toolbar_destroy(compose->toolbar);
8440 g_free(compose->toolbar);
8441 g_mutex_free(compose->mutex);
8445 static void compose_attach_info_free(AttachInfo *ainfo)
8447 g_free(ainfo->file);
8448 g_free(ainfo->content_type);
8449 g_free(ainfo->name);
8450 g_free(ainfo->charset);
8454 static void compose_attach_update_label(Compose *compose)
8459 GtkTreeModel *model;
8464 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8465 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8466 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8470 while(gtk_tree_model_iter_next(model, &iter))
8473 text = g_strdup_printf("(%d)", i);
8474 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8478 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8480 Compose *compose = (Compose *)data;
8481 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8482 GtkTreeSelection *selection;
8484 GtkTreeModel *model;
8486 selection = gtk_tree_view_get_selection(tree_view);
8487 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8492 for (cur = sel; cur != NULL; cur = cur->next) {
8493 GtkTreePath *path = cur->data;
8494 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8497 gtk_tree_path_free(path);
8500 for (cur = sel; cur != NULL; cur = cur->next) {
8501 GtkTreeRowReference *ref = cur->data;
8502 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8505 if (gtk_tree_model_get_iter(model, &iter, path))
8506 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8508 gtk_tree_path_free(path);
8509 gtk_tree_row_reference_free(ref);
8513 compose_attach_update_label(compose);
8516 static struct _AttachProperty
8519 GtkWidget *mimetype_entry;
8520 GtkWidget *encoding_optmenu;
8521 GtkWidget *path_entry;
8522 GtkWidget *filename_entry;
8524 GtkWidget *cancel_btn;
8527 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8529 gtk_tree_path_free((GtkTreePath *)ptr);
8532 static void compose_attach_property(GtkAction *action, gpointer data)
8534 Compose *compose = (Compose *)data;
8535 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8537 GtkComboBox *optmenu;
8538 GtkTreeSelection *selection;
8540 GtkTreeModel *model;
8543 static gboolean cancelled;
8545 /* only if one selected */
8546 selection = gtk_tree_view_get_selection(tree_view);
8547 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8550 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8554 path = (GtkTreePath *) sel->data;
8555 gtk_tree_model_get_iter(model, &iter, path);
8556 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8559 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8565 if (!attach_prop.window)
8566 compose_attach_property_create(&cancelled);
8567 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8568 gtk_widget_grab_focus(attach_prop.ok_btn);
8569 gtk_widget_show(attach_prop.window);
8570 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8572 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8573 if (ainfo->encoding == ENC_UNKNOWN)
8574 combobox_select_by_data(optmenu, ENC_BASE64);
8576 combobox_select_by_data(optmenu, ainfo->encoding);
8578 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8579 ainfo->content_type ? ainfo->content_type : "");
8580 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8581 ainfo->file ? ainfo->file : "");
8582 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8583 ainfo->name ? ainfo->name : "");
8586 const gchar *entry_text;
8588 gchar *cnttype = NULL;
8595 gtk_widget_hide(attach_prop.window);
8596 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8601 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8602 if (*entry_text != '\0') {
8605 text = g_strstrip(g_strdup(entry_text));
8606 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8607 cnttype = g_strdup(text);
8610 alertpanel_error(_("Invalid MIME type."));
8616 ainfo->encoding = combobox_get_active_data(optmenu);
8618 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8619 if (*entry_text != '\0') {
8620 if (is_file_exist(entry_text) &&
8621 (size = get_file_size(entry_text)) > 0)
8622 file = g_strdup(entry_text);
8625 (_("File doesn't exist or is empty."));
8631 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8632 if (*entry_text != '\0') {
8633 g_free(ainfo->name);
8634 ainfo->name = g_strdup(entry_text);
8638 g_free(ainfo->content_type);
8639 ainfo->content_type = cnttype;
8642 g_free(ainfo->file);
8646 ainfo->size = (goffset)size;
8648 /* update tree store */
8649 text = to_human_readable(ainfo->size);
8650 gtk_tree_model_get_iter(model, &iter, path);
8651 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8652 COL_MIMETYPE, ainfo->content_type,
8654 COL_NAME, ainfo->name,
8655 COL_CHARSET, ainfo->charset,
8661 gtk_tree_path_free(path);
8664 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8666 label = gtk_label_new(str); \
8667 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8668 GTK_FILL, 0, 0, 0); \
8669 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8671 entry = gtk_entry_new(); \
8672 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8673 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8676 static void compose_attach_property_create(gboolean *cancelled)
8682 GtkWidget *mimetype_entry;
8685 GtkListStore *optmenu_menu;
8686 GtkWidget *path_entry;
8687 GtkWidget *filename_entry;
8690 GtkWidget *cancel_btn;
8691 GList *mime_type_list, *strlist;
8694 debug_print("Creating attach_property window...\n");
8696 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8697 gtk_widget_set_size_request(window, 480, -1);
8698 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8699 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8700 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8701 g_signal_connect(G_OBJECT(window), "delete_event",
8702 G_CALLBACK(attach_property_delete_event),
8704 g_signal_connect(G_OBJECT(window), "key_press_event",
8705 G_CALLBACK(attach_property_key_pressed),
8708 vbox = gtk_vbox_new(FALSE, 8);
8709 gtk_container_add(GTK_CONTAINER(window), vbox);
8711 table = gtk_table_new(4, 2, FALSE);
8712 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8713 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8714 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8716 label = gtk_label_new(_("MIME type"));
8717 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8719 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8720 mimetype_entry = gtk_combo_box_entry_new_text();
8721 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8722 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8724 /* stuff with list */
8725 mime_type_list = procmime_get_mime_type_list();
8727 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8728 MimeType *type = (MimeType *) mime_type_list->data;
8731 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8733 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8736 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8737 (GCompareFunc)strcmp2);
8740 for (mime_type_list = strlist; mime_type_list != NULL;
8741 mime_type_list = mime_type_list->next) {
8742 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8743 g_free(mime_type_list->data);
8745 g_list_free(strlist);
8746 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8747 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8749 label = gtk_label_new(_("Encoding"));
8750 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8752 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8754 hbox = gtk_hbox_new(FALSE, 0);
8755 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8756 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8758 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8759 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8761 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8762 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8763 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8764 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8765 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8767 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8769 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8770 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8772 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8773 &ok_btn, GTK_STOCK_OK,
8775 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8776 gtk_widget_grab_default(ok_btn);
8778 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8779 G_CALLBACK(attach_property_ok),
8781 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8782 G_CALLBACK(attach_property_cancel),
8785 gtk_widget_show_all(vbox);
8787 attach_prop.window = window;
8788 attach_prop.mimetype_entry = mimetype_entry;
8789 attach_prop.encoding_optmenu = optmenu;
8790 attach_prop.path_entry = path_entry;
8791 attach_prop.filename_entry = filename_entry;
8792 attach_prop.ok_btn = ok_btn;
8793 attach_prop.cancel_btn = cancel_btn;
8796 #undef SET_LABEL_AND_ENTRY
8798 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8804 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8810 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8811 gboolean *cancelled)
8819 static gboolean attach_property_key_pressed(GtkWidget *widget,
8821 gboolean *cancelled)
8823 if (event && event->keyval == GDK_KEY_Escape) {
8827 if (event && event->keyval == GDK_KEY_Return) {
8835 static void compose_exec_ext_editor(Compose *compose)
8842 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8843 G_DIR_SEPARATOR, compose);
8845 if (pipe(pipe_fds) < 0) {
8851 if ((pid = fork()) < 0) {
8858 /* close the write side of the pipe */
8861 compose->exteditor_file = g_strdup(tmp);
8862 compose->exteditor_pid = pid;
8864 compose_set_ext_editor_sensitive(compose, FALSE);
8867 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8869 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8871 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8875 } else { /* process-monitoring process */
8881 /* close the read side of the pipe */
8884 if (compose_write_body_to_file(compose, tmp) < 0) {
8885 fd_write_all(pipe_fds[1], "2\n", 2);
8889 pid_ed = compose_exec_ext_editor_real(tmp);
8891 fd_write_all(pipe_fds[1], "1\n", 2);
8895 /* wait until editor is terminated */
8896 waitpid(pid_ed, NULL, 0);
8898 fd_write_all(pipe_fds[1], "0\n", 2);
8905 #endif /* G_OS_UNIX */
8909 static gint compose_exec_ext_editor_real(const gchar *file)
8916 cm_return_val_if_fail(file != NULL, -1);
8918 if ((pid = fork()) < 0) {
8923 if (pid != 0) return pid;
8925 /* grandchild process */
8927 if (setpgid(0, getppid()))
8930 if (prefs_common_get_ext_editor_cmd() &&
8931 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8932 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8933 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8935 if (prefs_common_get_ext_editor_cmd())
8936 g_warning("External editor command-line is invalid: '%s'\n",
8937 prefs_common_get_ext_editor_cmd());
8938 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8941 cmdline = strsplit_with_quote(buf, " ", 1024);
8942 execvp(cmdline[0], cmdline);
8945 g_strfreev(cmdline);
8950 static gboolean compose_ext_editor_kill(Compose *compose)
8952 pid_t pgid = compose->exteditor_pid * -1;
8955 ret = kill(pgid, 0);
8957 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8961 msg = g_strdup_printf
8962 (_("The external editor is still working.\n"
8963 "Force terminating the process?\n"
8964 "process group id: %d"), -pgid);
8965 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8966 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8970 if (val == G_ALERTALTERNATE) {
8971 g_source_remove(compose->exteditor_tag);
8972 g_io_channel_shutdown(compose->exteditor_ch,
8974 g_io_channel_unref(compose->exteditor_ch);
8976 if (kill(pgid, SIGTERM) < 0) perror("kill");
8977 waitpid(compose->exteditor_pid, NULL, 0);
8979 g_warning("Terminated process group id: %d", -pgid);
8980 g_warning("Temporary file: %s",
8981 compose->exteditor_file);
8983 compose_set_ext_editor_sensitive(compose, TRUE);
8985 g_free(compose->exteditor_file);
8986 compose->exteditor_file = NULL;
8987 compose->exteditor_pid = -1;
8988 compose->exteditor_ch = NULL;
8989 compose->exteditor_tag = -1;
8997 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
9001 Compose *compose = (Compose *)data;
9004 debug_print("Compose: input from monitoring process\n");
9006 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
9008 g_io_channel_shutdown(source, FALSE, NULL);
9009 g_io_channel_unref(source);
9011 waitpid(compose->exteditor_pid, NULL, 0);
9013 if (buf[0] == '0') { /* success */
9014 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9015 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9017 gtk_text_buffer_set_text(buffer, "", -1);
9018 compose_insert_file(compose, compose->exteditor_file);
9019 compose_changed_cb(NULL, compose);
9020 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9022 if (claws_unlink(compose->exteditor_file) < 0)
9023 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9024 } else if (buf[0] == '1') { /* failed */
9025 g_warning("Couldn't exec external editor\n");
9026 if (claws_unlink(compose->exteditor_file) < 0)
9027 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9028 } else if (buf[0] == '2') {
9029 g_warning("Couldn't write to file\n");
9030 } else if (buf[0] == '3') {
9031 g_warning("Pipe read failed\n");
9034 compose_set_ext_editor_sensitive(compose, TRUE);
9036 g_free(compose->exteditor_file);
9037 compose->exteditor_file = NULL;
9038 compose->exteditor_pid = -1;
9039 compose->exteditor_ch = NULL;
9040 compose->exteditor_tag = -1;
9045 static void compose_set_ext_editor_sensitive(Compose *compose,
9048 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9049 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9050 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9051 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9052 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9053 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9054 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9056 gtk_widget_set_sensitive(compose->text, sensitive);
9057 if (compose->toolbar->send_btn)
9058 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
9059 if (compose->toolbar->sendl_btn)
9060 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
9061 if (compose->toolbar->draft_btn)
9062 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
9063 if (compose->toolbar->insert_btn)
9064 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
9065 if (compose->toolbar->sig_btn)
9066 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
9067 if (compose->toolbar->exteditor_btn)
9068 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9069 if (compose->toolbar->linewrap_current_btn)
9070 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9071 if (compose->toolbar->linewrap_all_btn)
9072 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9074 #endif /* G_OS_UNIX */
9077 * compose_undo_state_changed:
9079 * Change the sensivity of the menuentries undo and redo
9081 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9082 gint redo_state, gpointer data)
9084 Compose *compose = (Compose *)data;
9086 switch (undo_state) {
9087 case UNDO_STATE_TRUE:
9088 if (!undostruct->undo_state) {
9089 undostruct->undo_state = TRUE;
9090 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9093 case UNDO_STATE_FALSE:
9094 if (undostruct->undo_state) {
9095 undostruct->undo_state = FALSE;
9096 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9099 case UNDO_STATE_UNCHANGED:
9101 case UNDO_STATE_REFRESH:
9102 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9105 g_warning("Undo state not recognized");
9109 switch (redo_state) {
9110 case UNDO_STATE_TRUE:
9111 if (!undostruct->redo_state) {
9112 undostruct->redo_state = TRUE;
9113 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9116 case UNDO_STATE_FALSE:
9117 if (undostruct->redo_state) {
9118 undostruct->redo_state = FALSE;
9119 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9122 case UNDO_STATE_UNCHANGED:
9124 case UNDO_STATE_REFRESH:
9125 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9128 g_warning("Redo state not recognized");
9133 /* callback functions */
9135 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9136 * includes "non-client" (windows-izm) in calculation, so this calculation
9137 * may not be accurate.
9139 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9140 GtkAllocation *allocation,
9141 GtkSHRuler *shruler)
9143 if (prefs_common.show_ruler) {
9144 gint char_width = 0, char_height = 0;
9145 gint line_width_in_chars;
9147 gtkut_get_font_size(GTK_WIDGET(widget),
9148 &char_width, &char_height);
9149 line_width_in_chars =
9150 (allocation->width - allocation->x) / char_width;
9152 /* got the maximum */
9153 gtk_shruler_set_range(GTK_SHRULER(shruler),
9154 0.0, line_width_in_chars, 0);
9163 ComposePrefType type;
9164 gboolean entry_marked;
9167 static void account_activated(GtkComboBox *optmenu, gpointer data)
9169 Compose *compose = (Compose *)data;
9172 gchar *folderidentifier;
9173 gint account_id = 0;
9176 GSList *list, *saved_list = NULL;
9177 HeaderEntryState *state;
9178 GtkRcStyle *style = NULL;
9179 static GdkColor yellow;
9180 static gboolean color_set = FALSE;
9182 /* Get ID of active account in the combo box */
9183 menu = gtk_combo_box_get_model(optmenu);
9184 gtk_combo_box_get_active_iter(optmenu, &iter);
9185 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9187 ac = account_find_from_id(account_id);
9188 cm_return_if_fail(ac != NULL);
9190 if (ac != compose->account) {
9191 compose_select_account(compose, ac, FALSE);
9193 for (list = compose->header_list; list; list = list->next) {
9194 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9196 if (hentry->type == PREF_ACCOUNT || !list->next) {
9197 compose_destroy_headerentry(compose, hentry);
9201 state = g_malloc0(sizeof(HeaderEntryState));
9202 state->header = gtk_editable_get_chars(GTK_EDITABLE(
9203 gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9204 state->entry = gtk_editable_get_chars(
9205 GTK_EDITABLE(hentry->entry), 0, -1);
9206 state->type = hentry->type;
9209 gdk_color_parse("#f5f6be", &yellow);
9210 color_set = gdk_colormap_alloc_color(
9211 gdk_colormap_get_system(),
9212 &yellow, FALSE, TRUE);
9215 style = gtk_widget_get_modifier_style(hentry->entry);
9216 state->entry_marked = gdk_color_equal(&yellow,
9217 &style->base[GTK_STATE_NORMAL]);
9219 saved_list = g_slist_append(saved_list, state);
9220 compose_destroy_headerentry(compose, hentry);
9223 compose->header_last = NULL;
9224 g_slist_free(compose->header_list);
9225 compose->header_list = NULL;
9226 compose->header_nextrow = 1;
9227 compose_create_header_entry(compose);
9229 if (ac->set_autocc && ac->auto_cc)
9230 compose_entry_append(compose, ac->auto_cc,
9231 COMPOSE_CC, PREF_ACCOUNT);
9233 if (ac->set_autobcc && ac->auto_bcc)
9234 compose_entry_append(compose, ac->auto_bcc,
9235 COMPOSE_BCC, PREF_ACCOUNT);
9237 if (ac->set_autoreplyto && ac->auto_replyto)
9238 compose_entry_append(compose, ac->auto_replyto,
9239 COMPOSE_REPLYTO, PREF_ACCOUNT);
9241 for (list = saved_list; list; list = list->next) {
9242 state = (HeaderEntryState *) list->data;
9244 compose_add_header_entry(compose, state->header,
9245 state->entry, state->type);
9246 if (state->entry_marked)
9247 compose_entry_mark_default_to(compose, state->entry);
9249 g_free(state->header);
9250 g_free(state->entry);
9253 g_slist_free(saved_list);
9255 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9256 (ac->protocol == A_NNTP) ?
9257 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9260 /* Set message save folder */
9261 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9262 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9264 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9265 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9267 compose_set_save_to(compose, NULL);
9268 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9269 folderidentifier = folder_item_get_identifier(account_get_special_folder
9270 (compose->account, F_OUTBOX));
9271 compose_set_save_to(compose, folderidentifier);
9272 g_free(folderidentifier);
9276 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9277 GtkTreeViewColumn *column, Compose *compose)
9279 compose_attach_property(NULL, compose);
9282 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9285 Compose *compose = (Compose *)data;
9286 GtkTreeSelection *attach_selection;
9287 gint attach_nr_selected;
9289 if (!event) return FALSE;
9291 if (event->button == 3) {
9292 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9293 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9295 if (attach_nr_selected > 0)
9297 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9298 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9300 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9301 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9304 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9305 NULL, NULL, event->button, event->time);
9312 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9315 Compose *compose = (Compose *)data;
9317 if (!event) return FALSE;
9319 switch (event->keyval) {
9320 case GDK_KEY_Delete:
9321 compose_attach_remove_selected(NULL, compose);
9327 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9329 toolbar_comp_set_sensitive(compose, allow);
9330 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9331 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9333 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9335 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9336 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9337 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9339 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9343 static void compose_send_cb(GtkAction *action, gpointer data)
9345 Compose *compose = (Compose *)data;
9347 if (prefs_common.work_offline &&
9348 !inc_offline_should_override(TRUE,
9349 _("Claws Mail needs network access in order "
9350 "to send this email.")))
9353 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9354 g_source_remove(compose->draft_timeout_tag);
9355 compose->draft_timeout_tag = -1;
9358 compose_send(compose);
9361 static void compose_send_later_cb(GtkAction *action, gpointer data)
9363 Compose *compose = (Compose *)data;
9367 compose_allow_user_actions(compose, FALSE);
9368 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9369 compose_allow_user_actions(compose, TRUE);
9373 compose_close(compose);
9374 } else if (val == -1) {
9375 alertpanel_error(_("Could not queue message."));
9376 } else if (val == -2) {
9377 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9378 } else if (val == -3) {
9379 if (privacy_peek_error())
9380 alertpanel_error(_("Could not queue message for sending:\n\n"
9381 "Signature failed: %s"), privacy_get_error());
9382 } else if (val == -4) {
9383 alertpanel_error(_("Could not queue message for sending:\n\n"
9384 "Charset conversion failed."));
9385 } else if (val == -5) {
9386 alertpanel_error(_("Could not queue message for sending:\n\n"
9387 "Couldn't get recipient encryption key."));
9388 } else if (val == -6) {
9391 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9394 #define DRAFTED_AT_EXIT "drafted_at_exit"
9395 static void compose_register_draft(MsgInfo *info)
9397 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9398 DRAFTED_AT_EXIT, NULL);
9399 FILE *fp = g_fopen(filepath, "ab");
9402 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9410 gboolean compose_draft (gpointer data, guint action)
9412 Compose *compose = (Compose *)data;
9416 MsgFlags flag = {0, 0};
9417 static gboolean lock = FALSE;
9418 MsgInfo *newmsginfo;
9420 gboolean target_locked = FALSE;
9421 gboolean err = FALSE;
9423 if (lock) return FALSE;
9425 if (compose->sending)
9428 draft = account_get_special_folder(compose->account, F_DRAFT);
9429 cm_return_val_if_fail(draft != NULL, FALSE);
9431 if (!g_mutex_trylock(compose->mutex)) {
9432 /* we don't want to lock the mutex once it's available,
9433 * because as the only other part of compose.c locking
9434 * it is compose_close - which means once unlocked,
9435 * the compose struct will be freed */
9436 debug_print("couldn't lock mutex, probably sending\n");
9442 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9443 G_DIR_SEPARATOR, compose);
9444 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9445 FILE_OP_ERROR(tmp, "fopen");
9449 /* chmod for security */
9450 if (change_file_mode_rw(fp, tmp) < 0) {
9451 FILE_OP_ERROR(tmp, "chmod");
9452 g_warning("can't change file mode\n");
9455 /* Save draft infos */
9456 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9457 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9459 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9460 gchar *savefolderid;
9462 savefolderid = compose_get_save_to(compose);
9463 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9464 g_free(savefolderid);
9466 if (compose->return_receipt) {
9467 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9469 if (compose->privacy_system) {
9470 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9471 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9472 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9475 /* Message-ID of message replying to */
9476 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9479 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9480 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9483 /* Message-ID of message forwarding to */
9484 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9487 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9488 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9492 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9493 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9495 /* end of headers */
9496 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9503 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9507 if (fclose(fp) == EOF) {
9511 if (compose->targetinfo) {
9512 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9513 flag.perm_flags = target_locked?MSG_LOCKED:0;
9515 flag.tmp_flags = MSG_DRAFT;
9517 folder_item_scan(draft);
9518 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9519 MsgInfo *tmpinfo = NULL;
9520 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9521 if (compose->msgid) {
9522 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9525 msgnum = tmpinfo->msgnum;
9526 procmsg_msginfo_free(tmpinfo);
9527 debug_print("got draft msgnum %d from scanning\n", msgnum);
9529 debug_print("didn't get draft msgnum after scanning\n");
9532 debug_print("got draft msgnum %d from adding\n", msgnum);
9538 if (action != COMPOSE_AUTO_SAVE) {
9539 if (action != COMPOSE_DRAFT_FOR_EXIT)
9540 alertpanel_error(_("Could not save draft."));
9543 gtkut_window_popup(compose->window);
9544 val = alertpanel_full(_("Could not save draft"),
9545 _("Could not save draft.\n"
9546 "Do you want to cancel exit or discard this email?"),
9547 _("_Cancel exit"), _("_Discard email"), NULL,
9548 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9549 if (val == G_ALERTALTERNATE) {
9551 g_mutex_unlock(compose->mutex); /* must be done before closing */
9552 compose_close(compose);
9556 g_mutex_unlock(compose->mutex); /* must be done before closing */
9565 if (compose->mode == COMPOSE_REEDIT) {
9566 compose_remove_reedit_target(compose, TRUE);
9569 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9572 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9574 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9576 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9577 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9578 procmsg_msginfo_set_flags(newmsginfo, 0,
9579 MSG_HAS_ATTACHMENT);
9581 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9582 compose_register_draft(newmsginfo);
9584 procmsg_msginfo_free(newmsginfo);
9587 folder_item_scan(draft);
9589 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9591 g_mutex_unlock(compose->mutex); /* must be done before closing */
9592 compose_close(compose);
9598 path = folder_item_fetch_msg(draft, msgnum);
9600 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9603 if (g_stat(path, &s) < 0) {
9604 FILE_OP_ERROR(path, "stat");
9610 procmsg_msginfo_free(compose->targetinfo);
9611 compose->targetinfo = procmsg_msginfo_new();
9612 compose->targetinfo->msgnum = msgnum;
9613 compose->targetinfo->size = (goffset)s.st_size;
9614 compose->targetinfo->mtime = s.st_mtime;
9615 compose->targetinfo->folder = draft;
9617 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9618 compose->mode = COMPOSE_REEDIT;
9620 if (action == COMPOSE_AUTO_SAVE) {
9621 compose->autosaved_draft = compose->targetinfo;
9623 compose->modified = FALSE;
9624 compose_set_title(compose);
9628 g_mutex_unlock(compose->mutex);
9632 void compose_clear_exit_drafts(void)
9634 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9635 DRAFTED_AT_EXIT, NULL);
9636 if (is_file_exist(filepath))
9637 claws_unlink(filepath);
9642 void compose_reopen_exit_drafts(void)
9644 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9645 DRAFTED_AT_EXIT, NULL);
9646 FILE *fp = g_fopen(filepath, "rb");
9650 while (fgets(buf, sizeof(buf), fp)) {
9651 gchar **parts = g_strsplit(buf, "\t", 2);
9652 const gchar *folder = parts[0];
9653 int msgnum = parts[1] ? atoi(parts[1]):-1;
9655 if (folder && *folder && msgnum > -1) {
9656 FolderItem *item = folder_find_item_from_identifier(folder);
9657 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9659 compose_reedit(info, FALSE);
9666 compose_clear_exit_drafts();
9669 static void compose_save_cb(GtkAction *action, gpointer data)
9671 Compose *compose = (Compose *)data;
9672 compose_draft(compose, COMPOSE_KEEP_EDITING);
9673 compose->rmode = COMPOSE_REEDIT;
9676 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9678 if (compose && file_list) {
9681 for ( tmp = file_list; tmp; tmp = tmp->next) {
9682 gchar *file = (gchar *) tmp->data;
9683 gchar *utf8_filename = conv_filename_to_utf8(file);
9684 compose_attach_append(compose, file, utf8_filename, NULL, NULL);
9685 compose_changed_cb(NULL, compose);
9690 g_free(utf8_filename);
9695 static void compose_attach_cb(GtkAction *action, gpointer data)
9697 Compose *compose = (Compose *)data;
9700 if (compose->redirect_filename != NULL)
9703 file_list = filesel_select_multiple_files_open(_("Select file"));
9706 compose_attach_from_list(compose, file_list, TRUE);
9707 g_list_free(file_list);
9711 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9713 Compose *compose = (Compose *)data;
9715 gint files_inserted = 0;
9717 file_list = filesel_select_multiple_files_open(_("Select file"));
9722 for ( tmp = file_list; tmp; tmp = tmp->next) {
9723 gchar *file = (gchar *) tmp->data;
9724 gchar *filedup = g_strdup(file);
9725 gchar *shortfile = g_path_get_basename(filedup);
9726 ComposeInsertResult res;
9727 /* insert the file if the file is short or if the user confirmed that
9728 he/she wants to insert the large file */
9729 res = compose_insert_file(compose, file);
9730 if (res == COMPOSE_INSERT_READ_ERROR) {
9731 alertpanel_error(_("File '%s' could not be read."), shortfile);
9732 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9733 alertpanel_error(_("File '%s' contained invalid characters\n"
9734 "for the current encoding, insertion may be incorrect."),
9736 } else if (res == COMPOSE_INSERT_SUCCESS)
9743 g_list_free(file_list);
9747 if (files_inserted > 0 && compose->gtkaspell &&
9748 compose->gtkaspell->check_while_typing)
9749 gtkaspell_highlight_all(compose->gtkaspell);
9753 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9755 Compose *compose = (Compose *)data;
9757 compose_insert_sig(compose, FALSE);
9760 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9764 Compose *compose = (Compose *)data;
9766 gtkut_widget_get_uposition(widget, &x, &y);
9767 if (!compose->batch) {
9768 prefs_common.compose_x = x;
9769 prefs_common.compose_y = y;
9771 if (compose->sending || compose->updating)
9773 compose_close_cb(NULL, compose);
9777 void compose_close_toolbar(Compose *compose)
9779 compose_close_cb(NULL, compose);
9782 static void compose_close_cb(GtkAction *action, gpointer data)
9784 Compose *compose = (Compose *)data;
9788 if (compose->exteditor_tag != -1) {
9789 if (!compose_ext_editor_kill(compose))
9794 if (compose->modified) {
9795 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9796 if (!g_mutex_trylock(compose->mutex)) {
9797 /* we don't want to lock the mutex once it's available,
9798 * because as the only other part of compose.c locking
9799 * it is compose_close - which means once unlocked,
9800 * the compose struct will be freed */
9801 debug_print("couldn't lock mutex, probably sending\n");
9805 val = alertpanel(_("Discard message"),
9806 _("This message has been modified. Discard it?"),
9807 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9809 val = alertpanel(_("Save changes"),
9810 _("This message has been modified. Save the latest changes?"),
9811 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9813 g_mutex_unlock(compose->mutex);
9815 case G_ALERTDEFAULT:
9816 if (prefs_common.autosave && !reedit)
9817 compose_remove_draft(compose);
9819 case G_ALERTALTERNATE:
9820 compose_draft(data, COMPOSE_QUIT_EDITING);
9827 compose_close(compose);
9830 static void compose_print_cb(GtkAction *action, gpointer data)
9832 Compose *compose = (Compose *) data;
9834 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9835 if (compose->targetinfo)
9836 messageview_print(compose->targetinfo, FALSE, -1, -1, 0);
9839 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9841 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9842 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9843 Compose *compose = (Compose *) data;
9846 compose->out_encoding = (CharSet)value;
9849 static void compose_address_cb(GtkAction *action, gpointer data)
9851 Compose *compose = (Compose *)data;
9853 addressbook_open(compose);
9856 static void about_show_cb(GtkAction *action, gpointer data)
9861 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9863 Compose *compose = (Compose *)data;
9868 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9869 cm_return_if_fail(tmpl != NULL);
9871 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9873 val = alertpanel(_("Apply template"), msg,
9874 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9877 if (val == G_ALERTDEFAULT)
9878 compose_template_apply(compose, tmpl, TRUE);
9879 else if (val == G_ALERTALTERNATE)
9880 compose_template_apply(compose, tmpl, FALSE);
9883 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9885 Compose *compose = (Compose *)data;
9887 compose_exec_ext_editor(compose);
9890 static void compose_undo_cb(GtkAction *action, gpointer data)
9892 Compose *compose = (Compose *)data;
9893 gboolean prev_autowrap = compose->autowrap;
9895 compose->autowrap = FALSE;
9896 undo_undo(compose->undostruct);
9897 compose->autowrap = prev_autowrap;
9900 static void compose_redo_cb(GtkAction *action, gpointer data)
9902 Compose *compose = (Compose *)data;
9903 gboolean prev_autowrap = compose->autowrap;
9905 compose->autowrap = FALSE;
9906 undo_redo(compose->undostruct);
9907 compose->autowrap = prev_autowrap;
9910 static void entry_cut_clipboard(GtkWidget *entry)
9912 if (GTK_IS_EDITABLE(entry))
9913 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9914 else if (GTK_IS_TEXT_VIEW(entry))
9915 gtk_text_buffer_cut_clipboard(
9916 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9917 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9921 static void entry_copy_clipboard(GtkWidget *entry)
9923 if (GTK_IS_EDITABLE(entry))
9924 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9925 else if (GTK_IS_TEXT_VIEW(entry))
9926 gtk_text_buffer_copy_clipboard(
9927 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9928 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9931 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9932 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9934 if (GTK_IS_TEXT_VIEW(entry)) {
9935 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9936 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9937 GtkTextIter start_iter, end_iter;
9939 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9941 if (contents == NULL)
9944 /* we shouldn't delete the selection when middle-click-pasting, or we
9945 * can't mid-click-paste our own selection */
9946 if (clip != GDK_SELECTION_PRIMARY) {
9947 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9948 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9951 if (insert_place == NULL) {
9952 /* if insert_place isn't specified, insert at the cursor.
9953 * used for Ctrl-V pasting */
9954 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9955 start = gtk_text_iter_get_offset(&start_iter);
9956 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9958 /* if insert_place is specified, paste here.
9959 * used for mid-click-pasting */
9960 start = gtk_text_iter_get_offset(insert_place);
9961 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9962 if (prefs_common.primary_paste_unselects)
9963 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9967 /* paste unwrapped: mark the paste so it's not wrapped later */
9968 end = start + strlen(contents);
9969 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9970 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9971 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9972 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9973 /* rewrap paragraph now (after a mid-click-paste) */
9974 mark_start = gtk_text_buffer_get_insert(buffer);
9975 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9976 gtk_text_iter_backward_char(&start_iter);
9977 compose_beautify_paragraph(compose, &start_iter, TRUE);
9979 } else if (GTK_IS_EDITABLE(entry))
9980 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9982 compose->modified = TRUE;
9985 static void entry_allsel(GtkWidget *entry)
9987 if (GTK_IS_EDITABLE(entry))
9988 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9989 else if (GTK_IS_TEXT_VIEW(entry)) {
9990 GtkTextIter startiter, enditer;
9991 GtkTextBuffer *textbuf;
9993 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9994 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9995 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9997 gtk_text_buffer_move_mark_by_name(textbuf,
9998 "selection_bound", &startiter);
9999 gtk_text_buffer_move_mark_by_name(textbuf,
10000 "insert", &enditer);
10004 static void compose_cut_cb(GtkAction *action, gpointer data)
10006 Compose *compose = (Compose *)data;
10007 if (compose->focused_editable
10008 #ifndef GENERIC_UMPC
10009 && gtk_widget_has_focus(compose->focused_editable)
10012 entry_cut_clipboard(compose->focused_editable);
10015 static void compose_copy_cb(GtkAction *action, gpointer data)
10017 Compose *compose = (Compose *)data;
10018 if (compose->focused_editable
10019 #ifndef GENERIC_UMPC
10020 && gtk_widget_has_focus(compose->focused_editable)
10023 entry_copy_clipboard(compose->focused_editable);
10026 static void compose_paste_cb(GtkAction *action, gpointer data)
10028 Compose *compose = (Compose *)data;
10029 gint prev_autowrap;
10030 GtkTextBuffer *buffer;
10032 if (compose->focused_editable &&
10033 gtk_widget_has_focus(compose->focused_editable))
10034 entry_paste_clipboard(compose, compose->focused_editable,
10035 prefs_common.linewrap_pastes,
10036 GDK_SELECTION_CLIPBOARD, NULL);
10040 if (gtk_widget_has_focus(compose->text) &&
10041 compose->gtkaspell &&
10042 compose->gtkaspell->check_while_typing)
10043 gtkaspell_highlight_all(compose->gtkaspell);
10047 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10049 Compose *compose = (Compose *)data;
10050 gint wrap_quote = prefs_common.linewrap_quote;
10051 if (compose->focused_editable
10052 #ifndef GENERIC_UMPC
10053 && gtk_widget_has_focus(compose->focused_editable)
10056 /* let text_insert() (called directly or at a later time
10057 * after the gtk_editable_paste_clipboard) know that
10058 * text is to be inserted as a quotation. implemented
10059 * by using a simple refcount... */
10060 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10061 G_OBJECT(compose->focused_editable),
10062 "paste_as_quotation"));
10063 g_object_set_data(G_OBJECT(compose->focused_editable),
10064 "paste_as_quotation",
10065 GINT_TO_POINTER(paste_as_quotation + 1));
10066 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10067 entry_paste_clipboard(compose, compose->focused_editable,
10068 prefs_common.linewrap_pastes,
10069 GDK_SELECTION_CLIPBOARD, NULL);
10070 prefs_common.linewrap_quote = wrap_quote;
10074 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10076 Compose *compose = (Compose *)data;
10077 gint prev_autowrap;
10078 GtkTextBuffer *buffer;
10080 if (compose->focused_editable
10081 #ifndef GENERIC_UMPC
10082 && gtk_widget_has_focus(compose->focused_editable)
10085 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10086 GDK_SELECTION_CLIPBOARD, NULL);
10090 if (gtk_widget_has_focus(compose->text) &&
10091 compose->gtkaspell &&
10092 compose->gtkaspell->check_while_typing)
10093 gtkaspell_highlight_all(compose->gtkaspell);
10097 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10099 Compose *compose = (Compose *)data;
10100 gint prev_autowrap;
10101 GtkTextBuffer *buffer;
10103 if (compose->focused_editable
10104 #ifndef GENERIC_UMPC
10105 && gtk_widget_has_focus(compose->focused_editable)
10108 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10109 GDK_SELECTION_CLIPBOARD, NULL);
10113 if (gtk_widget_has_focus(compose->text) &&
10114 compose->gtkaspell &&
10115 compose->gtkaspell->check_while_typing)
10116 gtkaspell_highlight_all(compose->gtkaspell);
10120 static void compose_allsel_cb(GtkAction *action, gpointer data)
10122 Compose *compose = (Compose *)data;
10123 if (compose->focused_editable
10124 #ifndef GENERIC_UMPC
10125 && gtk_widget_has_focus(compose->focused_editable)
10128 entry_allsel(compose->focused_editable);
10131 static void textview_move_beginning_of_line (GtkTextView *text)
10133 GtkTextBuffer *buffer;
10137 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10139 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10140 mark = gtk_text_buffer_get_insert(buffer);
10141 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10142 gtk_text_iter_set_line_offset(&ins, 0);
10143 gtk_text_buffer_place_cursor(buffer, &ins);
10146 static void textview_move_forward_character (GtkTextView *text)
10148 GtkTextBuffer *buffer;
10152 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10154 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10155 mark = gtk_text_buffer_get_insert(buffer);
10156 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10157 if (gtk_text_iter_forward_cursor_position(&ins))
10158 gtk_text_buffer_place_cursor(buffer, &ins);
10161 static void textview_move_backward_character (GtkTextView *text)
10163 GtkTextBuffer *buffer;
10167 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10169 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10170 mark = gtk_text_buffer_get_insert(buffer);
10171 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10172 if (gtk_text_iter_backward_cursor_position(&ins))
10173 gtk_text_buffer_place_cursor(buffer, &ins);
10176 static void textview_move_forward_word (GtkTextView *text)
10178 GtkTextBuffer *buffer;
10183 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10185 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10186 mark = gtk_text_buffer_get_insert(buffer);
10187 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10188 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10189 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10190 gtk_text_iter_backward_word_start(&ins);
10191 gtk_text_buffer_place_cursor(buffer, &ins);
10195 static void textview_move_backward_word (GtkTextView *text)
10197 GtkTextBuffer *buffer;
10202 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10204 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10205 mark = gtk_text_buffer_get_insert(buffer);
10206 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10207 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10208 if (gtk_text_iter_backward_word_starts(&ins, 1))
10209 gtk_text_buffer_place_cursor(buffer, &ins);
10212 static void textview_move_end_of_line (GtkTextView *text)
10214 GtkTextBuffer *buffer;
10218 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10220 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10221 mark = gtk_text_buffer_get_insert(buffer);
10222 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10223 if (gtk_text_iter_forward_to_line_end(&ins))
10224 gtk_text_buffer_place_cursor(buffer, &ins);
10227 static void textview_move_next_line (GtkTextView *text)
10229 GtkTextBuffer *buffer;
10234 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10236 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10237 mark = gtk_text_buffer_get_insert(buffer);
10238 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10239 offset = gtk_text_iter_get_line_offset(&ins);
10240 if (gtk_text_iter_forward_line(&ins)) {
10241 gtk_text_iter_set_line_offset(&ins, offset);
10242 gtk_text_buffer_place_cursor(buffer, &ins);
10246 static void textview_move_previous_line (GtkTextView *text)
10248 GtkTextBuffer *buffer;
10253 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10255 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10256 mark = gtk_text_buffer_get_insert(buffer);
10257 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10258 offset = gtk_text_iter_get_line_offset(&ins);
10259 if (gtk_text_iter_backward_line(&ins)) {
10260 gtk_text_iter_set_line_offset(&ins, offset);
10261 gtk_text_buffer_place_cursor(buffer, &ins);
10265 static void textview_delete_forward_character (GtkTextView *text)
10267 GtkTextBuffer *buffer;
10269 GtkTextIter ins, end_iter;
10271 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10273 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10274 mark = gtk_text_buffer_get_insert(buffer);
10275 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10277 if (gtk_text_iter_forward_char(&end_iter)) {
10278 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10282 static void textview_delete_backward_character (GtkTextView *text)
10284 GtkTextBuffer *buffer;
10286 GtkTextIter ins, end_iter;
10288 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10290 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10291 mark = gtk_text_buffer_get_insert(buffer);
10292 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10294 if (gtk_text_iter_backward_char(&end_iter)) {
10295 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10299 static void textview_delete_forward_word (GtkTextView *text)
10301 GtkTextBuffer *buffer;
10303 GtkTextIter ins, end_iter;
10305 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10307 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10308 mark = gtk_text_buffer_get_insert(buffer);
10309 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10311 if (gtk_text_iter_forward_word_end(&end_iter)) {
10312 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10316 static void textview_delete_backward_word (GtkTextView *text)
10318 GtkTextBuffer *buffer;
10320 GtkTextIter ins, end_iter;
10322 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10324 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10325 mark = gtk_text_buffer_get_insert(buffer);
10326 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10328 if (gtk_text_iter_backward_word_start(&end_iter)) {
10329 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10333 static void textview_delete_line (GtkTextView *text)
10335 GtkTextBuffer *buffer;
10337 GtkTextIter ins, start_iter, end_iter;
10339 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10341 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10342 mark = gtk_text_buffer_get_insert(buffer);
10343 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10346 gtk_text_iter_set_line_offset(&start_iter, 0);
10349 if (gtk_text_iter_ends_line(&end_iter)){
10350 if (!gtk_text_iter_forward_char(&end_iter))
10351 gtk_text_iter_backward_char(&start_iter);
10354 gtk_text_iter_forward_to_line_end(&end_iter);
10355 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10358 static void textview_delete_to_line_end (GtkTextView *text)
10360 GtkTextBuffer *buffer;
10362 GtkTextIter ins, end_iter;
10364 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10366 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10367 mark = gtk_text_buffer_get_insert(buffer);
10368 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10370 if (gtk_text_iter_ends_line(&end_iter))
10371 gtk_text_iter_forward_char(&end_iter);
10373 gtk_text_iter_forward_to_line_end(&end_iter);
10374 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10377 #define DO_ACTION(name, act) { \
10378 if(!strcmp(name, a_name)) { \
10382 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10384 const gchar *a_name = gtk_action_get_name(action);
10385 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10386 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10387 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10388 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10389 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10390 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10391 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10392 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10393 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10394 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10395 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10396 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10397 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10398 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10402 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10404 Compose *compose = (Compose *)data;
10405 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10406 ComposeCallAdvancedAction action = -1;
10408 action = compose_call_advanced_action_from_path(gaction);
10411 void (*do_action) (GtkTextView *text);
10412 } action_table[] = {
10413 {textview_move_beginning_of_line},
10414 {textview_move_forward_character},
10415 {textview_move_backward_character},
10416 {textview_move_forward_word},
10417 {textview_move_backward_word},
10418 {textview_move_end_of_line},
10419 {textview_move_next_line},
10420 {textview_move_previous_line},
10421 {textview_delete_forward_character},
10422 {textview_delete_backward_character},
10423 {textview_delete_forward_word},
10424 {textview_delete_backward_word},
10425 {textview_delete_line},
10426 {textview_delete_to_line_end}
10429 if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
10431 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10432 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10433 if (action_table[action].do_action)
10434 action_table[action].do_action(text);
10436 g_warning("Not implemented yet.");
10440 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10442 GtkAllocation allocation;
10446 if (GTK_IS_EDITABLE(widget)) {
10447 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10448 gtk_editable_set_position(GTK_EDITABLE(widget),
10451 if ((parent = gtk_widget_get_parent(widget))
10452 && (parent = gtk_widget_get_parent(parent))
10453 && (parent = gtk_widget_get_parent(parent))) {
10454 if (GTK_IS_SCROLLED_WINDOW(parent)) {
10455 gtk_widget_get_allocation(widget, &allocation);
10456 gint y = allocation.y;
10457 gint height = allocation.height;
10458 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10459 (GTK_SCROLLED_WINDOW(parent));
10461 gfloat value = gtk_adjustment_get_value(shown);
10462 gfloat upper = gtk_adjustment_get_upper(shown);
10463 gfloat page_size = gtk_adjustment_get_page_size(shown);
10464 if (y < (int)value) {
10465 gtk_adjustment_set_value(shown, y - 1);
10467 if ((y + height) > ((int)value + (int)page_size)) {
10468 if ((y - height - 1) < ((int)upper - (int)page_size)) {
10469 gtk_adjustment_set_value(shown,
10470 y + height - (int)page_size - 1);
10472 gtk_adjustment_set_value(shown,
10473 (int)upper - (int)page_size - 1);
10480 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10481 compose->focused_editable = widget;
10483 #ifdef GENERIC_UMPC
10484 if (GTK_IS_TEXT_VIEW(widget)
10485 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10486 g_object_ref(compose->notebook);
10487 g_object_ref(compose->edit_vbox);
10488 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10489 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10490 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10491 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10492 g_object_unref(compose->notebook);
10493 g_object_unref(compose->edit_vbox);
10494 g_signal_handlers_block_by_func(G_OBJECT(widget),
10495 G_CALLBACK(compose_grab_focus_cb),
10497 gtk_widget_grab_focus(widget);
10498 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10499 G_CALLBACK(compose_grab_focus_cb),
10501 } else if (!GTK_IS_TEXT_VIEW(widget)
10502 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10503 g_object_ref(compose->notebook);
10504 g_object_ref(compose->edit_vbox);
10505 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10506 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10507 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10508 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10509 g_object_unref(compose->notebook);
10510 g_object_unref(compose->edit_vbox);
10511 g_signal_handlers_block_by_func(G_OBJECT(widget),
10512 G_CALLBACK(compose_grab_focus_cb),
10514 gtk_widget_grab_focus(widget);
10515 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10516 G_CALLBACK(compose_grab_focus_cb),
10522 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10524 compose->modified = TRUE;
10525 // compose_beautify_paragraph(compose, NULL, TRUE);
10526 #ifndef GENERIC_UMPC
10527 compose_set_title(compose);
10531 static void compose_wrap_cb(GtkAction *action, gpointer data)
10533 Compose *compose = (Compose *)data;
10534 compose_beautify_paragraph(compose, NULL, TRUE);
10537 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10539 Compose *compose = (Compose *)data;
10540 compose_wrap_all_full(compose, TRUE);
10543 static void compose_find_cb(GtkAction *action, gpointer data)
10545 Compose *compose = (Compose *)data;
10547 message_search_compose(compose);
10550 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10553 Compose *compose = (Compose *)data;
10554 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10555 if (compose->autowrap)
10556 compose_wrap_all_full(compose, TRUE);
10557 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10560 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10563 Compose *compose = (Compose *)data;
10564 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10567 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10569 Compose *compose = (Compose *)data;
10571 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10574 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10576 Compose *compose = (Compose *)data;
10578 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10581 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10583 g_free(compose->privacy_system);
10585 compose->privacy_system = g_strdup(account->default_privacy_system);
10586 compose_update_privacy_system_menu_item(compose, warn);
10589 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10591 Compose *compose = (Compose *)data;
10593 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10594 gtk_widget_show(compose->ruler_hbox);
10595 prefs_common.show_ruler = TRUE;
10597 gtk_widget_hide(compose->ruler_hbox);
10598 gtk_widget_queue_resize(compose->edit_vbox);
10599 prefs_common.show_ruler = FALSE;
10603 static void compose_attach_drag_received_cb (GtkWidget *widget,
10604 GdkDragContext *context,
10607 GtkSelectionData *data,
10610 gpointer user_data)
10612 Compose *compose = (Compose *)user_data;
10616 type = gtk_selection_data_get_data_type(data);
10617 if (((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
10619 || (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND"))
10621 ) && gtk_drag_get_source_widget(context) !=
10622 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10623 list = uri_list_extract_filenames(
10624 (const gchar *)gtk_selection_data_get_data(data));
10625 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10626 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10627 compose_attach_append
10628 (compose, (const gchar *)tmp->data,
10629 utf8_filename, NULL, NULL);
10630 g_free(utf8_filename);
10632 if (list) compose_changed_cb(NULL, compose);
10633 list_free_strings(list);
10635 } else if (gtk_drag_get_source_widget(context)
10636 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10637 /* comes from our summaryview */
10638 SummaryView * summaryview = NULL;
10639 GSList * list = NULL, *cur = NULL;
10641 if (mainwindow_get_mainwindow())
10642 summaryview = mainwindow_get_mainwindow()->summaryview;
10645 list = summary_get_selected_msg_list(summaryview);
10647 for (cur = list; cur; cur = cur->next) {
10648 MsgInfo *msginfo = (MsgInfo *)cur->data;
10649 gchar *file = NULL;
10651 file = procmsg_get_message_file_full(msginfo,
10654 compose_attach_append(compose, (const gchar *)file,
10655 (const gchar *)file, "message/rfc822", NULL);
10659 g_slist_free(list);
10663 static gboolean compose_drag_drop(GtkWidget *widget,
10664 GdkDragContext *drag_context,
10666 guint time, gpointer user_data)
10668 /* not handling this signal makes compose_insert_drag_received_cb
10673 static void compose_insert_drag_received_cb (GtkWidget *widget,
10674 GdkDragContext *drag_context,
10677 GtkSelectionData *data,
10680 gpointer user_data)
10682 Compose *compose = (Compose *)user_data;
10686 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10688 type = gtk_selection_data_get_data_type(data);
10690 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
10692 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND")) {
10694 AlertValue val = G_ALERTDEFAULT;
10695 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
10697 list = uri_list_extract_filenames(ddata);
10698 if (list == NULL && strstr(ddata, "://")) {
10699 /* Assume a list of no files, and data has ://, is a remote link */
10700 gchar *tmpdata = g_strstrip(g_strdup(ddata));
10701 gchar *tmpfile = get_tmp_file();
10702 str_write_to_file(tmpdata, tmpfile);
10704 compose_insert_file(compose, tmpfile);
10705 claws_unlink(tmpfile);
10707 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10708 compose_beautify_paragraph(compose, NULL, TRUE);
10711 switch (prefs_common.compose_dnd_mode) {
10712 case COMPOSE_DND_ASK:
10713 val = alertpanel_full(_("Insert or attach?"),
10714 _("Do you want to insert the contents of the file(s) "
10715 "into the message body, or attach it to the email?"),
10716 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10717 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10719 case COMPOSE_DND_INSERT:
10720 val = G_ALERTALTERNATE;
10722 case COMPOSE_DND_ATTACH:
10723 val = G_ALERTOTHER;
10726 /* unexpected case */
10727 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10730 if (val & G_ALERTDISABLE) {
10731 val &= ~G_ALERTDISABLE;
10732 /* remember what action to perform by default, only if we don't click Cancel */
10733 if (val == G_ALERTALTERNATE)
10734 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10735 else if (val == G_ALERTOTHER)
10736 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10739 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10740 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10741 list_free_strings(list);
10744 } else if (val == G_ALERTOTHER) {
10745 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10746 list_free_strings(list);
10751 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10752 compose_insert_file(compose, (const gchar *)tmp->data);
10754 list_free_strings(list);
10756 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10761 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10764 static void compose_header_drag_received_cb (GtkWidget *widget,
10765 GdkDragContext *drag_context,
10768 GtkSelectionData *data,
10771 gpointer user_data)
10773 GtkEditable *entry = (GtkEditable *)user_data;
10774 const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
10776 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10779 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10780 gchar *decoded=g_new(gchar, strlen(email));
10783 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
10784 gtk_editable_delete_text(entry, 0, -1);
10785 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10786 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10790 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10793 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10795 Compose *compose = (Compose *)data;
10797 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10798 compose->return_receipt = TRUE;
10800 compose->return_receipt = FALSE;
10803 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10805 Compose *compose = (Compose *)data;
10807 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10808 compose->remove_references = TRUE;
10810 compose->remove_references = FALSE;
10813 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10814 ComposeHeaderEntry *headerentry)
10816 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10820 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10821 GdkEventKey *event,
10822 ComposeHeaderEntry *headerentry)
10824 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10825 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10826 !(event->state & GDK_MODIFIER_MASK) &&
10827 (event->keyval == GDK_KEY_BackSpace) &&
10828 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10829 gtk_container_remove
10830 (GTK_CONTAINER(headerentry->compose->header_table),
10831 headerentry->combo);
10832 gtk_container_remove
10833 (GTK_CONTAINER(headerentry->compose->header_table),
10834 headerentry->entry);
10835 headerentry->compose->header_list =
10836 g_slist_remove(headerentry->compose->header_list,
10838 g_free(headerentry);
10839 } else if (event->keyval == GDK_KEY_Tab) {
10840 if (headerentry->compose->header_last == headerentry) {
10841 /* Override default next focus, and give it to subject_entry
10842 * instead of notebook tabs
10844 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10845 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10852 static gboolean scroll_postpone(gpointer data)
10854 Compose *compose = (Compose *)data;
10856 cm_return_val_if_fail(!compose->batch, FALSE);
10858 GTK_EVENTS_FLUSH();
10859 compose_show_first_last_header(compose, FALSE);
10863 static void compose_headerentry_changed_cb(GtkWidget *entry,
10864 ComposeHeaderEntry *headerentry)
10866 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10867 compose_create_header_entry(headerentry->compose);
10868 g_signal_handlers_disconnect_matched
10869 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10870 0, 0, NULL, NULL, headerentry);
10872 if (!headerentry->compose->batch)
10873 g_timeout_add(0, scroll_postpone, headerentry->compose);
10877 static gboolean compose_defer_auto_save_draft(Compose *compose)
10879 compose->draft_timeout_tag = -1;
10880 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10884 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10886 GtkAdjustment *vadj;
10888 cm_return_if_fail(compose);
10889 cm_return_if_fail(!compose->batch);
10890 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10891 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
10892 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
10893 gtk_widget_get_parent(compose->header_table)));
10894 gtk_adjustment_set_value(vadj, (show_first ?
10895 gtk_adjustment_get_lower(vadj) :
10896 (gtk_adjustment_get_upper(vadj) -
10897 gtk_adjustment_get_page_size(vadj))));
10898 gtk_adjustment_changed(vadj);
10901 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10902 const gchar *text, gint len, Compose *compose)
10904 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10905 (G_OBJECT(compose->text), "paste_as_quotation"));
10908 cm_return_if_fail(text != NULL);
10910 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10911 G_CALLBACK(text_inserted),
10913 if (paste_as_quotation) {
10915 const gchar *qmark;
10917 GtkTextIter start_iter;
10920 len = strlen(text);
10922 new_text = g_strndup(text, len);
10924 qmark = compose_quote_char_from_context(compose);
10926 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10927 gtk_text_buffer_place_cursor(buffer, iter);
10929 pos = gtk_text_iter_get_offset(iter);
10931 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10932 _("Quote format error at line %d."));
10933 quote_fmt_reset_vartable();
10935 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10936 GINT_TO_POINTER(paste_as_quotation - 1));
10938 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10939 gtk_text_buffer_place_cursor(buffer, iter);
10940 gtk_text_buffer_delete_mark(buffer, mark);
10942 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10943 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10944 compose_beautify_paragraph(compose, &start_iter, FALSE);
10945 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10946 gtk_text_buffer_delete_mark(buffer, mark);
10948 if (strcmp(text, "\n") || compose->automatic_break
10949 || gtk_text_iter_starts_line(iter)) {
10950 GtkTextIter before_ins;
10951 gtk_text_buffer_insert(buffer, iter, text, len);
10952 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10953 before_ins = *iter;
10954 gtk_text_iter_backward_chars(&before_ins, len);
10955 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10958 /* check if the preceding is just whitespace or quote */
10959 GtkTextIter start_line;
10960 gchar *tmp = NULL, *quote = NULL;
10961 gint quote_len = 0, is_normal = 0;
10962 start_line = *iter;
10963 gtk_text_iter_set_line_offset(&start_line, 0);
10964 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10967 if (*tmp == '\0') {
10970 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10978 gtk_text_buffer_insert(buffer, iter, text, len);
10980 gtk_text_buffer_insert_with_tags_by_name(buffer,
10981 iter, text, len, "no_join", NULL);
10986 if (!paste_as_quotation) {
10987 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10988 compose_beautify_paragraph(compose, iter, FALSE);
10989 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10990 gtk_text_buffer_delete_mark(buffer, mark);
10993 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10994 G_CALLBACK(text_inserted),
10996 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10998 if (prefs_common.autosave &&
10999 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
11000 compose->draft_timeout_tag != -2 /* disabled while loading */)
11001 compose->draft_timeout_tag = g_timeout_add
11002 (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
11006 static void compose_check_all(GtkAction *action, gpointer data)
11008 Compose *compose = (Compose *)data;
11009 if (!compose->gtkaspell)
11012 if (gtk_widget_has_focus(compose->subject_entry))
11013 claws_spell_entry_check_all(
11014 CLAWS_SPELL_ENTRY(compose->subject_entry));
11016 gtkaspell_check_all(compose->gtkaspell);
11019 static void compose_highlight_all(GtkAction *action, gpointer data)
11021 Compose *compose = (Compose *)data;
11022 if (compose->gtkaspell) {
11023 claws_spell_entry_recheck_all(
11024 CLAWS_SPELL_ENTRY(compose->subject_entry));
11025 gtkaspell_highlight_all(compose->gtkaspell);
11029 static void compose_check_backwards(GtkAction *action, gpointer data)
11031 Compose *compose = (Compose *)data;
11032 if (!compose->gtkaspell) {
11033 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11037 if (gtk_widget_has_focus(compose->subject_entry))
11038 claws_spell_entry_check_backwards(
11039 CLAWS_SPELL_ENTRY(compose->subject_entry));
11041 gtkaspell_check_backwards(compose->gtkaspell);
11044 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11046 Compose *compose = (Compose *)data;
11047 if (!compose->gtkaspell) {
11048 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11052 if (gtk_widget_has_focus(compose->subject_entry))
11053 claws_spell_entry_check_forwards_go(
11054 CLAWS_SPELL_ENTRY(compose->subject_entry));
11056 gtkaspell_check_forwards_go(compose->gtkaspell);
11061 *\brief Guess originating forward account from MsgInfo and several
11062 * "common preference" settings. Return NULL if no guess.
11064 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11066 PrefsAccount *account = NULL;
11068 cm_return_val_if_fail(msginfo, NULL);
11069 cm_return_val_if_fail(msginfo->folder, NULL);
11070 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11072 if (msginfo->folder->prefs->enable_default_account)
11073 account = account_find_from_id(msginfo->folder->prefs->default_account);
11076 account = msginfo->folder->folder->account;
11078 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11080 Xstrdup_a(to, msginfo->to, return NULL);
11081 extract_address(to);
11082 account = account_find_from_address(to, FALSE);
11085 if (!account && prefs_common.forward_account_autosel) {
11086 gchar cc[BUFFSIZE];
11087 if (!procheader_get_header_from_msginfo
11088 (msginfo, cc,sizeof cc , "Cc:")) {
11089 gchar *buf = cc + strlen("Cc:");
11090 extract_address(buf);
11091 account = account_find_from_address(buf, FALSE);
11095 if (!account && prefs_common.forward_account_autosel) {
11096 gchar deliveredto[BUFFSIZE];
11097 if (!procheader_get_header_from_msginfo
11098 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
11099 gchar *buf = deliveredto + strlen("Delivered-To:");
11100 extract_address(buf);
11101 account = account_find_from_address(buf, FALSE);
11108 gboolean compose_close(Compose *compose)
11112 if (!g_mutex_trylock(compose->mutex)) {
11113 /* we have to wait for the (possibly deferred by auto-save)
11114 * drafting to be done, before destroying the compose under
11116 debug_print("waiting for drafting to finish...\n");
11117 compose_allow_user_actions(compose, FALSE);
11118 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11121 cm_return_val_if_fail(compose, FALSE);
11122 gtkut_widget_get_uposition(compose->window, &x, &y);
11123 if (!compose->batch) {
11124 prefs_common.compose_x = x;
11125 prefs_common.compose_y = y;
11127 g_mutex_unlock(compose->mutex);
11128 compose_destroy(compose);
11133 * Add entry field for each address in list.
11134 * \param compose E-Mail composition object.
11135 * \param listAddress List of (formatted) E-Mail addresses.
11137 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11140 node = listAddress;
11142 addr = ( gchar * ) node->data;
11143 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11144 node = g_list_next( node );
11148 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11149 guint action, gboolean opening_multiple)
11151 gchar *body = NULL;
11152 GSList *new_msglist = NULL;
11153 MsgInfo *tmp_msginfo = NULL;
11154 gboolean originally_enc = FALSE;
11155 gboolean originally_sig = FALSE;
11156 Compose *compose = NULL;
11157 gchar *s_system = NULL;
11159 cm_return_if_fail(msgview != NULL);
11161 cm_return_if_fail(msginfo_list != NULL);
11163 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11164 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11165 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11167 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11168 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11169 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11170 orig_msginfo, mimeinfo);
11171 if (tmp_msginfo != NULL) {
11172 new_msglist = g_slist_append(NULL, tmp_msginfo);
11174 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11175 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11176 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11178 tmp_msginfo->folder = orig_msginfo->folder;
11179 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11180 if (orig_msginfo->tags) {
11181 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11182 tmp_msginfo->folder->tags_dirty = TRUE;
11188 if (!opening_multiple)
11189 body = messageview_get_selection(msgview);
11192 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11193 procmsg_msginfo_free(tmp_msginfo);
11194 g_slist_free(new_msglist);
11196 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11198 if (compose && originally_enc) {
11199 compose_force_encryption(compose, compose->account, FALSE, s_system);
11202 if (compose && originally_sig && compose->account->default_sign_reply) {
11203 compose_force_signing(compose, compose->account, s_system);
11207 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11210 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11213 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11214 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11215 GSList *cur = msginfo_list;
11216 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11217 "messages. Opening the windows "
11218 "could take some time. Do you "
11219 "want to continue?"),
11220 g_slist_length(msginfo_list));
11221 if (g_slist_length(msginfo_list) > 9
11222 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11223 != G_ALERTALTERNATE) {
11228 /* We'll open multiple compose windows */
11229 /* let the WM place the next windows */
11230 compose_force_window_origin = FALSE;
11231 for (; cur; cur = cur->next) {
11233 tmplist.data = cur->data;
11234 tmplist.next = NULL;
11235 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11237 compose_force_window_origin = TRUE;
11239 /* forwarding multiple mails as attachments is done via a
11240 * single compose window */
11241 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11245 void compose_check_for_email_account(Compose *compose)
11247 PrefsAccount *ac = NULL, *curr = NULL;
11253 if (compose->account && compose->account->protocol == A_NNTP) {
11254 ac = account_get_cur_account();
11255 if (ac->protocol == A_NNTP) {
11256 list = account_get_list();
11258 for( ; list != NULL ; list = g_list_next(list)) {
11259 curr = (PrefsAccount *) list->data;
11260 if (curr->protocol != A_NNTP) {
11266 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11271 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo,
11272 const gchar *address)
11274 GSList *msginfo_list = NULL;
11275 gchar *body = messageview_get_selection(msgview);
11278 msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11280 compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11281 compose_check_for_email_account(compose);
11282 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11283 compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11284 compose_reply_set_subject(compose, msginfo);
11287 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11290 void compose_set_position(Compose *compose, gint pos)
11292 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11294 gtkut_text_view_set_position(text, pos);
11297 gboolean compose_search_string(Compose *compose,
11298 const gchar *str, gboolean case_sens)
11300 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11302 return gtkut_text_view_search_string(text, str, case_sens);
11305 gboolean compose_search_string_backward(Compose *compose,
11306 const gchar *str, gboolean case_sens)
11308 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11310 return gtkut_text_view_search_string_backward(text, str, case_sens);
11313 /* allocate a msginfo structure and populate its data from a compose data structure */
11314 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11316 MsgInfo *newmsginfo;
11318 gchar buf[BUFFSIZE];
11320 cm_return_val_if_fail( compose != NULL, NULL );
11322 newmsginfo = procmsg_msginfo_new();
11325 get_rfc822_date(buf, sizeof(buf));
11326 newmsginfo->date = g_strdup(buf);
11329 if (compose->from_name) {
11330 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11331 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11335 if (compose->subject_entry)
11336 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11338 /* to, cc, reply-to, newsgroups */
11339 for (list = compose->header_list; list; list = list->next) {
11340 gchar *header = gtk_editable_get_chars(
11342 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11343 gchar *entry = gtk_editable_get_chars(
11344 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11346 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11347 if ( newmsginfo->to == NULL ) {
11348 newmsginfo->to = g_strdup(entry);
11349 } else if (entry && *entry) {
11350 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11351 g_free(newmsginfo->to);
11352 newmsginfo->to = tmp;
11355 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11356 if ( newmsginfo->cc == NULL ) {
11357 newmsginfo->cc = g_strdup(entry);
11358 } else if (entry && *entry) {
11359 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11360 g_free(newmsginfo->cc);
11361 newmsginfo->cc = tmp;
11364 if ( strcasecmp(header,
11365 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11366 if ( newmsginfo->newsgroups == NULL ) {
11367 newmsginfo->newsgroups = g_strdup(entry);
11368 } else if (entry && *entry) {
11369 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11370 g_free(newmsginfo->newsgroups);
11371 newmsginfo->newsgroups = tmp;
11379 /* other data is unset */
11385 /* update compose's dictionaries from folder dict settings */
11386 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11387 FolderItem *folder_item)
11389 cm_return_if_fail(compose != NULL);
11391 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11392 FolderItemPrefs *prefs = folder_item->prefs;
11394 if (prefs->enable_default_dictionary)
11395 gtkaspell_change_dict(compose->gtkaspell,
11396 prefs->default_dictionary, FALSE);
11397 if (folder_item->prefs->enable_default_alt_dictionary)
11398 gtkaspell_change_alt_dict(compose->gtkaspell,
11399 prefs->default_alt_dictionary);
11400 if (prefs->enable_default_dictionary
11401 || prefs->enable_default_alt_dictionary)
11402 compose_spell_menu_changed(compose);