2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2012 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "claws-features.h"
27 #ifndef PANGO_ENABLE_ENGINE
28 # define PANGO_ENABLE_ENGINE
32 #include <glib/gi18n.h>
33 #include <gdk/gdkkeysyms.h>
36 #include <pango/pango-break.h>
41 #include <sys/types.h>
47 # include <sys/wait.h>
51 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
55 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
62 #include "mainwindow.h"
64 #ifndef USE_NEW_ADDRBOOK
65 #include "addressbook.h"
67 #include "addressbook-dbus.h"
68 #include "addressadd.h"
70 #include "folderview.h"
73 #include "stock_pixmap.h"
74 #include "send_message.h"
77 #include "customheader.h"
78 #include "prefs_common.h"
79 #include "prefs_account.h"
83 #include "procheader.h"
85 #include "statusbar.h"
88 #include "quoted-printable.h"
92 #include "gtkshruler.h"
94 #include "alertpanel.h"
95 #include "manage_window.h"
97 #include "folder_item_prefs.h"
98 #include "addr_compl.h"
99 #include "quote_fmt.h"
101 #include "foldersel.h"
104 #include "message_search.h"
105 #include "combobox.h"
109 #include "autofaces.h"
110 #include "spell_entry.h"
123 #define N_ATTACH_COLS (N_COL_COLUMNS)
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
128 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
130 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
132 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
134 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
135 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
136 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
137 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
138 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
139 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
140 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
141 } ComposeCallAdvancedAction;
145 PRIORITY_HIGHEST = 1,
154 COMPOSE_INSERT_SUCCESS,
155 COMPOSE_INSERT_READ_ERROR,
156 COMPOSE_INSERT_INVALID_CHARACTER,
157 COMPOSE_INSERT_NO_FILE
158 } ComposeInsertResult;
162 COMPOSE_WRITE_FOR_SEND,
163 COMPOSE_WRITE_FOR_STORE
168 COMPOSE_QUOTE_FORCED,
175 SUBJECT_FIELD_PRESENT,
180 #define B64_LINE_SIZE 57
181 #define B64_BUFFSIZE 77
183 #define MAX_REFERENCES_LEN 999
185 static GList *compose_list = NULL;
187 static Compose *compose_generic_new (PrefsAccount *account,
191 GList *listAddress );
193 static Compose *compose_create (PrefsAccount *account,
198 static void compose_entry_mark_default_to (Compose *compose,
199 const gchar *address);
200 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
201 ComposeQuoteMode quote_mode,
205 static Compose *compose_forward_multiple (PrefsAccount *account,
206 GSList *msginfo_list);
207 static Compose *compose_reply (MsgInfo *msginfo,
208 ComposeQuoteMode quote_mode,
213 static Compose *compose_reply_mode (ComposeMode mode,
214 GSList *msginfo_list,
216 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
217 static void compose_update_privacy_systems_menu(Compose *compose);
219 static GtkWidget *compose_account_option_menu_create
221 static void compose_set_out_encoding (Compose *compose);
222 static void compose_set_template_menu (Compose *compose);
223 static void compose_destroy (Compose *compose);
225 static MailField compose_entries_set (Compose *compose,
227 ComposeEntryType to_type);
228 static gint compose_parse_header (Compose *compose,
230 static gint compose_parse_manual_headers (Compose *compose,
232 HeaderEntry *entries);
233 static gchar *compose_parse_references (const gchar *ref,
236 static gchar *compose_quote_fmt (Compose *compose,
242 gboolean need_unescape,
243 const gchar *err_msg);
245 static void compose_reply_set_entry (Compose *compose,
251 followup_and_reply_to);
252 static void compose_reedit_set_entry (Compose *compose,
255 static void compose_insert_sig (Compose *compose,
257 static ComposeInsertResult compose_insert_file (Compose *compose,
260 static gboolean compose_attach_append (Compose *compose,
263 const gchar *content_type,
264 const gchar *charset);
265 static void compose_attach_parts (Compose *compose,
268 static gboolean compose_beautify_paragraph (Compose *compose,
269 GtkTextIter *par_iter,
271 static void compose_wrap_all (Compose *compose);
272 static void compose_wrap_all_full (Compose *compose,
275 static void compose_set_title (Compose *compose);
276 static void compose_select_account (Compose *compose,
277 PrefsAccount *account,
280 static PrefsAccount *compose_current_mail_account(void);
281 /* static gint compose_send (Compose *compose); */
282 static gboolean compose_check_for_valid_recipient
284 static gboolean compose_check_entries (Compose *compose,
285 gboolean check_everything);
286 static gint compose_write_to_file (Compose *compose,
289 gboolean attach_parts);
290 static gint compose_write_body_to_file (Compose *compose,
292 static gint compose_remove_reedit_target (Compose *compose,
294 static void compose_remove_draft (Compose *compose);
295 static gint compose_queue_sub (Compose *compose,
299 gboolean check_subject,
300 gboolean remove_reedit_target);
301 static int compose_add_attachments (Compose *compose,
303 static gchar *compose_get_header (Compose *compose);
304 static gchar *compose_get_manual_headers_info (Compose *compose);
306 static void compose_convert_header (Compose *compose,
311 gboolean addr_field);
313 static void compose_attach_info_free (AttachInfo *ainfo);
314 static void compose_attach_remove_selected (GtkAction *action,
317 static void compose_template_apply (Compose *compose,
320 static void compose_attach_property (GtkAction *action,
322 static void compose_attach_property_create (gboolean *cancelled);
323 static void attach_property_ok (GtkWidget *widget,
324 gboolean *cancelled);
325 static void attach_property_cancel (GtkWidget *widget,
326 gboolean *cancelled);
327 static gint attach_property_delete_event (GtkWidget *widget,
329 gboolean *cancelled);
330 static gboolean attach_property_key_pressed (GtkWidget *widget,
332 gboolean *cancelled);
334 static void compose_exec_ext_editor (Compose *compose);
336 static gint compose_exec_ext_editor_real (const gchar *file);
337 static gboolean compose_ext_editor_kill (Compose *compose);
338 static gboolean compose_input_cb (GIOChannel *source,
339 GIOCondition condition,
341 static void compose_set_ext_editor_sensitive (Compose *compose,
343 #endif /* G_OS_UNIX */
345 static void compose_undo_state_changed (UndoMain *undostruct,
350 static void compose_create_header_entry (Compose *compose);
351 static void compose_add_header_entry (Compose *compose, const gchar *header,
352 gchar *text, ComposePrefType pref_type);
353 static void compose_remove_header_entries(Compose *compose);
355 static void compose_update_priority_menu_item(Compose * compose);
357 static void compose_spell_menu_changed (void *data);
358 static void compose_dict_changed (void *data);
360 static void compose_add_field_list ( Compose *compose,
361 GList *listAddress );
363 /* callback functions */
365 static void compose_notebook_size_alloc (GtkNotebook *notebook,
366 GtkAllocation *allocation,
368 static gboolean compose_edit_size_alloc (GtkEditable *widget,
369 GtkAllocation *allocation,
370 GtkSHRuler *shruler);
371 static void account_activated (GtkComboBox *optmenu,
373 static void attach_selected (GtkTreeView *tree_view,
374 GtkTreePath *tree_path,
375 GtkTreeViewColumn *column,
377 static gboolean attach_button_pressed (GtkWidget *widget,
378 GdkEventButton *event,
380 static gboolean attach_key_pressed (GtkWidget *widget,
383 static void compose_send_cb (GtkAction *action, gpointer data);
384 static void compose_send_later_cb (GtkAction *action, gpointer data);
386 static void compose_save_cb (GtkAction *action,
389 static void compose_attach_cb (GtkAction *action,
391 static void compose_insert_file_cb (GtkAction *action,
393 static void compose_insert_sig_cb (GtkAction *action,
396 static void compose_close_cb (GtkAction *action,
398 static void compose_print_cb (GtkAction *action,
401 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
403 static void compose_address_cb (GtkAction *action,
405 static void about_show_cb (GtkAction *action,
407 static void compose_template_activate_cb(GtkWidget *widget,
410 static void compose_ext_editor_cb (GtkAction *action,
413 static gint compose_delete_cb (GtkWidget *widget,
417 static void compose_undo_cb (GtkAction *action,
419 static void compose_redo_cb (GtkAction *action,
421 static void compose_cut_cb (GtkAction *action,
423 static void compose_copy_cb (GtkAction *action,
425 static void compose_paste_cb (GtkAction *action,
427 static void compose_paste_as_quote_cb (GtkAction *action,
429 static void compose_paste_no_wrap_cb (GtkAction *action,
431 static void compose_paste_wrap_cb (GtkAction *action,
433 static void compose_allsel_cb (GtkAction *action,
436 static void compose_advanced_action_cb (GtkAction *action,
439 static void compose_grab_focus_cb (GtkWidget *widget,
442 static void compose_changed_cb (GtkTextBuffer *textbuf,
445 static void compose_wrap_cb (GtkAction *action,
447 static void compose_wrap_all_cb (GtkAction *action,
449 static void compose_find_cb (GtkAction *action,
451 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
453 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
456 static void compose_toggle_ruler_cb (GtkToggleAction *action,
458 static void compose_toggle_sign_cb (GtkToggleAction *action,
460 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
462 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
463 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
464 static void activate_privacy_system (Compose *compose,
465 PrefsAccount *account,
467 static void compose_use_signing(Compose *compose, gboolean use_signing);
468 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
469 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
471 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
473 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
474 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
475 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
477 static void compose_attach_drag_received_cb (GtkWidget *widget,
478 GdkDragContext *drag_context,
481 GtkSelectionData *data,
485 static void compose_insert_drag_received_cb (GtkWidget *widget,
486 GdkDragContext *drag_context,
489 GtkSelectionData *data,
493 static void compose_header_drag_received_cb (GtkWidget *widget,
494 GdkDragContext *drag_context,
497 GtkSelectionData *data,
502 static gboolean compose_drag_drop (GtkWidget *widget,
503 GdkDragContext *drag_context,
505 guint time, gpointer user_data);
506 static gboolean completion_set_focus_to_subject
511 static void text_inserted (GtkTextBuffer *buffer,
516 static Compose *compose_generic_reply(MsgInfo *msginfo,
517 ComposeQuoteMode quote_mode,
521 gboolean followup_and_reply_to,
524 static void compose_headerentry_changed_cb (GtkWidget *entry,
525 ComposeHeaderEntry *headerentry);
526 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
528 ComposeHeaderEntry *headerentry);
529 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
530 ComposeHeaderEntry *headerentry);
532 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
534 static void compose_allow_user_actions (Compose *compose, gboolean allow);
536 static void compose_nothing_cb (GtkAction *action, gpointer data)
542 static void compose_check_all (GtkAction *action, gpointer data);
543 static void compose_highlight_all (GtkAction *action, gpointer data);
544 static void compose_check_backwards (GtkAction *action, gpointer data);
545 static void compose_check_forwards_go (GtkAction *action, gpointer data);
548 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
550 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
553 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
554 FolderItem *folder_item);
556 static void compose_attach_update_label(Compose *compose);
557 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
558 gboolean respect_default_to);
560 static GtkActionEntry compose_popup_entries[] =
562 {"Compose", NULL, "Compose" },
563 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
564 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
565 {"Compose/---", NULL, "---", NULL, NULL, NULL },
566 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
569 static GtkActionEntry compose_entries[] =
571 {"Menu", NULL, "Menu" },
573 {"Message", NULL, N_("_Message") },
574 {"Edit", NULL, N_("_Edit") },
576 {"Spelling", NULL, N_("_Spelling") },
578 {"Options", NULL, N_("_Options") },
579 {"Tools", NULL, N_("_Tools") },
580 {"Help", NULL, N_("_Help") },
582 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
583 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
584 {"Message/---", NULL, "---" },
586 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
587 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
588 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
589 /* {"Message/---", NULL, "---" }, */
590 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
591 /* {"Message/---", NULL, "---" }, */
592 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
593 /* {"Message/---", NULL, "---" }, */
594 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
597 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
598 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
599 {"Edit/---", NULL, "---" },
601 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
602 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
603 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
605 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
606 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
607 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
608 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
610 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
612 {"Edit/Advanced", NULL, N_("A_dvanced") },
613 {"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*/
614 {"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*/
615 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
616 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
617 {"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*/
618 {"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*/
619 {"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*/
620 {"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*/
621 {"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*/
622 {"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*/
623 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
624 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
625 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
626 {"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*/
628 /* {"Edit/---", NULL, "---" }, */
629 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
631 /* {"Edit/---", NULL, "---" }, */
632 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
633 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
634 /* {"Edit/---", NULL, "---" }, */
635 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
638 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
639 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
640 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
641 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
643 {"Spelling/---", NULL, "---" },
644 {"Spelling/Options", NULL, N_("_Options") },
649 {"Options/ReplyMode", NULL, N_("Reply _mode") },
650 {"Options/---", NULL, "---" },
651 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
652 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
654 /* {"Options/---", NULL, "---" }, */
656 {"Options/Priority", NULL, N_("_Priority") },
658 {"Options/Encoding", NULL, N_("Character _encoding") },
659 {"Options/Encoding/---", NULL, "---" },
660 #define ENC_ACTION(cs_char,c_char,string) \
661 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
663 {"Options/Encoding/Western", NULL, N_("Western European") },
664 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
665 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
666 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
667 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
668 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
669 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
670 {"Options/Encoding/Korean", NULL, N_("Korean") },
671 {"Options/Encoding/Thai", NULL, N_("Thai") },
674 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
676 {"Tools/Template", NULL, N_("_Template") },
677 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
678 {"Tools/Actions", NULL, N_("Actio_ns") },
679 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
682 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
685 static GtkToggleActionEntry compose_toggle_entries[] =
687 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
688 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
689 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
690 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
691 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
692 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
693 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
696 static GtkRadioActionEntry compose_radio_rm_entries[] =
698 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
699 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
700 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
701 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
704 static GtkRadioActionEntry compose_radio_prio_entries[] =
706 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
707 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
708 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
709 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
710 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
713 static GtkRadioActionEntry compose_radio_enc_entries[] =
715 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
729 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
730 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
731 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
732 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
733 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
734 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
735 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
736 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
737 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
738 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
739 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
740 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
741 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
742 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
743 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
744 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
745 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
746 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
749 static GtkTargetEntry compose_mime_types[] =
751 {"text/uri-list", 0, 0},
752 {"UTF8_STRING", 0, 0},
756 static gboolean compose_put_existing_to_front(MsgInfo *info)
758 GList *compose_list = compose_get_compose_list();
762 for (elem = compose_list; elem != NULL && elem->data != NULL;
764 Compose *c = (Compose*)elem->data;
766 if (!c->targetinfo || !c->targetinfo->msgid ||
770 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
771 gtkut_window_popup(c->window);
779 static GdkColor quote_color1 =
780 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
781 static GdkColor quote_color2 =
782 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
783 static GdkColor quote_color3 =
784 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
786 static GdkColor quote_bgcolor1 =
787 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
788 static GdkColor quote_bgcolor2 =
789 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
790 static GdkColor quote_bgcolor3 =
791 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
793 static GdkColor signature_color = {
800 static GdkColor uri_color = {
807 static void compose_create_tags(GtkTextView *text, Compose *compose)
809 GtkTextBuffer *buffer;
810 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
811 #if !GTK_CHECK_VERSION(2, 24, 0)
818 buffer = gtk_text_view_get_buffer(text);
820 if (prefs_common.enable_color) {
821 /* grab the quote colors, converting from an int to a GdkColor */
822 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
824 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
826 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
828 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
830 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
832 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
834 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
836 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
839 signature_color = quote_color1 = quote_color2 = quote_color3 =
840 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
843 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
844 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
845 "foreground-gdk", "e_color1,
846 "paragraph-background-gdk", "e_bgcolor1,
848 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
849 "foreground-gdk", "e_color2,
850 "paragraph-background-gdk", "e_bgcolor2,
852 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
853 "foreground-gdk", "e_color3,
854 "paragraph-background-gdk", "e_bgcolor3,
857 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
858 "foreground-gdk", "e_color1,
860 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
861 "foreground-gdk", "e_color2,
863 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
864 "foreground-gdk", "e_color3,
868 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
869 "foreground-gdk", &signature_color,
872 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
873 "foreground-gdk", &uri_color,
875 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
876 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
878 color[0] = quote_color1;
879 color[1] = quote_color2;
880 color[2] = quote_color3;
881 color[3] = quote_bgcolor1;
882 color[4] = quote_bgcolor2;
883 color[5] = quote_bgcolor3;
884 color[6] = signature_color;
885 color[7] = uri_color;
886 #if !GTK_CHECK_VERSION(2, 24, 0)
887 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
888 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
890 for (i = 0; i < 8; i++) {
891 if (success[i] == FALSE) {
892 g_warning("Compose: color allocation failed.\n");
893 quote_color1 = quote_color2 = quote_color3 =
894 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
895 signature_color = uri_color = black;
901 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
904 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
907 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
909 return compose_generic_new(account, mailto, item, NULL, NULL);
912 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
914 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
917 #define SCROLL_TO_CURSOR(compose) { \
918 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
919 gtk_text_view_get_buffer( \
920 GTK_TEXT_VIEW(compose->text))); \
921 gtk_text_view_scroll_mark_onscreen( \
922 GTK_TEXT_VIEW(compose->text), \
926 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
929 if (folderidentifier) {
930 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
931 prefs_common.compose_save_to_history = add_history(
932 prefs_common.compose_save_to_history, folderidentifier);
933 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
934 prefs_common.compose_save_to_history);
937 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
938 if (folderidentifier)
939 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
941 gtk_entry_set_text(GTK_ENTRY(entry), "");
944 static gchar *compose_get_save_to(Compose *compose)
947 gchar *result = NULL;
948 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
949 result = gtk_editable_get_chars(entry, 0, -1);
952 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
953 prefs_common.compose_save_to_history = add_history(
954 prefs_common.compose_save_to_history, result);
955 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
956 prefs_common.compose_save_to_history);
961 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
962 GList *attach_files, GList *listAddress )
965 GtkTextView *textview;
966 GtkTextBuffer *textbuf;
968 const gchar *subject_format = NULL;
969 const gchar *body_format = NULL;
970 gchar *mailto_from = NULL;
971 PrefsAccount *mailto_account = NULL;
972 MsgInfo* dummyinfo = NULL;
973 gint cursor_pos = -1;
974 MailField mfield = NO_FIELD_PRESENT;
978 /* check if mailto defines a from */
979 if (mailto && *mailto != '\0') {
980 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
981 /* mailto defines a from, check if we can get account prefs from it,
982 if not, the account prefs will be guessed using other ways, but we'll keep
985 mailto_account = account_find_from_address(mailto_from, TRUE);
987 account = mailto_account;
990 /* if no account prefs set from mailto, set if from folder prefs (if any) */
991 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
992 account = account_find_from_id(item->prefs->default_account);
994 /* if no account prefs set, fallback to the current one */
995 if (!account) account = cur_account;
996 cm_return_val_if_fail(account != NULL, NULL);
998 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1000 /* override from name if mailto asked for it */
1002 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1003 g_free(mailto_from);
1005 /* override from name according to folder properties */
1006 if (item && item->prefs &&
1007 item->prefs->compose_with_format &&
1008 item->prefs->compose_override_from_format &&
1009 *item->prefs->compose_override_from_format != '\0') {
1014 dummyinfo = compose_msginfo_new_from_compose(compose);
1016 /* decode \-escape sequences in the internal representation of the quote format */
1017 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1018 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1021 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1022 compose->gtkaspell);
1024 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1026 quote_fmt_scan_string(tmp);
1029 buf = quote_fmt_get_buffer();
1031 alertpanel_error(_("New message From format error."));
1033 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1034 quote_fmt_reset_vartable();
1039 compose->replyinfo = NULL;
1040 compose->fwdinfo = NULL;
1042 textview = GTK_TEXT_VIEW(compose->text);
1043 textbuf = gtk_text_view_get_buffer(textview);
1044 compose_create_tags(textview, compose);
1046 undo_block(compose->undostruct);
1048 compose_set_dictionaries_from_folder_prefs(compose, item);
1051 if (account->auto_sig)
1052 compose_insert_sig(compose, FALSE);
1053 gtk_text_buffer_get_start_iter(textbuf, &iter);
1054 gtk_text_buffer_place_cursor(textbuf, &iter);
1056 if (account->protocol != A_NNTP) {
1057 if (mailto && *mailto != '\0') {
1058 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1061 compose_set_folder_prefs(compose, item, TRUE);
1063 if (item && item->ret_rcpt) {
1064 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1067 if (mailto && *mailto != '\0') {
1068 if (!strchr(mailto, '@'))
1069 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1071 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1072 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1073 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1074 mfield = TO_FIELD_PRESENT;
1077 * CLAWS: just don't allow return receipt request, even if the user
1078 * may want to send an email. simple but foolproof.
1080 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1082 compose_add_field_list( compose, listAddress );
1084 if (item && item->prefs && item->prefs->compose_with_format) {
1085 subject_format = item->prefs->compose_subject_format;
1086 body_format = item->prefs->compose_body_format;
1087 } else if (account->compose_with_format) {
1088 subject_format = account->compose_subject_format;
1089 body_format = account->compose_body_format;
1090 } else if (prefs_common.compose_with_format) {
1091 subject_format = prefs_common.compose_subject_format;
1092 body_format = prefs_common.compose_body_format;
1095 if (subject_format || body_format) {
1098 && *subject_format != '\0' )
1100 gchar *subject = NULL;
1105 dummyinfo = compose_msginfo_new_from_compose(compose);
1107 /* decode \-escape sequences in the internal representation of the quote format */
1108 tmp = g_malloc(strlen(subject_format)+1);
1109 pref_get_unescaped_pref(tmp, subject_format);
1111 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1113 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1114 compose->gtkaspell);
1116 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1118 quote_fmt_scan_string(tmp);
1121 buf = quote_fmt_get_buffer();
1123 alertpanel_error(_("New message subject format error."));
1125 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1126 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1127 quote_fmt_reset_vartable();
1131 mfield = SUBJECT_FIELD_PRESENT;
1135 && *body_format != '\0' )
1138 GtkTextBuffer *buffer;
1139 GtkTextIter start, end;
1143 dummyinfo = compose_msginfo_new_from_compose(compose);
1145 text = GTK_TEXT_VIEW(compose->text);
1146 buffer = gtk_text_view_get_buffer(text);
1147 gtk_text_buffer_get_start_iter(buffer, &start);
1148 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1149 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1151 compose_quote_fmt(compose, dummyinfo,
1153 NULL, tmp, FALSE, TRUE,
1154 _("The body of the \"New message\" template has an error at line %d."));
1155 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1156 quote_fmt_reset_vartable();
1160 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1161 gtkaspell_highlight_all(compose->gtkaspell);
1163 mfield = BODY_FIELD_PRESENT;
1167 procmsg_msginfo_free( dummyinfo );
1173 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1174 ainfo = (AttachInfo *) curr->data;
1175 compose_attach_append(compose, ainfo->file, ainfo->name,
1176 ainfo->content_type, ainfo->charset);
1180 compose_show_first_last_header(compose, TRUE);
1182 /* Set save folder */
1183 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1184 gchar *folderidentifier;
1186 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1187 folderidentifier = folder_item_get_identifier(item);
1188 compose_set_save_to(compose, folderidentifier);
1189 g_free(folderidentifier);
1192 /* Place cursor according to provided input (mfield) */
1194 case NO_FIELD_PRESENT:
1195 if (compose->header_last)
1196 gtk_widget_grab_focus(compose->header_last->entry);
1198 case TO_FIELD_PRESENT:
1199 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1201 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1204 gtk_widget_grab_focus(compose->subject_entry);
1206 case SUBJECT_FIELD_PRESENT:
1207 textview = GTK_TEXT_VIEW(compose->text);
1210 textbuf = gtk_text_view_get_buffer(textview);
1213 mark = gtk_text_buffer_get_insert(textbuf);
1214 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1215 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1217 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1218 * only defers where it comes to the variable body
1219 * is not null. If no body is present compose->text
1220 * will be null in which case you cannot place the
1221 * cursor inside the component so. An empty component
1222 * is therefore created before placing the cursor
1224 case BODY_FIELD_PRESENT:
1225 cursor_pos = quote_fmt_get_cursor_pos();
1226 if (cursor_pos == -1)
1227 gtk_widget_grab_focus(compose->header_last->entry);
1229 gtk_widget_grab_focus(compose->text);
1233 undo_unblock(compose->undostruct);
1235 if (prefs_common.auto_exteditor)
1236 compose_exec_ext_editor(compose);
1238 compose->draft_timeout_tag = -1;
1239 SCROLL_TO_CURSOR(compose);
1241 compose->modified = FALSE;
1242 compose_set_title(compose);
1244 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1249 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1250 gboolean override_pref, const gchar *system)
1252 const gchar *privacy = NULL;
1254 cm_return_if_fail(compose != NULL);
1255 cm_return_if_fail(account != NULL);
1257 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1262 else if (account->default_privacy_system
1263 && strlen(account->default_privacy_system)) {
1264 privacy = account->default_privacy_system;
1266 GSList *privacy_avail = privacy_get_system_ids();
1267 if (privacy_avail && g_slist_length(privacy_avail)) {
1268 privacy = (gchar *)(privacy_avail->data);
1271 if (privacy != NULL) {
1273 g_free(compose->privacy_system);
1274 compose->privacy_system = NULL;
1276 if (compose->privacy_system == NULL)
1277 compose->privacy_system = g_strdup(privacy);
1278 else if (*(compose->privacy_system) == '\0') {
1279 g_free(compose->privacy_system);
1280 compose->privacy_system = g_strdup(privacy);
1282 compose_update_privacy_system_menu_item(compose, FALSE);
1283 compose_use_encryption(compose, TRUE);
1287 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1289 const gchar *privacy = NULL;
1293 else if (account->default_privacy_system
1294 && strlen(account->default_privacy_system)) {
1295 privacy = account->default_privacy_system;
1297 GSList *privacy_avail = privacy_get_system_ids();
1298 if (privacy_avail && g_slist_length(privacy_avail)) {
1299 privacy = (gchar *)(privacy_avail->data);
1303 if (privacy != NULL) {
1305 g_free(compose->privacy_system);
1306 compose->privacy_system = NULL;
1308 if (compose->privacy_system == NULL)
1309 compose->privacy_system = g_strdup(privacy);
1310 compose_update_privacy_system_menu_item(compose, FALSE);
1311 compose_use_signing(compose, TRUE);
1315 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1319 Compose *compose = NULL;
1321 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1323 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1324 cm_return_val_if_fail(msginfo != NULL, NULL);
1326 list_len = g_slist_length(msginfo_list);
1330 case COMPOSE_REPLY_TO_ADDRESS:
1331 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1332 FALSE, prefs_common.default_reply_list, FALSE, body);
1334 case COMPOSE_REPLY_WITH_QUOTE:
1335 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1336 FALSE, prefs_common.default_reply_list, FALSE, body);
1338 case COMPOSE_REPLY_WITHOUT_QUOTE:
1339 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1340 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1342 case COMPOSE_REPLY_TO_SENDER:
1343 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1344 FALSE, FALSE, TRUE, body);
1346 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1347 compose = compose_followup_and_reply_to(msginfo,
1348 COMPOSE_QUOTE_CHECK,
1349 FALSE, FALSE, body);
1351 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1352 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1353 FALSE, FALSE, TRUE, body);
1355 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1356 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1357 FALSE, FALSE, TRUE, NULL);
1359 case COMPOSE_REPLY_TO_ALL:
1360 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1361 TRUE, FALSE, FALSE, body);
1363 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1364 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1365 TRUE, FALSE, FALSE, body);
1367 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1368 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1369 TRUE, FALSE, FALSE, NULL);
1371 case COMPOSE_REPLY_TO_LIST:
1372 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1373 FALSE, TRUE, FALSE, body);
1375 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1376 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1377 FALSE, TRUE, FALSE, body);
1379 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1380 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1381 FALSE, TRUE, FALSE, NULL);
1383 case COMPOSE_FORWARD:
1384 if (prefs_common.forward_as_attachment) {
1385 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1388 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1392 case COMPOSE_FORWARD_INLINE:
1393 /* check if we reply to more than one Message */
1394 if (list_len == 1) {
1395 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1398 /* more messages FALL THROUGH */
1399 case COMPOSE_FORWARD_AS_ATTACH:
1400 compose = compose_forward_multiple(NULL, msginfo_list);
1402 case COMPOSE_REDIRECT:
1403 compose = compose_redirect(NULL, msginfo, FALSE);
1406 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1409 if (compose == NULL) {
1410 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1414 compose->rmode = mode;
1415 switch (compose->rmode) {
1417 case COMPOSE_REPLY_WITH_QUOTE:
1418 case COMPOSE_REPLY_WITHOUT_QUOTE:
1419 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1420 debug_print("reply mode Normal\n");
1421 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1422 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1424 case COMPOSE_REPLY_TO_SENDER:
1425 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1426 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1427 debug_print("reply mode Sender\n");
1428 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1430 case COMPOSE_REPLY_TO_ALL:
1431 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1432 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1433 debug_print("reply mode All\n");
1434 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1436 case COMPOSE_REPLY_TO_LIST:
1437 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1438 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1439 debug_print("reply mode List\n");
1440 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1442 case COMPOSE_REPLY_TO_ADDRESS:
1443 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1451 static Compose *compose_reply(MsgInfo *msginfo,
1452 ComposeQuoteMode quote_mode,
1458 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1459 to_sender, FALSE, body);
1462 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1463 ComposeQuoteMode quote_mode,
1468 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1469 to_sender, TRUE, body);
1472 static void compose_extract_original_charset(Compose *compose)
1474 MsgInfo *info = NULL;
1475 if (compose->replyinfo) {
1476 info = compose->replyinfo;
1477 } else if (compose->fwdinfo) {
1478 info = compose->fwdinfo;
1479 } else if (compose->targetinfo) {
1480 info = compose->targetinfo;
1483 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1484 MimeInfo *partinfo = mimeinfo;
1485 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1486 partinfo = procmime_mimeinfo_next(partinfo);
1488 compose->orig_charset =
1489 g_strdup(procmime_mimeinfo_get_parameter(
1490 partinfo, "charset"));
1492 procmime_mimeinfo_free_all(mimeinfo);
1496 #define SIGNAL_BLOCK(buffer) { \
1497 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1498 G_CALLBACK(compose_changed_cb), \
1500 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1501 G_CALLBACK(text_inserted), \
1505 #define SIGNAL_UNBLOCK(buffer) { \
1506 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1507 G_CALLBACK(compose_changed_cb), \
1509 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1510 G_CALLBACK(text_inserted), \
1514 static Compose *compose_generic_reply(MsgInfo *msginfo,
1515 ComposeQuoteMode quote_mode,
1516 gboolean to_all, gboolean to_ml,
1518 gboolean followup_and_reply_to,
1522 PrefsAccount *account = NULL;
1523 GtkTextView *textview;
1524 GtkTextBuffer *textbuf;
1525 gboolean quote = FALSE;
1526 const gchar *qmark = NULL;
1527 const gchar *body_fmt = NULL;
1528 gchar *s_system = NULL;
1530 cm_return_val_if_fail(msginfo != NULL, NULL);
1531 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1533 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1535 cm_return_val_if_fail(account != NULL, NULL);
1537 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1539 compose->updating = TRUE;
1541 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1542 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1544 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1545 if (!compose->replyinfo)
1546 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1548 compose_extract_original_charset(compose);
1550 if (msginfo->folder && msginfo->folder->ret_rcpt)
1551 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1553 /* Set save folder */
1554 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1555 gchar *folderidentifier;
1557 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1558 folderidentifier = folder_item_get_identifier(msginfo->folder);
1559 compose_set_save_to(compose, folderidentifier);
1560 g_free(folderidentifier);
1563 if (compose_parse_header(compose, msginfo) < 0) {
1564 compose->updating = FALSE;
1565 compose_destroy(compose);
1569 /* override from name according to folder properties */
1570 if (msginfo->folder && msginfo->folder->prefs &&
1571 msginfo->folder->prefs->reply_with_format &&
1572 msginfo->folder->prefs->reply_override_from_format &&
1573 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1578 /* decode \-escape sequences in the internal representation of the quote format */
1579 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1580 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1583 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1584 compose->gtkaspell);
1586 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1588 quote_fmt_scan_string(tmp);
1591 buf = quote_fmt_get_buffer();
1593 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1595 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1596 quote_fmt_reset_vartable();
1601 textview = (GTK_TEXT_VIEW(compose->text));
1602 textbuf = gtk_text_view_get_buffer(textview);
1603 compose_create_tags(textview, compose);
1605 undo_block(compose->undostruct);
1607 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1608 gtkaspell_block_check(compose->gtkaspell);
1611 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1612 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1613 /* use the reply format of folder (if enabled), or the account's one
1614 (if enabled) or fallback to the global reply format, which is always
1615 enabled (even if empty), and use the relevant quotemark */
1617 if (msginfo->folder && msginfo->folder->prefs &&
1618 msginfo->folder->prefs->reply_with_format) {
1619 qmark = msginfo->folder->prefs->reply_quotemark;
1620 body_fmt = msginfo->folder->prefs->reply_body_format;
1622 } else if (account->reply_with_format) {
1623 qmark = account->reply_quotemark;
1624 body_fmt = account->reply_body_format;
1627 qmark = prefs_common.quotemark;
1628 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1629 body_fmt = gettext(prefs_common.quotefmt);
1636 /* empty quotemark is not allowed */
1637 if (qmark == NULL || *qmark == '\0')
1639 compose_quote_fmt(compose, compose->replyinfo,
1640 body_fmt, qmark, body, FALSE, TRUE,
1641 _("The body of the \"Reply\" template has an error at line %d."));
1642 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1643 quote_fmt_reset_vartable();
1646 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1647 compose_force_encryption(compose, account, FALSE, s_system);
1650 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1651 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1652 compose_force_signing(compose, account, s_system);
1656 SIGNAL_BLOCK(textbuf);
1658 if (account->auto_sig)
1659 compose_insert_sig(compose, FALSE);
1661 compose_wrap_all(compose);
1664 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1665 gtkaspell_highlight_all(compose->gtkaspell);
1666 gtkaspell_unblock_check(compose->gtkaspell);
1668 SIGNAL_UNBLOCK(textbuf);
1670 gtk_widget_grab_focus(compose->text);
1672 undo_unblock(compose->undostruct);
1674 if (prefs_common.auto_exteditor)
1675 compose_exec_ext_editor(compose);
1677 compose->modified = FALSE;
1678 compose_set_title(compose);
1680 compose->updating = FALSE;
1681 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1682 SCROLL_TO_CURSOR(compose);
1684 if (compose->deferred_destroy) {
1685 compose_destroy(compose);
1693 #define INSERT_FW_HEADER(var, hdr) \
1694 if (msginfo->var && *msginfo->var) { \
1695 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1696 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1697 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1700 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1701 gboolean as_attach, const gchar *body,
1702 gboolean no_extedit,
1706 GtkTextView *textview;
1707 GtkTextBuffer *textbuf;
1708 gint cursor_pos = -1;
1711 cm_return_val_if_fail(msginfo != NULL, NULL);
1712 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1715 !(account = compose_guess_forward_account_from_msginfo
1717 account = cur_account;
1719 if (!prefs_common.forward_as_attachment)
1720 mode = COMPOSE_FORWARD_INLINE;
1722 mode = COMPOSE_FORWARD;
1723 compose = compose_create(account, msginfo->folder, mode, batch);
1725 compose->updating = TRUE;
1726 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1727 if (!compose->fwdinfo)
1728 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1730 compose_extract_original_charset(compose);
1732 if (msginfo->subject && *msginfo->subject) {
1733 gchar *buf, *buf2, *p;
1735 buf = p = g_strdup(msginfo->subject);
1736 p += subject_get_prefix_length(p);
1737 memmove(buf, p, strlen(p) + 1);
1739 buf2 = g_strdup_printf("Fw: %s", buf);
1740 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1746 /* override from name according to folder properties */
1747 if (msginfo->folder && msginfo->folder->prefs &&
1748 msginfo->folder->prefs->forward_with_format &&
1749 msginfo->folder->prefs->forward_override_from_format &&
1750 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1754 MsgInfo *full_msginfo = NULL;
1757 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1759 full_msginfo = procmsg_msginfo_copy(msginfo);
1761 /* decode \-escape sequences in the internal representation of the quote format */
1762 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1763 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1766 gtkaspell_block_check(compose->gtkaspell);
1767 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1768 compose->gtkaspell);
1770 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1772 quote_fmt_scan_string(tmp);
1775 buf = quote_fmt_get_buffer();
1777 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1779 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1780 quote_fmt_reset_vartable();
1783 procmsg_msginfo_free(full_msginfo);
1786 textview = GTK_TEXT_VIEW(compose->text);
1787 textbuf = gtk_text_view_get_buffer(textview);
1788 compose_create_tags(textview, compose);
1790 undo_block(compose->undostruct);
1794 msgfile = procmsg_get_message_file(msginfo);
1795 if (!is_file_exist(msgfile))
1796 g_warning("%s: file not exist\n", msgfile);
1798 compose_attach_append(compose, msgfile, msgfile,
1799 "message/rfc822", NULL);
1803 const gchar *qmark = NULL;
1804 const gchar *body_fmt = NULL;
1805 MsgInfo *full_msginfo;
1807 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1809 full_msginfo = procmsg_msginfo_copy(msginfo);
1811 /* use the forward format of folder (if enabled), or the account's one
1812 (if enabled) or fallback to the global forward format, which is always
1813 enabled (even if empty), and use the relevant quotemark */
1814 if (msginfo->folder && msginfo->folder->prefs &&
1815 msginfo->folder->prefs->forward_with_format) {
1816 qmark = msginfo->folder->prefs->forward_quotemark;
1817 body_fmt = msginfo->folder->prefs->forward_body_format;
1819 } else if (account->forward_with_format) {
1820 qmark = account->forward_quotemark;
1821 body_fmt = account->forward_body_format;
1824 qmark = prefs_common.fw_quotemark;
1825 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1826 body_fmt = gettext(prefs_common.fw_quotefmt);
1831 /* empty quotemark is not allowed */
1832 if (qmark == NULL || *qmark == '\0')
1835 compose_quote_fmt(compose, full_msginfo,
1836 body_fmt, qmark, body, FALSE, TRUE,
1837 _("The body of the \"Forward\" template has an error at line %d."));
1838 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1839 quote_fmt_reset_vartable();
1840 compose_attach_parts(compose, msginfo);
1842 procmsg_msginfo_free(full_msginfo);
1845 SIGNAL_BLOCK(textbuf);
1847 if (account->auto_sig)
1848 compose_insert_sig(compose, FALSE);
1850 compose_wrap_all(compose);
1853 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1854 gtkaspell_highlight_all(compose->gtkaspell);
1855 gtkaspell_unblock_check(compose->gtkaspell);
1857 SIGNAL_UNBLOCK(textbuf);
1859 cursor_pos = quote_fmt_get_cursor_pos();
1860 if (cursor_pos == -1)
1861 gtk_widget_grab_focus(compose->header_last->entry);
1863 gtk_widget_grab_focus(compose->text);
1865 if (!no_extedit && prefs_common.auto_exteditor)
1866 compose_exec_ext_editor(compose);
1869 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1870 gchar *folderidentifier;
1872 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1873 folderidentifier = folder_item_get_identifier(msginfo->folder);
1874 compose_set_save_to(compose, folderidentifier);
1875 g_free(folderidentifier);
1878 undo_unblock(compose->undostruct);
1880 compose->modified = FALSE;
1881 compose_set_title(compose);
1883 compose->updating = FALSE;
1884 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1885 SCROLL_TO_CURSOR(compose);
1887 if (compose->deferred_destroy) {
1888 compose_destroy(compose);
1892 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1897 #undef INSERT_FW_HEADER
1899 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1902 GtkTextView *textview;
1903 GtkTextBuffer *textbuf;
1907 gboolean single_mail = TRUE;
1909 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1911 if (g_slist_length(msginfo_list) > 1)
1912 single_mail = FALSE;
1914 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1915 if (((MsgInfo *)msginfo->data)->folder == NULL)
1918 /* guess account from first selected message */
1920 !(account = compose_guess_forward_account_from_msginfo
1921 (msginfo_list->data)))
1922 account = cur_account;
1924 cm_return_val_if_fail(account != NULL, NULL);
1926 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1927 if (msginfo->data) {
1928 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1929 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1933 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1934 g_warning("no msginfo_list");
1938 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1940 compose->updating = TRUE;
1942 /* override from name according to folder properties */
1943 if (msginfo_list->data) {
1944 MsgInfo *msginfo = msginfo_list->data;
1946 if (msginfo->folder && msginfo->folder->prefs &&
1947 msginfo->folder->prefs->forward_with_format &&
1948 msginfo->folder->prefs->forward_override_from_format &&
1949 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1954 /* decode \-escape sequences in the internal representation of the quote format */
1955 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1956 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1959 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1960 compose->gtkaspell);
1962 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1964 quote_fmt_scan_string(tmp);
1967 buf = quote_fmt_get_buffer();
1969 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1971 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1972 quote_fmt_reset_vartable();
1978 textview = GTK_TEXT_VIEW(compose->text);
1979 textbuf = gtk_text_view_get_buffer(textview);
1980 compose_create_tags(textview, compose);
1982 undo_block(compose->undostruct);
1983 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1984 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1986 if (!is_file_exist(msgfile))
1987 g_warning("%s: file not exist\n", msgfile);
1989 compose_attach_append(compose, msgfile, msgfile,
1990 "message/rfc822", NULL);
1995 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1996 if (info->subject && *info->subject) {
1997 gchar *buf, *buf2, *p;
1999 buf = p = g_strdup(info->subject);
2000 p += subject_get_prefix_length(p);
2001 memmove(buf, p, strlen(p) + 1);
2003 buf2 = g_strdup_printf("Fw: %s", buf);
2004 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2010 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2011 _("Fw: multiple emails"));
2014 SIGNAL_BLOCK(textbuf);
2016 if (account->auto_sig)
2017 compose_insert_sig(compose, FALSE);
2019 compose_wrap_all(compose);
2021 SIGNAL_UNBLOCK(textbuf);
2023 gtk_text_buffer_get_start_iter(textbuf, &iter);
2024 gtk_text_buffer_place_cursor(textbuf, &iter);
2026 gtk_widget_grab_focus(compose->header_last->entry);
2027 undo_unblock(compose->undostruct);
2028 compose->modified = FALSE;
2029 compose_set_title(compose);
2031 compose->updating = FALSE;
2032 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2033 SCROLL_TO_CURSOR(compose);
2035 if (compose->deferred_destroy) {
2036 compose_destroy(compose);
2040 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2045 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2047 GtkTextIter start = *iter;
2048 GtkTextIter end_iter;
2049 int start_pos = gtk_text_iter_get_offset(&start);
2051 if (!compose->account->sig_sep)
2054 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2055 start_pos+strlen(compose->account->sig_sep));
2057 /* check sig separator */
2058 str = gtk_text_iter_get_text(&start, &end_iter);
2059 if (!strcmp(str, compose->account->sig_sep)) {
2061 /* check end of line (\n) */
2062 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2063 start_pos+strlen(compose->account->sig_sep));
2064 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2065 start_pos+strlen(compose->account->sig_sep)+1);
2066 tmp = gtk_text_iter_get_text(&start, &end_iter);
2067 if (!strcmp(tmp,"\n")) {
2079 static void compose_colorize_signature(Compose *compose)
2081 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2083 GtkTextIter end_iter;
2084 gtk_text_buffer_get_start_iter(buffer, &iter);
2085 while (gtk_text_iter_forward_line(&iter))
2086 if (compose_is_sig_separator(compose, buffer, &iter)) {
2087 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2088 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2092 #define BLOCK_WRAP() { \
2093 prev_autowrap = compose->autowrap; \
2094 buffer = gtk_text_view_get_buffer( \
2095 GTK_TEXT_VIEW(compose->text)); \
2096 compose->autowrap = FALSE; \
2098 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2099 G_CALLBACK(compose_changed_cb), \
2101 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2102 G_CALLBACK(text_inserted), \
2105 #define UNBLOCK_WRAP() { \
2106 compose->autowrap = prev_autowrap; \
2107 if (compose->autowrap) { \
2108 gint old = compose->draft_timeout_tag; \
2109 compose->draft_timeout_tag = -2; \
2110 compose_wrap_all(compose); \
2111 compose->draft_timeout_tag = old; \
2114 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2115 G_CALLBACK(compose_changed_cb), \
2117 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2118 G_CALLBACK(text_inserted), \
2122 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2124 Compose *compose = NULL;
2125 PrefsAccount *account = NULL;
2126 GtkTextView *textview;
2127 GtkTextBuffer *textbuf;
2131 gchar buf[BUFFSIZE];
2132 gboolean use_signing = FALSE;
2133 gboolean use_encryption = FALSE;
2134 gchar *privacy_system = NULL;
2135 int priority = PRIORITY_NORMAL;
2136 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2137 gboolean autowrap = prefs_common.autowrap;
2138 gboolean autoindent = prefs_common.auto_indent;
2139 HeaderEntry *manual_headers = NULL;
2141 cm_return_val_if_fail(msginfo != NULL, NULL);
2142 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2144 if (compose_put_existing_to_front(msginfo)) {
2148 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2149 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2150 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2151 gchar queueheader_buf[BUFFSIZE];
2154 /* Select Account from queue headers */
2155 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2156 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2157 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2158 account = account_find_from_id(id);
2160 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2161 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2162 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2163 account = account_find_from_id(id);
2165 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2166 sizeof(queueheader_buf), "NAID:")) {
2167 id = atoi(&queueheader_buf[strlen("NAID:")]);
2168 account = account_find_from_id(id);
2170 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2171 sizeof(queueheader_buf), "MAID:")) {
2172 id = atoi(&queueheader_buf[strlen("MAID:")]);
2173 account = account_find_from_id(id);
2175 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2176 sizeof(queueheader_buf), "S:")) {
2177 account = account_find_from_address(queueheader_buf, FALSE);
2179 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2180 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2181 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2182 use_signing = param;
2185 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2186 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2187 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2188 use_signing = param;
2191 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2192 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2193 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2194 use_encryption = param;
2196 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2197 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2198 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2199 use_encryption = param;
2201 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2202 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2203 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2206 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2207 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2208 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2211 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2212 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2213 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2215 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2216 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2217 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2219 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2220 sizeof(queueheader_buf), "X-Priority: ")) {
2221 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2224 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2225 sizeof(queueheader_buf), "RMID:")) {
2226 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2227 if (tokens[0] && tokens[1] && tokens[2]) {
2228 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2229 if (orig_item != NULL) {
2230 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2235 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2236 sizeof(queueheader_buf), "FMID:")) {
2237 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2238 if (tokens[0] && tokens[1] && tokens[2]) {
2239 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2240 if (orig_item != NULL) {
2241 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2246 /* Get manual headers */
2247 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2248 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2249 if (*listmh != '\0') {
2250 debug_print("Got manual headers: %s\n", listmh);
2251 manual_headers = procheader_entries_from_str(listmh);
2256 account = msginfo->folder->folder->account;
2259 if (!account && prefs_common.reedit_account_autosel) {
2260 gchar from[BUFFSIZE];
2261 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2262 extract_address(from);
2263 account = account_find_from_address(from, FALSE);
2267 account = cur_account;
2269 cm_return_val_if_fail(account != NULL, NULL);
2271 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2273 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2274 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2275 compose->autowrap = autowrap;
2276 compose->replyinfo = replyinfo;
2277 compose->fwdinfo = fwdinfo;
2279 compose->updating = TRUE;
2280 compose->priority = priority;
2282 if (privacy_system != NULL) {
2283 compose->privacy_system = privacy_system;
2284 compose_use_signing(compose, use_signing);
2285 compose_use_encryption(compose, use_encryption);
2286 compose_update_privacy_system_menu_item(compose, FALSE);
2288 activate_privacy_system(compose, account, FALSE);
2291 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2293 compose_extract_original_charset(compose);
2295 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2296 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2297 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2298 gchar queueheader_buf[BUFFSIZE];
2300 /* Set message save folder */
2301 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2302 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2303 compose_set_save_to(compose, &queueheader_buf[4]);
2305 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2306 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2308 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2313 if (compose_parse_header(compose, msginfo) < 0) {
2314 compose->updating = FALSE;
2315 compose_destroy(compose);
2318 compose_reedit_set_entry(compose, msginfo);
2320 textview = GTK_TEXT_VIEW(compose->text);
2321 textbuf = gtk_text_view_get_buffer(textview);
2322 compose_create_tags(textview, compose);
2324 mark = gtk_text_buffer_get_insert(textbuf);
2325 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2327 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2328 G_CALLBACK(compose_changed_cb),
2331 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2332 fp = procmime_get_first_encrypted_text_content(msginfo);
2334 compose_force_encryption(compose, account, TRUE, NULL);
2337 fp = procmime_get_first_text_content(msginfo);
2340 g_warning("Can't get text part\n");
2344 gboolean prev_autowrap;
2345 GtkTextBuffer *buffer;
2347 while (fgets(buf, sizeof(buf), fp) != NULL) {
2349 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2355 compose_attach_parts(compose, msginfo);
2357 compose_colorize_signature(compose);
2359 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2360 G_CALLBACK(compose_changed_cb),
2363 if (manual_headers != NULL) {
2364 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2365 procheader_entries_free(manual_headers);
2366 compose->updating = FALSE;
2367 compose_destroy(compose);
2370 procheader_entries_free(manual_headers);
2373 gtk_widget_grab_focus(compose->text);
2375 if (prefs_common.auto_exteditor) {
2376 compose_exec_ext_editor(compose);
2378 compose->modified = FALSE;
2379 compose_set_title(compose);
2381 compose->updating = FALSE;
2382 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2383 SCROLL_TO_CURSOR(compose);
2385 if (compose->deferred_destroy) {
2386 compose_destroy(compose);
2390 compose->sig_str = account_get_signature_str(compose->account);
2392 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2397 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2404 cm_return_val_if_fail(msginfo != NULL, NULL);
2407 account = account_get_reply_account(msginfo,
2408 prefs_common.reply_account_autosel);
2409 cm_return_val_if_fail(account != NULL, NULL);
2411 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2413 compose->updating = TRUE;
2415 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2416 compose->replyinfo = NULL;
2417 compose->fwdinfo = NULL;
2419 compose_show_first_last_header(compose, TRUE);
2421 gtk_widget_grab_focus(compose->header_last->entry);
2423 filename = procmsg_get_message_file(msginfo);
2425 if (filename == NULL) {
2426 compose->updating = FALSE;
2427 compose_destroy(compose);
2432 compose->redirect_filename = filename;
2434 /* Set save folder */
2435 item = msginfo->folder;
2436 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2437 gchar *folderidentifier;
2439 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2440 folderidentifier = folder_item_get_identifier(item);
2441 compose_set_save_to(compose, folderidentifier);
2442 g_free(folderidentifier);
2445 compose_attach_parts(compose, msginfo);
2447 if (msginfo->subject)
2448 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2450 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2452 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2453 _("The body of the \"Redirect\" template has an error at line %d."));
2454 quote_fmt_reset_vartable();
2455 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2457 compose_colorize_signature(compose);
2460 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2461 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2462 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2464 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2465 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2466 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2467 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2468 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2469 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2470 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2471 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2473 if (compose->toolbar->draft_btn)
2474 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2475 if (compose->toolbar->insert_btn)
2476 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2477 if (compose->toolbar->attach_btn)
2478 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2479 if (compose->toolbar->sig_btn)
2480 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2481 if (compose->toolbar->exteditor_btn)
2482 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2483 if (compose->toolbar->linewrap_current_btn)
2484 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2485 if (compose->toolbar->linewrap_all_btn)
2486 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2488 compose->modified = FALSE;
2489 compose_set_title(compose);
2490 compose->updating = FALSE;
2491 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2492 SCROLL_TO_CURSOR(compose);
2494 if (compose->deferred_destroy) {
2495 compose_destroy(compose);
2499 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2504 GList *compose_get_compose_list(void)
2506 return compose_list;
2509 void compose_entry_append(Compose *compose, const gchar *address,
2510 ComposeEntryType type, ComposePrefType pref_type)
2512 const gchar *header;
2514 gboolean in_quote = FALSE;
2515 if (!address || *address == '\0') return;
2522 header = N_("Bcc:");
2524 case COMPOSE_REPLYTO:
2525 header = N_("Reply-To:");
2527 case COMPOSE_NEWSGROUPS:
2528 header = N_("Newsgroups:");
2530 case COMPOSE_FOLLOWUPTO:
2531 header = N_( "Followup-To:");
2533 case COMPOSE_INREPLYTO:
2534 header = N_( "In-Reply-To:");
2541 header = prefs_common_translated_header_name(header);
2543 cur = begin = (gchar *)address;
2545 /* we separate the line by commas, but not if we're inside a quoted
2547 while (*cur != '\0') {
2549 in_quote = !in_quote;
2550 if (*cur == ',' && !in_quote) {
2551 gchar *tmp = g_strdup(begin);
2553 tmp[cur-begin]='\0';
2556 while (*tmp == ' ' || *tmp == '\t')
2558 compose_add_header_entry(compose, header, tmp, pref_type);
2565 gchar *tmp = g_strdup(begin);
2567 tmp[cur-begin]='\0';
2568 while (*tmp == ' ' || *tmp == '\t')
2570 compose_add_header_entry(compose, header, tmp, pref_type);
2575 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2577 #if !GTK_CHECK_VERSION(3, 0, 0)
2578 static GdkColor yellow;
2579 static GdkColor black;
2580 static gboolean yellow_initialised = FALSE;
2582 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2583 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2588 #if !GTK_CHECK_VERSION(3, 0, 0)
2589 if (!yellow_initialised) {
2590 gdk_color_parse("#f5f6be", &yellow);
2591 gdk_color_parse("#000000", &black);
2592 yellow_initialised = gdk_colormap_alloc_color(
2593 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2594 yellow_initialised &= gdk_colormap_alloc_color(
2595 gdk_colormap_get_system(), &black, FALSE, TRUE);
2599 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2600 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2601 if (gtk_entry_get_text(entry) &&
2602 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2603 #if !GTK_CHECK_VERSION(3, 0, 0)
2604 if (yellow_initialised) {
2606 gtk_widget_modify_base(
2607 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2608 GTK_STATE_NORMAL, &yellow);
2609 gtk_widget_modify_text(
2610 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2611 GTK_STATE_NORMAL, &black);
2612 #if !GTK_CHECK_VERSION(3, 0, 0)
2619 void compose_toolbar_cb(gint action, gpointer data)
2621 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2622 Compose *compose = (Compose*)toolbar_item->parent;
2624 cm_return_if_fail(compose != NULL);
2628 compose_send_cb(NULL, compose);
2631 compose_send_later_cb(NULL, compose);
2634 compose_draft(compose, COMPOSE_QUIT_EDITING);
2637 compose_insert_file_cb(NULL, compose);
2640 compose_attach_cb(NULL, compose);
2643 compose_insert_sig(compose, FALSE);
2646 compose_ext_editor_cb(NULL, compose);
2648 case A_LINEWRAP_CURRENT:
2649 compose_beautify_paragraph(compose, NULL, TRUE);
2651 case A_LINEWRAP_ALL:
2652 compose_wrap_all_full(compose, TRUE);
2655 compose_address_cb(NULL, compose);
2658 case A_CHECK_SPELLING:
2659 compose_check_all(NULL, compose);
2667 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2672 gchar *subject = NULL;
2676 gchar **attach = NULL;
2677 gchar *inreplyto = NULL;
2678 MailField mfield = NO_FIELD_PRESENT;
2680 /* get mailto parts but skip from */
2681 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2684 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2685 mfield = TO_FIELD_PRESENT;
2688 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2690 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2692 if (!g_utf8_validate (subject, -1, NULL)) {
2693 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2694 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2697 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2699 mfield = SUBJECT_FIELD_PRESENT;
2702 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2703 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2706 gboolean prev_autowrap = compose->autowrap;
2708 compose->autowrap = FALSE;
2710 mark = gtk_text_buffer_get_insert(buffer);
2711 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2713 if (!g_utf8_validate (body, -1, NULL)) {
2714 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2715 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2718 gtk_text_buffer_insert(buffer, &iter, body, -1);
2720 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2722 compose->autowrap = prev_autowrap;
2723 if (compose->autowrap)
2724 compose_wrap_all(compose);
2725 mfield = BODY_FIELD_PRESENT;
2729 gint i = 0, att = 0;
2730 gchar *warn_files = NULL;
2731 while (attach[i] != NULL) {
2732 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2733 if (utf8_filename) {
2734 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2735 gchar *tmp = g_strdup_printf("%s%s\n",
2736 warn_files?warn_files:"",
2742 g_free(utf8_filename);
2744 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2749 alertpanel_notice(ngettext(
2750 "The following file has been attached: \n%s",
2751 "The following files have been attached: \n%s", att), warn_files);
2756 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2769 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2771 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2772 {"Cc:", NULL, TRUE},
2773 {"References:", NULL, FALSE},
2774 {"Bcc:", NULL, TRUE},
2775 {"Newsgroups:", NULL, TRUE},
2776 {"Followup-To:", NULL, TRUE},
2777 {"List-Post:", NULL, FALSE},
2778 {"X-Priority:", NULL, FALSE},
2779 {NULL, NULL, FALSE}};
2795 cm_return_val_if_fail(msginfo != NULL, -1);
2797 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2798 procheader_get_header_fields(fp, hentry);
2801 if (hentry[H_REPLY_TO].body != NULL) {
2802 if (hentry[H_REPLY_TO].body[0] != '\0') {
2804 conv_unmime_header(hentry[H_REPLY_TO].body,
2807 g_free(hentry[H_REPLY_TO].body);
2808 hentry[H_REPLY_TO].body = NULL;
2810 if (hentry[H_CC].body != NULL) {
2811 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2812 g_free(hentry[H_CC].body);
2813 hentry[H_CC].body = NULL;
2815 if (hentry[H_REFERENCES].body != NULL) {
2816 if (compose->mode == COMPOSE_REEDIT)
2817 compose->references = hentry[H_REFERENCES].body;
2819 compose->references = compose_parse_references
2820 (hentry[H_REFERENCES].body, msginfo->msgid);
2821 g_free(hentry[H_REFERENCES].body);
2823 hentry[H_REFERENCES].body = NULL;
2825 if (hentry[H_BCC].body != NULL) {
2826 if (compose->mode == COMPOSE_REEDIT)
2828 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2829 g_free(hentry[H_BCC].body);
2830 hentry[H_BCC].body = NULL;
2832 if (hentry[H_NEWSGROUPS].body != NULL) {
2833 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2834 hentry[H_NEWSGROUPS].body = NULL;
2836 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2837 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2838 compose->followup_to =
2839 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2842 g_free(hentry[H_FOLLOWUP_TO].body);
2843 hentry[H_FOLLOWUP_TO].body = NULL;
2845 if (hentry[H_LIST_POST].body != NULL) {
2846 gchar *to = NULL, *start = NULL;
2848 extract_address(hentry[H_LIST_POST].body);
2849 if (hentry[H_LIST_POST].body[0] != '\0') {
2850 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2852 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2853 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2856 g_free(compose->ml_post);
2857 compose->ml_post = to;
2860 g_free(hentry[H_LIST_POST].body);
2861 hentry[H_LIST_POST].body = NULL;
2864 /* CLAWS - X-Priority */
2865 if (compose->mode == COMPOSE_REEDIT)
2866 if (hentry[H_X_PRIORITY].body != NULL) {
2869 priority = atoi(hentry[H_X_PRIORITY].body);
2870 g_free(hentry[H_X_PRIORITY].body);
2872 hentry[H_X_PRIORITY].body = NULL;
2874 if (priority < PRIORITY_HIGHEST ||
2875 priority > PRIORITY_LOWEST)
2876 priority = PRIORITY_NORMAL;
2878 compose->priority = priority;
2881 if (compose->mode == COMPOSE_REEDIT) {
2882 if (msginfo->inreplyto && *msginfo->inreplyto)
2883 compose->inreplyto = g_strdup(msginfo->inreplyto);
2887 if (msginfo->msgid && *msginfo->msgid)
2888 compose->inreplyto = g_strdup(msginfo->msgid);
2890 if (!compose->references) {
2891 if (msginfo->msgid && *msginfo->msgid) {
2892 if (msginfo->inreplyto && *msginfo->inreplyto)
2893 compose->references =
2894 g_strdup_printf("<%s>\n\t<%s>",
2898 compose->references =
2899 g_strconcat("<", msginfo->msgid, ">",
2901 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2902 compose->references =
2903 g_strconcat("<", msginfo->inreplyto, ">",
2911 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2916 cm_return_val_if_fail(msginfo != NULL, -1);
2918 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2919 procheader_get_header_fields(fp, entries);
2923 while (he != NULL && he->name != NULL) {
2925 GtkListStore *model = NULL;
2927 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
2928 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
2929 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
2930 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
2931 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
2938 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2940 GSList *ref_id_list, *cur;
2944 ref_id_list = references_list_append(NULL, ref);
2945 if (!ref_id_list) return NULL;
2946 if (msgid && *msgid)
2947 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2952 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2953 /* "<" + Message-ID + ">" + CR+LF+TAB */
2954 len += strlen((gchar *)cur->data) + 5;
2956 if (len > MAX_REFERENCES_LEN) {
2957 /* remove second message-ID */
2958 if (ref_id_list && ref_id_list->next &&
2959 ref_id_list->next->next) {
2960 g_free(ref_id_list->next->data);
2961 ref_id_list = g_slist_remove
2962 (ref_id_list, ref_id_list->next->data);
2964 slist_free_strings(ref_id_list);
2965 g_slist_free(ref_id_list);
2972 new_ref = g_string_new("");
2973 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2974 if (new_ref->len > 0)
2975 g_string_append(new_ref, "\n\t");
2976 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2979 slist_free_strings(ref_id_list);
2980 g_slist_free(ref_id_list);
2982 new_ref_str = new_ref->str;
2983 g_string_free(new_ref, FALSE);
2988 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2989 const gchar *fmt, const gchar *qmark,
2990 const gchar *body, gboolean rewrap,
2991 gboolean need_unescape,
2992 const gchar *err_msg)
2994 MsgInfo* dummyinfo = NULL;
2995 gchar *quote_str = NULL;
2997 gboolean prev_autowrap;
2998 const gchar *trimmed_body = body;
2999 gint cursor_pos = -1;
3000 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3001 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3006 SIGNAL_BLOCK(buffer);
3009 dummyinfo = compose_msginfo_new_from_compose(compose);
3010 msginfo = dummyinfo;
3013 if (qmark != NULL) {
3015 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3016 compose->gtkaspell);
3018 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3020 quote_fmt_scan_string(qmark);
3023 buf = quote_fmt_get_buffer();
3025 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3027 Xstrdup_a(quote_str, buf, goto error)
3030 if (fmt && *fmt != '\0') {
3033 while (*trimmed_body == '\n')
3037 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3038 compose->gtkaspell);
3040 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3042 if (need_unescape) {
3045 /* decode \-escape sequences in the internal representation of the quote format */
3046 tmp = g_malloc(strlen(fmt)+1);
3047 pref_get_unescaped_pref(tmp, fmt);
3048 quote_fmt_scan_string(tmp);
3052 quote_fmt_scan_string(fmt);
3056 buf = quote_fmt_get_buffer();
3058 gint line = quote_fmt_get_line();
3059 alertpanel_error(err_msg, line);
3065 prev_autowrap = compose->autowrap;
3066 compose->autowrap = FALSE;
3068 mark = gtk_text_buffer_get_insert(buffer);
3069 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3070 if (g_utf8_validate(buf, -1, NULL)) {
3071 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3073 gchar *tmpout = NULL;
3074 tmpout = conv_codeset_strdup
3075 (buf, conv_get_locale_charset_str_no_utf8(),
3077 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3079 tmpout = g_malloc(strlen(buf)*2+1);
3080 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3082 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3086 cursor_pos = quote_fmt_get_cursor_pos();
3087 if (cursor_pos == -1)
3088 cursor_pos = gtk_text_iter_get_offset(&iter);
3089 compose->set_cursor_pos = cursor_pos;
3091 gtk_text_buffer_get_start_iter(buffer, &iter);
3092 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3093 gtk_text_buffer_place_cursor(buffer, &iter);
3095 compose->autowrap = prev_autowrap;
3096 if (compose->autowrap && rewrap)
3097 compose_wrap_all(compose);
3104 SIGNAL_UNBLOCK(buffer);
3106 procmsg_msginfo_free( dummyinfo );
3111 /* if ml_post is of type addr@host and from is of type
3112 * addr-anything@host, return TRUE
3114 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3116 gchar *left_ml = NULL;
3117 gchar *right_ml = NULL;
3118 gchar *left_from = NULL;
3119 gchar *right_from = NULL;
3120 gboolean result = FALSE;
3122 if (!ml_post || !from)
3125 left_ml = g_strdup(ml_post);
3126 if (strstr(left_ml, "@")) {
3127 right_ml = strstr(left_ml, "@")+1;
3128 *(strstr(left_ml, "@")) = '\0';
3131 left_from = g_strdup(from);
3132 if (strstr(left_from, "@")) {
3133 right_from = strstr(left_from, "@")+1;
3134 *(strstr(left_from, "@")) = '\0';
3137 if (left_ml && left_from && right_ml && right_from
3138 && !strncmp(left_from, left_ml, strlen(left_ml))
3139 && !strcmp(right_from, right_ml)) {
3148 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3149 gboolean respect_default_to)
3153 if (!folder || !folder->prefs)
3156 if (respect_default_to && folder->prefs->enable_default_to) {
3157 compose_entry_append(compose, folder->prefs->default_to,
3158 COMPOSE_TO, PREF_FOLDER);
3159 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3161 if (folder->prefs->enable_default_cc)
3162 compose_entry_append(compose, folder->prefs->default_cc,
3163 COMPOSE_CC, PREF_FOLDER);
3164 if (folder->prefs->enable_default_bcc)
3165 compose_entry_append(compose, folder->prefs->default_bcc,
3166 COMPOSE_BCC, PREF_FOLDER);
3167 if (folder->prefs->enable_default_replyto)
3168 compose_entry_append(compose, folder->prefs->default_replyto,
3169 COMPOSE_REPLYTO, PREF_FOLDER);
3172 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3177 if (!compose || !msginfo)
3180 if (msginfo->subject && *msginfo->subject) {
3181 buf = p = g_strdup(msginfo->subject);
3182 p += subject_get_prefix_length(p);
3183 memmove(buf, p, strlen(p) + 1);
3185 buf2 = g_strdup_printf("Re: %s", buf);
3186 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3191 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3194 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3195 gboolean to_all, gboolean to_ml,
3197 gboolean followup_and_reply_to)
3199 GSList *cc_list = NULL;
3202 gchar *replyto = NULL;
3203 gchar *ac_email = NULL;
3205 gboolean reply_to_ml = FALSE;
3206 gboolean default_reply_to = FALSE;
3208 cm_return_if_fail(compose->account != NULL);
3209 cm_return_if_fail(msginfo != NULL);
3211 reply_to_ml = to_ml && compose->ml_post;
3213 default_reply_to = msginfo->folder &&
3214 msginfo->folder->prefs->enable_default_reply_to;
3216 if (compose->account->protocol != A_NNTP) {
3217 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3219 if (reply_to_ml && !default_reply_to) {
3221 gboolean is_subscr = is_subscription(compose->ml_post,
3224 /* normal answer to ml post with a reply-to */
3225 compose_entry_append(compose,
3227 COMPOSE_TO, PREF_ML);
3228 if (compose->replyto)
3229 compose_entry_append(compose,
3231 COMPOSE_CC, PREF_ML);
3233 /* answer to subscription confirmation */
3234 if (compose->replyto)
3235 compose_entry_append(compose,
3237 COMPOSE_TO, PREF_ML);
3238 else if (msginfo->from)
3239 compose_entry_append(compose,
3241 COMPOSE_TO, PREF_ML);
3244 else if (!(to_all || to_sender) && default_reply_to) {
3245 compose_entry_append(compose,
3246 msginfo->folder->prefs->default_reply_to,
3247 COMPOSE_TO, PREF_FOLDER);
3248 compose_entry_mark_default_to(compose,
3249 msginfo->folder->prefs->default_reply_to);
3254 Xstrdup_a(tmp1, msginfo->from, return);
3255 extract_address(tmp1);
3256 if (to_all || to_sender ||
3257 !account_find_from_address(tmp1, FALSE))
3258 compose_entry_append(compose,
3259 (compose->replyto && !to_sender)
3260 ? compose->replyto :
3261 msginfo->from ? msginfo->from : "",
3262 COMPOSE_TO, PREF_NONE);
3263 else if (!to_all && !to_sender) {
3264 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3265 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3266 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3267 if (compose->replyto) {
3268 compose_entry_append(compose,
3270 COMPOSE_TO, PREF_NONE);
3272 compose_entry_append(compose,
3273 msginfo->from ? msginfo->from : "",
3274 COMPOSE_TO, PREF_NONE);
3277 /* replying to own mail, use original recp */
3278 compose_entry_append(compose,
3279 msginfo->to ? msginfo->to : "",
3280 COMPOSE_TO, PREF_NONE);
3281 compose_entry_append(compose,
3282 msginfo->cc ? msginfo->cc : "",
3283 COMPOSE_CC, PREF_NONE);
3288 if (to_sender || (compose->followup_to &&
3289 !strncmp(compose->followup_to, "poster", 6)))
3290 compose_entry_append
3292 (compose->replyto ? compose->replyto :
3293 msginfo->from ? msginfo->from : ""),
3294 COMPOSE_TO, PREF_NONE);
3296 else if (followup_and_reply_to || to_all) {
3297 compose_entry_append
3299 (compose->replyto ? compose->replyto :
3300 msginfo->from ? msginfo->from : ""),
3301 COMPOSE_TO, PREF_NONE);
3303 compose_entry_append
3305 compose->followup_to ? compose->followup_to :
3306 compose->newsgroups ? compose->newsgroups : "",
3307 COMPOSE_NEWSGROUPS, PREF_NONE);
3310 compose_entry_append
3312 compose->followup_to ? compose->followup_to :
3313 compose->newsgroups ? compose->newsgroups : "",
3314 COMPOSE_NEWSGROUPS, PREF_NONE);
3316 compose_reply_set_subject(compose, msginfo);
3318 if (to_ml && compose->ml_post) return;
3319 if (!to_all || compose->account->protocol == A_NNTP) return;
3321 if (compose->replyto) {
3322 Xstrdup_a(replyto, compose->replyto, return);
3323 extract_address(replyto);
3325 if (msginfo->from) {
3326 Xstrdup_a(from, msginfo->from, return);
3327 extract_address(from);
3330 if (replyto && from)
3331 cc_list = address_list_append_with_comments(cc_list, from);
3332 if (to_all && msginfo->folder &&
3333 msginfo->folder->prefs->enable_default_reply_to)
3334 cc_list = address_list_append_with_comments(cc_list,
3335 msginfo->folder->prefs->default_reply_to);
3336 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3337 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3339 ac_email = g_utf8_strdown(compose->account->address, -1);
3342 for (cur = cc_list; cur != NULL; cur = cur->next) {
3343 gchar *addr = g_utf8_strdown(cur->data, -1);
3344 extract_address(addr);
3346 if (strcmp(ac_email, addr))
3347 compose_entry_append(compose, (gchar *)cur->data,
3348 COMPOSE_CC, PREF_NONE);
3350 debug_print("Cc address same as compose account's, ignoring\n");
3355 slist_free_strings(cc_list);
3356 g_slist_free(cc_list);
3362 #define SET_ENTRY(entry, str) \
3365 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3368 #define SET_ADDRESS(type, str) \
3371 compose_entry_append(compose, str, type, PREF_NONE); \
3374 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3376 cm_return_if_fail(msginfo != NULL);
3378 SET_ENTRY(subject_entry, msginfo->subject);
3379 SET_ENTRY(from_name, msginfo->from);
3380 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3381 SET_ADDRESS(COMPOSE_CC, compose->cc);
3382 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3383 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3384 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3385 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3387 compose_update_priority_menu_item(compose);
3388 compose_update_privacy_system_menu_item(compose, FALSE);
3389 compose_show_first_last_header(compose, TRUE);
3395 static void compose_insert_sig(Compose *compose, gboolean replace)
3397 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3398 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3400 GtkTextIter iter, iter_end;
3401 gint cur_pos, ins_pos;
3402 gboolean prev_autowrap;
3403 gboolean found = FALSE;
3404 gboolean exists = FALSE;
3406 cm_return_if_fail(compose->account != NULL);
3410 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3411 G_CALLBACK(compose_changed_cb),
3414 mark = gtk_text_buffer_get_insert(buffer);
3415 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3416 cur_pos = gtk_text_iter_get_offset (&iter);
3419 gtk_text_buffer_get_end_iter(buffer, &iter);
3421 exists = (compose->sig_str != NULL);
3424 GtkTextIter first_iter, start_iter, end_iter;
3426 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3428 if (!exists || compose->sig_str[0] == '\0')
3431 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3432 compose->signature_tag);
3435 /* include previous \n\n */
3436 gtk_text_iter_backward_chars(&first_iter, 1);
3437 start_iter = first_iter;
3438 end_iter = first_iter;
3440 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3441 compose->signature_tag);
3442 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3443 compose->signature_tag);
3445 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3451 g_free(compose->sig_str);
3452 compose->sig_str = account_get_signature_str(compose->account);
3454 cur_pos = gtk_text_iter_get_offset(&iter);
3456 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3457 g_free(compose->sig_str);
3458 compose->sig_str = NULL;
3460 if (compose->sig_inserted == FALSE)
3461 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3462 compose->sig_inserted = TRUE;
3464 cur_pos = gtk_text_iter_get_offset(&iter);
3465 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3467 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3468 gtk_text_iter_forward_chars(&iter, 1);
3469 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3470 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3472 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3473 cur_pos = gtk_text_buffer_get_char_count (buffer);
3476 /* put the cursor where it should be
3477 * either where the quote_fmt says, either where it was */
3478 if (compose->set_cursor_pos < 0)
3479 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3481 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3482 compose->set_cursor_pos);
3484 compose->set_cursor_pos = -1;
3485 gtk_text_buffer_place_cursor(buffer, &iter);
3486 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3487 G_CALLBACK(compose_changed_cb),
3493 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3496 GtkTextBuffer *buffer;
3499 const gchar *cur_encoding;
3500 gchar buf[BUFFSIZE];
3503 gboolean prev_autowrap;
3504 gboolean badtxt = FALSE;
3505 struct stat file_stat;
3508 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3510 /* get the size of the file we are about to insert */
3511 ret = g_stat(file, &file_stat);
3513 gchar *shortfile = g_path_get_basename(file);
3514 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3516 return COMPOSE_INSERT_NO_FILE;
3517 } else if (prefs_common.warn_large_insert == TRUE) {
3519 /* ask user for confirmation if the file is large */
3520 if (prefs_common.warn_large_insert_size < 0 ||
3521 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3525 msg = g_strdup_printf(_("You are about to insert a file of %s "
3526 "in the message body. Are you sure you want to do that?"),
3527 to_human_readable(file_stat.st_size));
3528 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3529 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3532 /* do we ask for confirmation next time? */
3533 if (aval & G_ALERTDISABLE) {
3534 /* no confirmation next time, disable feature in preferences */
3535 aval &= ~G_ALERTDISABLE;
3536 prefs_common.warn_large_insert = FALSE;
3539 /* abort file insertion if user canceled action */
3540 if (aval != G_ALERTALTERNATE) {
3541 return COMPOSE_INSERT_NO_FILE;
3547 if ((fp = g_fopen(file, "rb")) == NULL) {
3548 FILE_OP_ERROR(file, "fopen");
3549 return COMPOSE_INSERT_READ_ERROR;
3552 prev_autowrap = compose->autowrap;
3553 compose->autowrap = FALSE;
3555 text = GTK_TEXT_VIEW(compose->text);
3556 buffer = gtk_text_view_get_buffer(text);
3557 mark = gtk_text_buffer_get_insert(buffer);
3558 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3560 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3561 G_CALLBACK(text_inserted),
3564 cur_encoding = conv_get_locale_charset_str_no_utf8();
3566 while (fgets(buf, sizeof(buf), fp) != NULL) {
3569 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3570 str = g_strdup(buf);
3572 str = conv_codeset_strdup
3573 (buf, cur_encoding, CS_INTERNAL);
3576 /* strip <CR> if DOS/Windows file,
3577 replace <CR> with <LF> if Macintosh file. */
3580 if (len > 0 && str[len - 1] != '\n') {
3582 if (str[len] == '\r') str[len] = '\n';
3585 gtk_text_buffer_insert(buffer, &iter, str, -1);
3589 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3590 G_CALLBACK(text_inserted),
3592 compose->autowrap = prev_autowrap;
3593 if (compose->autowrap)
3594 compose_wrap_all(compose);
3599 return COMPOSE_INSERT_INVALID_CHARACTER;
3601 return COMPOSE_INSERT_SUCCESS;
3604 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3605 const gchar *filename,
3606 const gchar *content_type,
3607 const gchar *charset)
3615 GtkListStore *store;
3617 gboolean has_binary = FALSE;
3619 if (!is_file_exist(file)) {
3620 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3621 gboolean result = FALSE;
3622 if (file_from_uri && is_file_exist(file_from_uri)) {
3623 result = compose_attach_append(
3624 compose, file_from_uri,
3625 filename, content_type,
3628 g_free(file_from_uri);
3631 alertpanel_error("File %s doesn't exist\n", filename);
3634 if ((size = get_file_size(file)) < 0) {
3635 alertpanel_error("Can't get file size of %s\n", filename);
3639 alertpanel_error(_("File %s is empty."), filename);
3642 if ((fp = g_fopen(file, "rb")) == NULL) {
3643 alertpanel_error(_("Can't read %s."), filename);
3648 ainfo = g_new0(AttachInfo, 1);
3649 auto_ainfo = g_auto_pointer_new_with_free
3650 (ainfo, (GFreeFunc) compose_attach_info_free);
3651 ainfo->file = g_strdup(file);
3654 ainfo->content_type = g_strdup(content_type);
3655 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3657 MsgFlags flags = {0, 0};
3659 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3660 ainfo->encoding = ENC_7BIT;
3662 ainfo->encoding = ENC_8BIT;
3664 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3665 if (msginfo && msginfo->subject)
3666 name = g_strdup(msginfo->subject);
3668 name = g_path_get_basename(filename ? filename : file);
3670 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3672 procmsg_msginfo_free(msginfo);
3674 if (!g_ascii_strncasecmp(content_type, "text/", 5)) {
3675 ainfo->charset = g_strdup(charset);
3676 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3678 ainfo->encoding = ENC_BASE64;
3680 name = g_path_get_basename(filename ? filename : file);
3681 ainfo->name = g_strdup(name);
3685 ainfo->content_type = procmime_get_mime_type(file);
3686 if (!ainfo->content_type) {
3687 ainfo->content_type =
3688 g_strdup("application/octet-stream");
3689 ainfo->encoding = ENC_BASE64;
3690 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text/", 5))
3692 procmime_get_encoding_for_text_file(file, &has_binary);
3694 ainfo->encoding = ENC_BASE64;
3695 name = g_path_get_basename(filename ? filename : file);
3696 ainfo->name = g_strdup(name);
3700 if (ainfo->name != NULL
3701 && !strcmp(ainfo->name, ".")) {
3702 g_free(ainfo->name);
3706 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3707 g_free(ainfo->content_type);
3708 ainfo->content_type = g_strdup("application/octet-stream");
3709 g_free(ainfo->charset);
3710 ainfo->charset = NULL;
3713 ainfo->size = (goffset)size;
3714 size_text = to_human_readable((goffset)size);
3716 store = GTK_LIST_STORE(gtk_tree_view_get_model
3717 (GTK_TREE_VIEW(compose->attach_clist)));
3719 gtk_list_store_append(store, &iter);
3720 gtk_list_store_set(store, &iter,
3721 COL_MIMETYPE, ainfo->content_type,
3722 COL_SIZE, size_text,
3723 COL_NAME, ainfo->name,
3724 COL_CHARSET, ainfo->charset,
3726 COL_AUTODATA, auto_ainfo,
3729 g_auto_pointer_free(auto_ainfo);
3730 compose_attach_update_label(compose);
3734 static void compose_use_signing(Compose *compose, gboolean use_signing)
3736 compose->use_signing = use_signing;
3737 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3740 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3742 compose->use_encryption = use_encryption;
3743 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3746 #define NEXT_PART_NOT_CHILD(info) \
3748 node = info->node; \
3749 while (node->children) \
3750 node = g_node_last_child(node); \
3751 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3754 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3758 MimeInfo *firsttext = NULL;
3759 MimeInfo *encrypted = NULL;
3762 const gchar *partname = NULL;
3764 mimeinfo = procmime_scan_message(msginfo);
3765 if (!mimeinfo) return;
3767 if (mimeinfo->node->children == NULL) {
3768 procmime_mimeinfo_free_all(mimeinfo);
3772 /* find first content part */
3773 child = (MimeInfo *) mimeinfo->node->children->data;
3774 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3775 child = (MimeInfo *)child->node->children->data;
3778 if (child->type == MIMETYPE_TEXT) {
3780 debug_print("First text part found\n");
3781 } else if (compose->mode == COMPOSE_REEDIT &&
3782 child->type == MIMETYPE_APPLICATION &&
3783 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3784 encrypted = (MimeInfo *)child->node->parent->data;
3787 child = (MimeInfo *) mimeinfo->node->children->data;
3788 while (child != NULL) {
3791 if (child == encrypted) {
3792 /* skip this part of tree */
3793 NEXT_PART_NOT_CHILD(child);
3797 if (child->type == MIMETYPE_MULTIPART) {
3798 /* get the actual content */
3799 child = procmime_mimeinfo_next(child);
3803 if (child == firsttext) {
3804 child = procmime_mimeinfo_next(child);
3808 outfile = procmime_get_tmp_file_name(child);
3809 if ((err = procmime_get_part(outfile, child)) < 0)
3810 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3812 gchar *content_type;
3814 content_type = procmime_get_content_type_str(child->type, child->subtype);
3816 /* if we meet a pgp signature, we don't attach it, but
3817 * we force signing. */
3818 if ((strcmp(content_type, "application/pgp-signature") &&
3819 strcmp(content_type, "application/pkcs7-signature") &&
3820 strcmp(content_type, "application/x-pkcs7-signature"))
3821 || compose->mode == COMPOSE_REDIRECT) {
3822 partname = procmime_mimeinfo_get_parameter(child, "filename");
3823 if (partname == NULL)
3824 partname = procmime_mimeinfo_get_parameter(child, "name");
3825 if (partname == NULL)
3827 compose_attach_append(compose, outfile,
3828 partname, content_type,
3829 procmime_mimeinfo_get_parameter(child, "charset"));
3831 compose_force_signing(compose, compose->account, NULL);
3833 g_free(content_type);
3836 NEXT_PART_NOT_CHILD(child);
3838 procmime_mimeinfo_free_all(mimeinfo);
3841 #undef NEXT_PART_NOT_CHILD
3846 WAIT_FOR_INDENT_CHAR,
3847 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3850 /* return indent length, we allow:
3851 indent characters followed by indent characters or spaces/tabs,
3852 alphabets and numbers immediately followed by indent characters,
3853 and the repeating sequences of the above
3854 If quote ends with multiple spaces, only the first one is included. */
3855 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3856 const GtkTextIter *start, gint *len)
3858 GtkTextIter iter = *start;
3862 IndentState state = WAIT_FOR_INDENT_CHAR;
3865 gint alnum_count = 0;
3866 gint space_count = 0;
3869 if (prefs_common.quote_chars == NULL) {
3873 while (!gtk_text_iter_ends_line(&iter)) {
3874 wc = gtk_text_iter_get_char(&iter);
3875 if (g_unichar_iswide(wc))
3877 clen = g_unichar_to_utf8(wc, ch);
3881 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3882 is_space = g_unichar_isspace(wc);
3884 if (state == WAIT_FOR_INDENT_CHAR) {
3885 if (!is_indent && !g_unichar_isalnum(wc))
3888 quote_len += alnum_count + space_count + 1;
3889 alnum_count = space_count = 0;
3890 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3893 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3894 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3898 else if (is_indent) {
3899 quote_len += alnum_count + space_count + 1;
3900 alnum_count = space_count = 0;
3903 state = WAIT_FOR_INDENT_CHAR;
3907 gtk_text_iter_forward_char(&iter);
3910 if (quote_len > 0 && space_count > 0)
3916 if (quote_len > 0) {
3918 gtk_text_iter_forward_chars(&iter, quote_len);
3919 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3925 /* return >0 if the line is itemized */
3926 static int compose_itemized_length(GtkTextBuffer *buffer,
3927 const GtkTextIter *start)
3929 GtkTextIter iter = *start;
3934 if (gtk_text_iter_ends_line(&iter))
3939 wc = gtk_text_iter_get_char(&iter);
3940 if (!g_unichar_isspace(wc))
3942 gtk_text_iter_forward_char(&iter);
3943 if (gtk_text_iter_ends_line(&iter))
3947 clen = g_unichar_to_utf8(wc, ch);
3951 if (!strchr("*-+", ch[0]))
3954 gtk_text_iter_forward_char(&iter);
3955 if (gtk_text_iter_ends_line(&iter))
3957 wc = gtk_text_iter_get_char(&iter);
3958 if (g_unichar_isspace(wc)) {
3964 /* return the string at the start of the itemization */
3965 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3966 const GtkTextIter *start)
3968 GtkTextIter iter = *start;
3971 GString *item_chars = g_string_new("");
3974 if (gtk_text_iter_ends_line(&iter))
3979 wc = gtk_text_iter_get_char(&iter);
3980 if (!g_unichar_isspace(wc))
3982 gtk_text_iter_forward_char(&iter);
3983 if (gtk_text_iter_ends_line(&iter))
3985 g_string_append_unichar(item_chars, wc);
3988 str = item_chars->str;
3989 g_string_free(item_chars, FALSE);
3993 /* return the number of spaces at a line's start */
3994 static int compose_left_offset_length(GtkTextBuffer *buffer,
3995 const GtkTextIter *start)
3997 GtkTextIter iter = *start;
4000 if (gtk_text_iter_ends_line(&iter))
4004 wc = gtk_text_iter_get_char(&iter);
4005 if (!g_unichar_isspace(wc))
4008 gtk_text_iter_forward_char(&iter);
4009 if (gtk_text_iter_ends_line(&iter))
4013 gtk_text_iter_forward_char(&iter);
4014 if (gtk_text_iter_ends_line(&iter))
4019 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
4020 const GtkTextIter *start,
4021 GtkTextIter *break_pos,
4025 GtkTextIter iter = *start, line_end = *start;
4026 PangoLogAttr *attrs;
4033 gboolean can_break = FALSE;
4034 gboolean do_break = FALSE;
4035 gboolean was_white = FALSE;
4036 gboolean prev_dont_break = FALSE;
4038 gtk_text_iter_forward_to_line_end(&line_end);
4039 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
4040 len = g_utf8_strlen(str, -1);
4044 g_warning("compose_get_line_break_pos: len = 0!\n");
4048 /* g_print("breaking line: %d: %s (len = %d)\n",
4049 gtk_text_iter_get_line(&iter), str, len); */
4051 attrs = g_new(PangoLogAttr, len + 1);
4053 pango_default_break(str, -1, NULL, attrs, len + 1);
4057 /* skip quote and leading spaces */
4058 for (i = 0; *p != '\0' && i < len; i++) {
4061 wc = g_utf8_get_char(p);
4062 if (i >= quote_len && !g_unichar_isspace(wc))
4064 if (g_unichar_iswide(wc))
4066 else if (*p == '\t')
4070 p = g_utf8_next_char(p);
4073 for (; *p != '\0' && i < len; i++) {
4074 PangoLogAttr *attr = attrs + i;
4078 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
4081 was_white = attr->is_white;
4083 /* don't wrap URI */
4084 if ((uri_len = get_uri_len(p)) > 0) {
4086 if (pos > 0 && col > max_col) {
4096 wc = g_utf8_get_char(p);
4097 if (g_unichar_iswide(wc)) {
4099 if (prev_dont_break && can_break && attr->is_line_break)
4101 } else if (*p == '\t')
4105 if (pos > 0 && col > max_col) {
4110 if (*p == '-' || *p == '/')
4111 prev_dont_break = TRUE;
4113 prev_dont_break = FALSE;
4115 p = g_utf8_next_char(p);
4119 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4124 *break_pos = *start;
4125 gtk_text_iter_set_line_offset(break_pos, pos);
4130 static gboolean compose_join_next_line(Compose *compose,
4131 GtkTextBuffer *buffer,
4133 const gchar *quote_str)
4135 GtkTextIter iter_ = *iter, cur, prev, next, end;
4136 PangoLogAttr attrs[3];
4138 gchar *next_quote_str;
4141 gboolean keep_cursor = FALSE;
4143 if (!gtk_text_iter_forward_line(&iter_) ||
4144 gtk_text_iter_ends_line(&iter_)) {
4147 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4149 if ((quote_str || next_quote_str) &&
4150 strcmp2(quote_str, next_quote_str) != 0) {
4151 g_free(next_quote_str);
4154 g_free(next_quote_str);
4157 if (quote_len > 0) {
4158 gtk_text_iter_forward_chars(&end, quote_len);
4159 if (gtk_text_iter_ends_line(&end)) {
4164 /* don't join itemized lines */
4165 if (compose_itemized_length(buffer, &end) > 0) {
4169 /* don't join signature separator */
4170 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4173 /* delete quote str */
4175 gtk_text_buffer_delete(buffer, &iter_, &end);
4177 /* don't join line breaks put by the user */
4179 gtk_text_iter_backward_char(&cur);
4180 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4181 gtk_text_iter_forward_char(&cur);
4185 gtk_text_iter_forward_char(&cur);
4186 /* delete linebreak and extra spaces */
4187 while (gtk_text_iter_backward_char(&cur)) {
4188 wc1 = gtk_text_iter_get_char(&cur);
4189 if (!g_unichar_isspace(wc1))
4194 while (!gtk_text_iter_ends_line(&cur)) {
4195 wc1 = gtk_text_iter_get_char(&cur);
4196 if (!g_unichar_isspace(wc1))
4198 gtk_text_iter_forward_char(&cur);
4201 if (!gtk_text_iter_equal(&prev, &next)) {
4204 mark = gtk_text_buffer_get_insert(buffer);
4205 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4206 if (gtk_text_iter_equal(&prev, &cur))
4208 gtk_text_buffer_delete(buffer, &prev, &next);
4212 /* insert space if required */
4213 gtk_text_iter_backward_char(&prev);
4214 wc1 = gtk_text_iter_get_char(&prev);
4215 wc2 = gtk_text_iter_get_char(&next);
4216 gtk_text_iter_forward_char(&next);
4217 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4218 pango_default_break(str, -1, NULL, attrs, 3);
4219 if (!attrs[1].is_line_break ||
4220 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4221 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4223 gtk_text_iter_backward_char(&iter_);
4224 gtk_text_buffer_place_cursor(buffer, &iter_);
4233 #define ADD_TXT_POS(bp_, ep_, pti_) \
4234 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4235 last = last->next; \
4236 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4237 last->next = NULL; \
4239 g_warning("alloc error scanning URIs\n"); \
4242 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4244 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4245 GtkTextBuffer *buffer;
4246 GtkTextIter iter, break_pos, end_of_line;
4247 gchar *quote_str = NULL;
4249 gboolean wrap_quote = prefs_common.linewrap_quote;
4250 gboolean prev_autowrap = compose->autowrap;
4251 gint startq_offset = -1, noq_offset = -1;
4252 gint uri_start = -1, uri_stop = -1;
4253 gint nouri_start = -1, nouri_stop = -1;
4254 gint num_blocks = 0;
4255 gint quotelevel = -1;
4256 gboolean modified = force;
4257 gboolean removed = FALSE;
4258 gboolean modified_before_remove = FALSE;
4260 gboolean start = TRUE;
4261 gint itemized_len = 0, rem_item_len = 0;
4262 gchar *itemized_chars = NULL;
4263 gboolean item_continuation = FALSE;
4268 if (compose->draft_timeout_tag == -2) {
4272 compose->autowrap = FALSE;
4274 buffer = gtk_text_view_get_buffer(text);
4275 undo_wrapping(compose->undostruct, TRUE);
4280 mark = gtk_text_buffer_get_insert(buffer);
4281 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4285 if (compose->draft_timeout_tag == -2) {
4286 if (gtk_text_iter_ends_line(&iter)) {
4287 while (gtk_text_iter_ends_line(&iter) &&
4288 gtk_text_iter_forward_line(&iter))
4291 while (gtk_text_iter_backward_line(&iter)) {
4292 if (gtk_text_iter_ends_line(&iter)) {
4293 gtk_text_iter_forward_line(&iter);
4299 /* move to line start */
4300 gtk_text_iter_set_line_offset(&iter, 0);
4303 itemized_len = compose_itemized_length(buffer, &iter);
4305 if (!itemized_len) {
4306 itemized_len = compose_left_offset_length(buffer, &iter);
4307 item_continuation = TRUE;
4311 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4313 /* go until paragraph end (empty line) */
4314 while (start || !gtk_text_iter_ends_line(&iter)) {
4315 gchar *scanpos = NULL;
4316 /* parse table - in order of priority */
4318 const gchar *needle; /* token */
4320 /* token search function */
4321 gchar *(*search) (const gchar *haystack,
4322 const gchar *needle);
4323 /* part parsing function */
4324 gboolean (*parse) (const gchar *start,
4325 const gchar *scanpos,
4329 /* part to URI function */
4330 gchar *(*build_uri) (const gchar *bp,
4334 static struct table parser[] = {
4335 {"http://", strcasestr, get_uri_part, make_uri_string},
4336 {"https://", strcasestr, get_uri_part, make_uri_string},
4337 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4338 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4339 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4340 {"www.", strcasestr, get_uri_part, make_http_string},
4341 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4342 {"@", strcasestr, get_email_part, make_email_string}
4344 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4345 gint last_index = PARSE_ELEMS;
4347 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4351 if (!prev_autowrap && num_blocks == 0) {
4353 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4354 G_CALLBACK(text_inserted),
4357 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4360 uri_start = uri_stop = -1;
4362 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4365 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4366 if (startq_offset == -1)
4367 startq_offset = gtk_text_iter_get_offset(&iter);
4368 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4369 if (quotelevel > 2) {
4370 /* recycle colors */
4371 if (prefs_common.recycle_quote_colors)
4380 if (startq_offset == -1)
4381 noq_offset = gtk_text_iter_get_offset(&iter);
4385 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4388 if (gtk_text_iter_ends_line(&iter)) {
4390 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4391 prefs_common.linewrap_len,
4393 GtkTextIter prev, next, cur;
4394 if (prev_autowrap != FALSE || force) {
4395 compose->automatic_break = TRUE;
4397 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4398 compose->automatic_break = FALSE;
4399 if (itemized_len && compose->autoindent) {
4400 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4401 if (!item_continuation)
4402 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4404 } else if (quote_str && wrap_quote) {
4405 compose->automatic_break = TRUE;
4407 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4408 compose->automatic_break = FALSE;
4409 if (itemized_len && compose->autoindent) {
4410 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4411 if (!item_continuation)
4412 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4416 /* remove trailing spaces */
4418 rem_item_len = itemized_len;
4419 while (compose->autoindent && rem_item_len-- > 0)
4420 gtk_text_iter_backward_char(&cur);
4421 gtk_text_iter_backward_char(&cur);
4424 while (!gtk_text_iter_starts_line(&cur)) {
4427 gtk_text_iter_backward_char(&cur);
4428 wc = gtk_text_iter_get_char(&cur);
4429 if (!g_unichar_isspace(wc))
4433 if (!gtk_text_iter_equal(&prev, &next)) {
4434 gtk_text_buffer_delete(buffer, &prev, &next);
4436 gtk_text_iter_forward_char(&break_pos);
4440 gtk_text_buffer_insert(buffer, &break_pos,
4444 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4446 /* move iter to current line start */
4447 gtk_text_iter_set_line_offset(&iter, 0);
4454 /* move iter to next line start */
4460 if (!prev_autowrap && num_blocks > 0) {
4462 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4463 G_CALLBACK(text_inserted),
4467 while (!gtk_text_iter_ends_line(&end_of_line)) {
4468 gtk_text_iter_forward_char(&end_of_line);
4470 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4472 nouri_start = gtk_text_iter_get_offset(&iter);
4473 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4475 walk_pos = gtk_text_iter_get_offset(&iter);
4476 /* FIXME: this looks phony. scanning for anything in the parse table */
4477 for (n = 0; n < PARSE_ELEMS; n++) {
4480 tmp = parser[n].search(walk, parser[n].needle);
4482 if (scanpos == NULL || tmp < scanpos) {
4491 /* check if URI can be parsed */
4492 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4493 (const gchar **)&ep, FALSE)
4494 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4498 strlen(parser[last_index].needle);
4501 uri_start = walk_pos + (bp - o_walk);
4502 uri_stop = walk_pos + (ep - o_walk);
4506 gtk_text_iter_forward_line(&iter);
4509 if (startq_offset != -1) {
4510 GtkTextIter startquote, endquote;
4511 gtk_text_buffer_get_iter_at_offset(
4512 buffer, &startquote, startq_offset);
4515 switch (quotelevel) {
4517 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4518 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4519 gtk_text_buffer_apply_tag_by_name(
4520 buffer, "quote0", &startquote, &endquote);
4521 gtk_text_buffer_remove_tag_by_name(
4522 buffer, "quote1", &startquote, &endquote);
4523 gtk_text_buffer_remove_tag_by_name(
4524 buffer, "quote2", &startquote, &endquote);
4529 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4530 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4531 gtk_text_buffer_apply_tag_by_name(
4532 buffer, "quote1", &startquote, &endquote);
4533 gtk_text_buffer_remove_tag_by_name(
4534 buffer, "quote0", &startquote, &endquote);
4535 gtk_text_buffer_remove_tag_by_name(
4536 buffer, "quote2", &startquote, &endquote);
4541 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4542 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4543 gtk_text_buffer_apply_tag_by_name(
4544 buffer, "quote2", &startquote, &endquote);
4545 gtk_text_buffer_remove_tag_by_name(
4546 buffer, "quote0", &startquote, &endquote);
4547 gtk_text_buffer_remove_tag_by_name(
4548 buffer, "quote1", &startquote, &endquote);
4554 } else if (noq_offset != -1) {
4555 GtkTextIter startnoquote, endnoquote;
4556 gtk_text_buffer_get_iter_at_offset(
4557 buffer, &startnoquote, noq_offset);
4560 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4561 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4562 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4563 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4564 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4565 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4566 gtk_text_buffer_remove_tag_by_name(
4567 buffer, "quote0", &startnoquote, &endnoquote);
4568 gtk_text_buffer_remove_tag_by_name(
4569 buffer, "quote1", &startnoquote, &endnoquote);
4570 gtk_text_buffer_remove_tag_by_name(
4571 buffer, "quote2", &startnoquote, &endnoquote);
4577 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4578 GtkTextIter nouri_start_iter, nouri_end_iter;
4579 gtk_text_buffer_get_iter_at_offset(
4580 buffer, &nouri_start_iter, nouri_start);
4581 gtk_text_buffer_get_iter_at_offset(
4582 buffer, &nouri_end_iter, nouri_stop);
4583 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4584 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4585 gtk_text_buffer_remove_tag_by_name(
4586 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4587 modified_before_remove = modified;
4592 if (uri_start >= 0 && uri_stop > 0) {
4593 GtkTextIter uri_start_iter, uri_end_iter, back;
4594 gtk_text_buffer_get_iter_at_offset(
4595 buffer, &uri_start_iter, uri_start);
4596 gtk_text_buffer_get_iter_at_offset(
4597 buffer, &uri_end_iter, uri_stop);
4598 back = uri_end_iter;
4599 gtk_text_iter_backward_char(&back);
4600 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4601 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4602 gtk_text_buffer_apply_tag_by_name(
4603 buffer, "link", &uri_start_iter, &uri_end_iter);
4605 if (removed && !modified_before_remove) {
4611 // debug_print("not modified, out after %d lines\n", lines);
4615 // debug_print("modified, out after %d lines\n", lines);
4617 g_free(itemized_chars);
4620 undo_wrapping(compose->undostruct, FALSE);
4621 compose->autowrap = prev_autowrap;
4626 void compose_action_cb(void *data)
4628 Compose *compose = (Compose *)data;
4629 compose_wrap_all(compose);
4632 static void compose_wrap_all(Compose *compose)
4634 compose_wrap_all_full(compose, FALSE);
4637 static void compose_wrap_all_full(Compose *compose, gboolean force)
4639 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4640 GtkTextBuffer *buffer;
4642 gboolean modified = TRUE;
4644 buffer = gtk_text_view_get_buffer(text);
4646 gtk_text_buffer_get_start_iter(buffer, &iter);
4647 while (!gtk_text_iter_is_end(&iter) && modified)
4648 modified = compose_beautify_paragraph(compose, &iter, force);
4652 static void compose_set_title(Compose *compose)
4658 edited = compose->modified ? _(" [Edited]") : "";
4660 subject = gtk_editable_get_chars(
4661 GTK_EDITABLE(compose->subject_entry), 0, -1);
4663 #ifndef GENERIC_UMPC
4664 if (subject && strlen(subject))
4665 str = g_strdup_printf(_("%s - Compose message%s"),
4668 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4670 str = g_strdup(_("Compose message"));
4673 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4679 * compose_current_mail_account:
4681 * Find a current mail account (the currently selected account, or the
4682 * default account, if a news account is currently selected). If a
4683 * mail account cannot be found, display an error message.
4685 * Return value: Mail account, or NULL if not found.
4687 static PrefsAccount *
4688 compose_current_mail_account(void)
4692 if (cur_account && cur_account->protocol != A_NNTP)
4695 ac = account_get_default();
4696 if (!ac || ac->protocol == A_NNTP) {
4697 alertpanel_error(_("Account for sending mail is not specified.\n"
4698 "Please select a mail account before sending."));
4705 #define QUOTE_IF_REQUIRED(out, str) \
4707 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4711 len = strlen(str) + 3; \
4712 if ((__tmp = alloca(len)) == NULL) { \
4713 g_warning("can't allocate memory\n"); \
4714 g_string_free(header, TRUE); \
4717 g_snprintf(__tmp, len, "\"%s\"", str); \
4722 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4723 g_warning("can't allocate memory\n"); \
4724 g_string_free(header, TRUE); \
4727 strcpy(__tmp, str); \
4733 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4735 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4739 len = strlen(str) + 3; \
4740 if ((__tmp = alloca(len)) == NULL) { \
4741 g_warning("can't allocate memory\n"); \
4744 g_snprintf(__tmp, len, "\"%s\"", str); \
4749 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4750 g_warning("can't allocate memory\n"); \
4753 strcpy(__tmp, str); \
4759 static void compose_select_account(Compose *compose, PrefsAccount *account,
4762 gchar *from = NULL, *header;
4763 ComposeHeaderEntry *header_entry;
4765 cm_return_if_fail(account != NULL);
4767 compose->account = account;
4768 if (account->name && *account->name) {
4770 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4771 from = g_strdup_printf("%s <%s>",
4772 buf, account->address);
4773 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4775 from = g_strdup_printf("<%s>",
4777 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4782 compose_set_title(compose);
4784 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4785 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4787 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4788 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4789 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4791 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4793 activate_privacy_system(compose, account, FALSE);
4795 if (!init && compose->mode != COMPOSE_REDIRECT) {
4796 undo_block(compose->undostruct);
4797 compose_insert_sig(compose, TRUE);
4798 undo_unblock(compose->undostruct);
4801 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4802 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4804 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4805 if (account->protocol == A_NNTP) {
4806 if (!strcmp(header, _("To:")))
4807 combobox_select_by_text(
4808 GTK_COMBO_BOX(header_entry->combo),
4811 if (!strcmp(header, _("Newsgroups:")))
4812 combobox_select_by_text(
4813 GTK_COMBO_BOX(header_entry->combo),
4821 /* use account's dict info if set */
4822 if (compose->gtkaspell) {
4823 if (account->enable_default_dictionary)
4824 gtkaspell_change_dict(compose->gtkaspell,
4825 account->default_dictionary, FALSE);
4826 if (account->enable_default_alt_dictionary)
4827 gtkaspell_change_alt_dict(compose->gtkaspell,
4828 account->default_alt_dictionary);
4829 if (account->enable_default_dictionary
4830 || account->enable_default_alt_dictionary)
4831 compose_spell_menu_changed(compose);
4836 gboolean compose_check_for_valid_recipient(Compose *compose) {
4837 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4838 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4839 gboolean recipient_found = FALSE;
4843 /* free to and newsgroup list */
4844 slist_free_strings(compose->to_list);
4845 g_slist_free(compose->to_list);
4846 compose->to_list = NULL;
4848 slist_free_strings(compose->newsgroup_list);
4849 g_slist_free(compose->newsgroup_list);
4850 compose->newsgroup_list = NULL;
4852 /* search header entries for to and newsgroup entries */
4853 for (list = compose->header_list; list; list = list->next) {
4856 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4857 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4860 if (entry[0] != '\0') {
4861 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4862 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4863 compose->to_list = address_list_append(compose->to_list, entry);
4864 recipient_found = TRUE;
4867 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4868 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4869 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4870 recipient_found = TRUE;
4877 return recipient_found;
4880 static gboolean compose_check_for_set_recipients(Compose *compose)
4882 if (compose->account->set_autocc && compose->account->auto_cc) {
4883 gboolean found_other = FALSE;
4885 /* search header entries for to and newsgroup entries */
4886 for (list = compose->header_list; list; list = list->next) {
4889 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4890 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4893 if (strcmp(entry, compose->account->auto_cc)
4894 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4904 if (compose->batch) {
4905 gtk_widget_show_all(compose->window);
4907 aval = alertpanel(_("Send"),
4908 _("The only recipient is the default CC address. Send anyway?"),
4909 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4910 if (aval != G_ALERTALTERNATE)
4914 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4915 gboolean found_other = FALSE;
4917 /* search header entries for to and newsgroup entries */
4918 for (list = compose->header_list; list; list = list->next) {
4921 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4922 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4925 if (strcmp(entry, compose->account->auto_bcc)
4926 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4936 if (compose->batch) {
4937 gtk_widget_show_all(compose->window);
4939 aval = alertpanel(_("Send"),
4940 _("The only recipient is the default BCC address. Send anyway?"),
4941 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4942 if (aval != G_ALERTALTERNATE)
4949 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4953 if (compose_check_for_valid_recipient(compose) == FALSE) {
4954 if (compose->batch) {
4955 gtk_widget_show_all(compose->window);
4957 alertpanel_error(_("Recipient is not specified."));
4961 if (compose_check_for_set_recipients(compose) == FALSE) {
4965 if (!compose->batch) {
4966 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4967 if (*str == '\0' && check_everything == TRUE &&
4968 compose->mode != COMPOSE_REDIRECT) {
4970 gchar *button_label;
4973 if (compose->sending)
4974 button_label = _("+_Send");
4976 button_label = _("+_Queue");
4977 message = g_strdup_printf(_("Subject is empty. %s"),
4978 compose->sending?_("Send it anyway?"):
4979 _("Queue it anyway?"));
4981 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4982 GTK_STOCK_CANCEL, button_label, NULL);
4984 if (aval != G_ALERTALTERNATE)
4989 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4995 gint compose_send(Compose *compose)
4998 FolderItem *folder = NULL;
5000 gchar *msgpath = NULL;
5001 gboolean discard_window = FALSE;
5002 gchar *errstr = NULL;
5003 gchar *tmsgid = NULL;
5004 MainWindow *mainwin = mainwindow_get_mainwindow();
5005 gboolean queued_removed = FALSE;
5007 if (prefs_common.send_dialog_invisible
5008 || compose->batch == TRUE)
5009 discard_window = TRUE;
5011 compose_allow_user_actions (compose, FALSE);
5012 compose->sending = TRUE;
5014 if (compose_check_entries(compose, TRUE) == FALSE) {
5015 if (compose->batch) {
5016 gtk_widget_show_all(compose->window);
5022 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
5025 if (compose->batch) {
5026 gtk_widget_show_all(compose->window);
5029 alertpanel_error(_("Could not queue message for sending:\n\n"
5030 "Charset conversion failed."));
5031 } else if (val == -5) {
5032 alertpanel_error(_("Could not queue message for sending:\n\n"
5033 "Couldn't get recipient encryption key."));
5034 } else if (val == -6) {
5036 } else if (val == -3) {
5037 if (privacy_peek_error())
5038 alertpanel_error(_("Could not queue message for sending:\n\n"
5039 "Signature failed: %s"), privacy_get_error());
5040 } else if (val == -2 && errno != 0) {
5041 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
5043 alertpanel_error(_("Could not queue message for sending."));
5048 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
5049 if (discard_window) {
5050 compose->sending = FALSE;
5051 compose_close(compose);
5052 /* No more compose access in the normal codepath
5053 * after this point! */
5058 alertpanel_error(_("The message was queued but could not be "
5059 "sent.\nUse \"Send queued messages\" from "
5060 "the main window to retry."));
5061 if (!discard_window) {
5068 if (msgpath == NULL) {
5069 msgpath = folder_item_fetch_msg(folder, msgnum);
5070 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5073 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5074 claws_unlink(msgpath);
5077 if (!discard_window) {
5079 if (!queued_removed)
5080 folder_item_remove_msg(folder, msgnum);
5081 folder_item_scan(folder);
5083 /* make sure we delete that */
5084 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5086 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5087 folder_item_remove_msg(folder, tmp->msgnum);
5088 procmsg_msginfo_free(tmp);
5095 if (!queued_removed)
5096 folder_item_remove_msg(folder, msgnum);
5097 folder_item_scan(folder);
5099 /* make sure we delete that */
5100 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5102 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5103 folder_item_remove_msg(folder, tmp->msgnum);
5104 procmsg_msginfo_free(tmp);
5107 if (!discard_window) {
5108 compose->sending = FALSE;
5109 compose_allow_user_actions (compose, TRUE);
5110 compose_close(compose);
5114 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5115 "the main window to retry."), errstr);
5118 alertpanel_error_log(_("The message was queued but could not be "
5119 "sent.\nUse \"Send queued messages\" from "
5120 "the main window to retry."));
5122 if (!discard_window) {
5131 toolbar_main_set_sensitive(mainwin);
5132 main_window_set_menu_sensitive(mainwin);
5138 compose_allow_user_actions (compose, TRUE);
5139 compose->sending = FALSE;
5140 compose->modified = TRUE;
5141 toolbar_main_set_sensitive(mainwin);
5142 main_window_set_menu_sensitive(mainwin);
5147 static gboolean compose_use_attach(Compose *compose)
5149 GtkTreeModel *model = gtk_tree_view_get_model
5150 (GTK_TREE_VIEW(compose->attach_clist));
5151 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5154 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5157 gchar buf[BUFFSIZE];
5159 gboolean first_to_address;
5160 gboolean first_cc_address;
5162 ComposeHeaderEntry *headerentry;
5163 const gchar *headerentryname;
5164 const gchar *cc_hdr;
5165 const gchar *to_hdr;
5166 gboolean err = FALSE;
5168 debug_print("Writing redirect header\n");
5170 cc_hdr = prefs_common_translated_header_name("Cc:");
5171 to_hdr = prefs_common_translated_header_name("To:");
5173 first_to_address = TRUE;
5174 for (list = compose->header_list; list; list = list->next) {
5175 headerentry = ((ComposeHeaderEntry *)list->data);
5176 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5178 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5179 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5180 Xstrdup_a(str, entstr, return -1);
5182 if (str[0] != '\0') {
5183 compose_convert_header
5184 (compose, buf, sizeof(buf), str,
5185 strlen("Resent-To") + 2, TRUE);
5187 if (first_to_address) {
5188 err |= (fprintf(fp, "Resent-To: ") < 0);
5189 first_to_address = FALSE;
5191 err |= (fprintf(fp, ",") < 0);
5193 err |= (fprintf(fp, "%s", buf) < 0);
5197 if (!first_to_address) {
5198 err |= (fprintf(fp, "\n") < 0);
5201 first_cc_address = TRUE;
5202 for (list = compose->header_list; list; list = list->next) {
5203 headerentry = ((ComposeHeaderEntry *)list->data);
5204 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5206 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5207 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5208 Xstrdup_a(str, strg, return -1);
5210 if (str[0] != '\0') {
5211 compose_convert_header
5212 (compose, buf, sizeof(buf), str,
5213 strlen("Resent-Cc") + 2, TRUE);
5215 if (first_cc_address) {
5216 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5217 first_cc_address = FALSE;
5219 err |= (fprintf(fp, ",") < 0);
5221 err |= (fprintf(fp, "%s", buf) < 0);
5225 if (!first_cc_address) {
5226 err |= (fprintf(fp, "\n") < 0);
5229 return (err ? -1:0);
5232 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5234 gchar buf[BUFFSIZE];
5236 const gchar *entstr;
5237 /* struct utsname utsbuf; */
5238 gboolean err = FALSE;
5240 cm_return_val_if_fail(fp != NULL, -1);
5241 cm_return_val_if_fail(compose->account != NULL, -1);
5242 cm_return_val_if_fail(compose->account->address != NULL, -1);
5245 get_rfc822_date(buf, sizeof(buf));
5246 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5249 if (compose->account->name && *compose->account->name) {
5250 compose_convert_header
5251 (compose, buf, sizeof(buf), compose->account->name,
5252 strlen("From: "), TRUE);
5253 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5254 buf, compose->account->address) < 0);
5256 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5259 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5260 if (*entstr != '\0') {
5261 Xstrdup_a(str, entstr, return -1);
5264 compose_convert_header(compose, buf, sizeof(buf), str,
5265 strlen("Subject: "), FALSE);
5266 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5270 /* Resent-Message-ID */
5271 if (compose->account->set_domain && compose->account->domain) {
5272 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5273 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5274 g_snprintf(buf, sizeof(buf), "%s",
5275 strchr(compose->account->address, '@') ?
5276 strchr(compose->account->address, '@')+1 :
5277 compose->account->address);
5279 g_snprintf(buf, sizeof(buf), "%s", "");
5282 if (compose->account->gen_msgid) {
5284 if (compose->account->msgid_with_addr) {
5285 addr = compose->account->address;
5287 generate_msgid(buf, sizeof(buf), addr);
5288 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5289 compose->msgid = g_strdup(buf);
5291 compose->msgid = NULL;
5294 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5297 /* separator between header and body */
5298 err |= (fputs("\n", fp) == EOF);
5300 return (err ? -1:0);
5303 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5307 gchar buf[BUFFSIZE];
5309 gboolean skip = FALSE;
5310 gboolean err = FALSE;
5311 gchar *not_included[]={
5312 "Return-Path:", "Delivered-To:", "Received:",
5313 "Subject:", "X-UIDL:", "AF:",
5314 "NF:", "PS:", "SRH:",
5315 "SFN:", "DSR:", "MID:",
5316 "CFG:", "PT:", "S:",
5317 "RQ:", "SSV:", "NSV:",
5318 "SSH:", "R:", "MAID:",
5319 "NAID:", "RMID:", "FMID:",
5320 "SCF:", "RRCPT:", "NG:",
5321 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5322 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5323 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5324 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5325 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5328 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5329 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5333 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5335 for (i = 0; not_included[i] != NULL; i++) {
5336 if (g_ascii_strncasecmp(buf, not_included[i],
5337 strlen(not_included[i])) == 0) {
5344 if (fputs(buf, fdest) == -1)
5347 if (!prefs_common.redirect_keep_from) {
5348 if (g_ascii_strncasecmp(buf, "From:",
5349 strlen("From:")) == 0) {
5350 err |= (fputs(" (by way of ", fdest) == EOF);
5351 if (compose->account->name
5352 && *compose->account->name) {
5353 compose_convert_header
5354 (compose, buf, sizeof(buf),
5355 compose->account->name,
5358 err |= (fprintf(fdest, "%s <%s>",
5360 compose->account->address) < 0);
5362 err |= (fprintf(fdest, "%s",
5363 compose->account->address) < 0);
5364 err |= (fputs(")", fdest) == EOF);
5368 if (fputs("\n", fdest) == -1)
5375 if (compose_redirect_write_headers(compose, fdest))
5378 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5379 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5392 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5394 GtkTextBuffer *buffer;
5395 GtkTextIter start, end;
5398 const gchar *out_codeset;
5399 EncodingType encoding = ENC_UNKNOWN;
5400 MimeInfo *mimemsg, *mimetext;
5402 const gchar *src_codeset = CS_INTERNAL;
5403 gchar *from_addr = NULL;
5404 gchar *from_name = NULL;
5406 if (action == COMPOSE_WRITE_FOR_SEND)
5407 attach_parts = TRUE;
5409 /* create message MimeInfo */
5410 mimemsg = procmime_mimeinfo_new();
5411 mimemsg->type = MIMETYPE_MESSAGE;
5412 mimemsg->subtype = g_strdup("rfc822");
5413 mimemsg->content = MIMECONTENT_MEM;
5414 mimemsg->tmp = TRUE; /* must free content later */
5415 mimemsg->data.mem = compose_get_header(compose);
5417 /* Create text part MimeInfo */
5418 /* get all composed text */
5419 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5420 gtk_text_buffer_get_start_iter(buffer, &start);
5421 gtk_text_buffer_get_end_iter(buffer, &end);
5422 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5424 out_codeset = conv_get_charset_str(compose->out_encoding);
5426 if (!out_codeset && is_ascii_str(chars)) {
5427 out_codeset = CS_US_ASCII;
5428 } else if (prefs_common.outgoing_fallback_to_ascii &&
5429 is_ascii_str(chars)) {
5430 out_codeset = CS_US_ASCII;
5431 encoding = ENC_7BIT;
5435 gchar *test_conv_global_out = NULL;
5436 gchar *test_conv_reply = NULL;
5438 /* automatic mode. be automatic. */
5439 codeconv_set_strict(TRUE);
5441 out_codeset = conv_get_outgoing_charset_str();
5443 debug_print("trying to convert to %s\n", out_codeset);
5444 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5447 if (!test_conv_global_out && compose->orig_charset
5448 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5449 out_codeset = compose->orig_charset;
5450 debug_print("failure; trying to convert to %s\n", out_codeset);
5451 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5454 if (!test_conv_global_out && !test_conv_reply) {
5456 out_codeset = CS_INTERNAL;
5457 debug_print("failure; finally using %s\n", out_codeset);
5459 g_free(test_conv_global_out);
5460 g_free(test_conv_reply);
5461 codeconv_set_strict(FALSE);
5464 if (encoding == ENC_UNKNOWN) {
5465 if (prefs_common.encoding_method == CTE_BASE64)
5466 encoding = ENC_BASE64;
5467 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5468 encoding = ENC_QUOTED_PRINTABLE;
5469 else if (prefs_common.encoding_method == CTE_8BIT)
5470 encoding = ENC_8BIT;
5472 encoding = procmime_get_encoding_for_charset(out_codeset);
5475 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5476 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5478 if (action == COMPOSE_WRITE_FOR_SEND) {
5479 codeconv_set_strict(TRUE);
5480 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5481 codeconv_set_strict(FALSE);
5487 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5488 "to the specified %s charset.\n"
5489 "Send it as %s?"), out_codeset, src_codeset);
5490 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5491 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5494 if (aval != G_ALERTALTERNATE) {
5499 out_codeset = src_codeset;
5505 out_codeset = src_codeset;
5510 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5511 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5512 strstr(buf, "\nFrom ") != NULL) {
5513 encoding = ENC_QUOTED_PRINTABLE;
5517 mimetext = procmime_mimeinfo_new();
5518 mimetext->content = MIMECONTENT_MEM;
5519 mimetext->tmp = TRUE; /* must free content later */
5520 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5521 * and free the data, which we need later. */
5522 mimetext->data.mem = g_strdup(buf);
5523 mimetext->type = MIMETYPE_TEXT;
5524 mimetext->subtype = g_strdup("plain");
5525 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5526 g_strdup(out_codeset));
5528 /* protect trailing spaces when signing message */
5529 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5530 privacy_system_can_sign(compose->privacy_system)) {
5531 encoding = ENC_QUOTED_PRINTABLE;
5534 debug_print("main text: %zd bytes encoded as %s in %d\n",
5535 strlen(buf), out_codeset, encoding);
5537 /* check for line length limit */
5538 if (action == COMPOSE_WRITE_FOR_SEND &&
5539 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5540 check_line_length(buf, 1000, &line) < 0) {
5544 msg = g_strdup_printf
5545 (_("Line %d exceeds the line length limit (998 bytes).\n"
5546 "The contents of the message might be broken on the way to the delivery.\n"
5548 "Send it anyway?"), line + 1);
5549 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5551 if (aval != G_ALERTALTERNATE) {
5557 if (encoding != ENC_UNKNOWN)
5558 procmime_encode_content(mimetext, encoding);
5560 /* append attachment parts */
5561 if (compose_use_attach(compose) && attach_parts) {
5562 MimeInfo *mimempart;
5563 gchar *boundary = NULL;
5564 mimempart = procmime_mimeinfo_new();
5565 mimempart->content = MIMECONTENT_EMPTY;
5566 mimempart->type = MIMETYPE_MULTIPART;
5567 mimempart->subtype = g_strdup("mixed");
5571 boundary = generate_mime_boundary(NULL);
5572 } while (strstr(buf, boundary) != NULL);
5574 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5577 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5579 g_node_append(mimempart->node, mimetext->node);
5580 g_node_append(mimemsg->node, mimempart->node);
5582 if (compose_add_attachments(compose, mimempart) < 0)
5585 g_node_append(mimemsg->node, mimetext->node);
5589 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5590 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5591 /* extract name and address */
5592 if (strstr(spec, " <") && strstr(spec, ">")) {
5593 from_addr = g_strdup(strrchr(spec, '<')+1);
5594 *(strrchr(from_addr, '>')) = '\0';
5595 from_name = g_strdup(spec);
5596 *(strrchr(from_name, '<')) = '\0';
5603 /* sign message if sending */
5604 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5605 privacy_system_can_sign(compose->privacy_system))
5606 if (!privacy_sign(compose->privacy_system, mimemsg,
5607 compose->account, from_addr)) {
5614 procmime_write_mimeinfo(mimemsg, fp);
5616 procmime_mimeinfo_free_all(mimemsg);
5621 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5623 GtkTextBuffer *buffer;
5624 GtkTextIter start, end;
5629 if ((fp = g_fopen(file, "wb")) == NULL) {
5630 FILE_OP_ERROR(file, "fopen");
5634 /* chmod for security */
5635 if (change_file_mode_rw(fp, file) < 0) {
5636 FILE_OP_ERROR(file, "chmod");
5637 g_warning("can't change file mode\n");
5640 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5641 gtk_text_buffer_get_start_iter(buffer, &start);
5642 gtk_text_buffer_get_end_iter(buffer, &end);
5643 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5645 chars = conv_codeset_strdup
5646 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5649 if (!chars) return -1;
5652 len = strlen(chars);
5653 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5654 FILE_OP_ERROR(file, "fwrite");
5663 if (fclose(fp) == EOF) {
5664 FILE_OP_ERROR(file, "fclose");
5671 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5674 MsgInfo *msginfo = compose->targetinfo;
5676 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5677 if (!msginfo) return -1;
5679 if (!force && MSG_IS_LOCKED(msginfo->flags))
5682 item = msginfo->folder;
5683 cm_return_val_if_fail(item != NULL, -1);
5685 if (procmsg_msg_exist(msginfo) &&
5686 (folder_has_parent_of_type(item, F_QUEUE) ||
5687 folder_has_parent_of_type(item, F_DRAFT)
5688 || msginfo == compose->autosaved_draft)) {
5689 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5690 g_warning("can't remove the old message\n");
5693 debug_print("removed reedit target %d\n", msginfo->msgnum);
5700 static void compose_remove_draft(Compose *compose)
5703 MsgInfo *msginfo = compose->targetinfo;
5704 drafts = account_get_special_folder(compose->account, F_DRAFT);
5706 if (procmsg_msg_exist(msginfo)) {
5707 folder_item_remove_msg(drafts, msginfo->msgnum);
5712 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5713 gboolean remove_reedit_target)
5715 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5718 static gboolean compose_warn_encryption(Compose *compose)
5720 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5721 AlertValue val = G_ALERTALTERNATE;
5723 if (warning == NULL)
5726 val = alertpanel_full(_("Encryption warning"), warning,
5727 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5728 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5729 if (val & G_ALERTDISABLE) {
5730 val &= ~G_ALERTDISABLE;
5731 if (val == G_ALERTALTERNATE)
5732 privacy_inhibit_encrypt_warning(compose->privacy_system,
5736 if (val == G_ALERTALTERNATE) {
5743 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5744 gchar **msgpath, gboolean check_subject,
5745 gboolean remove_reedit_target)
5752 static gboolean lock = FALSE;
5753 PrefsAccount *mailac = NULL, *newsac = NULL;
5754 gboolean err = FALSE;
5756 debug_print("queueing message...\n");
5757 cm_return_val_if_fail(compose->account != NULL, -1);
5761 if (compose_check_entries(compose, check_subject) == FALSE) {
5763 if (compose->batch) {
5764 gtk_widget_show_all(compose->window);
5769 if (!compose->to_list && !compose->newsgroup_list) {
5770 g_warning("can't get recipient list.");
5775 if (compose->to_list) {
5776 if (compose->account->protocol != A_NNTP)
5777 mailac = compose->account;
5778 else if (cur_account && cur_account->protocol != A_NNTP)
5779 mailac = cur_account;
5780 else if (!(mailac = compose_current_mail_account())) {
5782 alertpanel_error(_("No account for sending mails available!"));
5787 if (compose->newsgroup_list) {
5788 if (compose->account->protocol == A_NNTP)
5789 newsac = compose->account;
5792 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5797 /* write queue header */
5798 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5799 G_DIR_SEPARATOR, compose, (guint) rand());
5800 debug_print("queuing to %s\n", tmp);
5801 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5802 FILE_OP_ERROR(tmp, "fopen");
5808 if (change_file_mode_rw(fp, tmp) < 0) {
5809 FILE_OP_ERROR(tmp, "chmod");
5810 g_warning("can't change file mode\n");
5813 /* queueing variables */
5814 err |= (fprintf(fp, "AF:\n") < 0);
5815 err |= (fprintf(fp, "NF:0\n") < 0);
5816 err |= (fprintf(fp, "PS:10\n") < 0);
5817 err |= (fprintf(fp, "SRH:1\n") < 0);
5818 err |= (fprintf(fp, "SFN:\n") < 0);
5819 err |= (fprintf(fp, "DSR:\n") < 0);
5821 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5823 err |= (fprintf(fp, "MID:\n") < 0);
5824 err |= (fprintf(fp, "CFG:\n") < 0);
5825 err |= (fprintf(fp, "PT:0\n") < 0);
5826 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5827 err |= (fprintf(fp, "RQ:\n") < 0);
5829 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5831 err |= (fprintf(fp, "SSV:\n") < 0);
5833 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5835 err |= (fprintf(fp, "NSV:\n") < 0);
5836 err |= (fprintf(fp, "SSH:\n") < 0);
5837 /* write recepient list */
5838 if (compose->to_list) {
5839 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5840 for (cur = compose->to_list->next; cur != NULL;
5842 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5843 err |= (fprintf(fp, "\n") < 0);
5845 /* write newsgroup list */
5846 if (compose->newsgroup_list) {
5847 err |= (fprintf(fp, "NG:") < 0);
5848 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5849 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5850 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5851 err |= (fprintf(fp, "\n") < 0);
5853 /* Sylpheed account IDs */
5855 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5857 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5860 if (compose->privacy_system != NULL) {
5861 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5862 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5863 if (compose->use_encryption) {
5865 if (!compose_warn_encryption(compose)) {
5872 if (mailac && mailac->encrypt_to_self) {
5873 GSList *tmp_list = g_slist_copy(compose->to_list);
5874 tmp_list = g_slist_append(tmp_list, compose->account->address);
5875 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5876 g_slist_free(tmp_list);
5878 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5880 if (encdata != NULL) {
5881 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5882 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5883 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5885 } /* else we finally dont want to encrypt */
5887 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5888 /* and if encdata was null, it means there's been a problem in
5891 g_warning("failed to write queue message");
5902 /* Save copy folder */
5903 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5904 gchar *savefolderid;
5906 savefolderid = compose_get_save_to(compose);
5907 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5908 g_free(savefolderid);
5910 /* Save copy folder */
5911 if (compose->return_receipt) {
5912 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5914 /* Message-ID of message replying to */
5915 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5918 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5919 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5922 /* Message-ID of message forwarding to */
5923 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5926 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5927 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5931 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5932 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5934 /* end of headers */
5935 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5937 if (compose->redirect_filename != NULL) {
5938 if (compose_redirect_write_to_file(compose, fp) < 0) {
5947 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5952 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5956 g_warning("failed to write queue message\n");
5963 if (fclose(fp) == EOF) {
5964 FILE_OP_ERROR(tmp, "fclose");
5971 if (item && *item) {
5974 queue = account_get_special_folder(compose->account, F_QUEUE);
5977 g_warning("can't find queue folder\n");
5983 folder_item_scan(queue);
5984 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5985 g_warning("can't queue the message\n");
5992 if (msgpath == NULL) {
5998 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5999 compose_remove_reedit_target(compose, FALSE);
6002 if ((msgnum != NULL) && (item != NULL)) {
6010 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
6013 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
6015 struct stat statbuf;
6016 gchar *type, *subtype;
6017 GtkTreeModel *model;
6020 model = gtk_tree_view_get_model(tree_view);
6022 if (!gtk_tree_model_get_iter_first(model, &iter))
6025 gtk_tree_model_get(model, &iter,
6029 if (!is_file_exist(ainfo->file)) {
6030 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
6031 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
6032 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
6034 if (val == G_ALERTDEFAULT) {
6039 mimepart = procmime_mimeinfo_new();
6040 mimepart->content = MIMECONTENT_FILE;
6041 mimepart->data.filename = g_strdup(ainfo->file);
6042 mimepart->tmp = FALSE; /* or we destroy our attachment */
6043 mimepart->offset = 0;
6045 g_stat(ainfo->file, &statbuf);
6046 mimepart->length = statbuf.st_size;
6048 type = g_strdup(ainfo->content_type);
6050 if (!strchr(type, '/')) {
6052 type = g_strdup("application/octet-stream");
6055 subtype = strchr(type, '/') + 1;
6056 *(subtype - 1) = '\0';
6057 mimepart->type = procmime_get_media_type(type);
6058 mimepart->subtype = g_strdup(subtype);
6061 if (mimepart->type == MIMETYPE_MESSAGE &&
6062 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
6063 mimepart->disposition = DISPOSITIONTYPE_INLINE;
6064 } else if (mimepart->type == MIMETYPE_TEXT) {
6065 if (!ainfo->name && g_ascii_strcasecmp(mimepart->subtype, "plain")) {
6066 /* Text parts with no name come from multipart/alternative
6067 * forwards. Make sure the recipient won't look at the
6068 * original HTML part by mistake. */
6069 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6070 ainfo->name = g_strdup_printf(_("Original %s part"),
6074 g_hash_table_insert(mimepart->typeparameters,
6075 g_strdup("charset"), g_strdup(ainfo->charset));
6077 if (ainfo->name && mimepart->type != MIMETYPE_MESSAGE) {
6078 if (mimepart->type == MIMETYPE_APPLICATION &&
6079 !strcmp2(mimepart->subtype, "octet-stream"))
6080 g_hash_table_insert(mimepart->typeparameters,
6081 g_strdup("name"), g_strdup(ainfo->name));
6082 g_hash_table_insert(mimepart->dispositionparameters,
6083 g_strdup("filename"), g_strdup(ainfo->name));
6084 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6087 if (mimepart->type == MIMETYPE_MESSAGE
6088 || mimepart->type == MIMETYPE_MULTIPART)
6089 ainfo->encoding = ENC_BINARY;
6090 else if (compose->use_signing) {
6091 if (ainfo->encoding == ENC_7BIT)
6092 ainfo->encoding = ENC_QUOTED_PRINTABLE;
6093 else if (ainfo->encoding == ENC_8BIT)
6094 ainfo->encoding = ENC_BASE64;
6099 procmime_encode_content(mimepart, ainfo->encoding);
6101 g_node_append(parent->node, mimepart->node);
6102 } while (gtk_tree_model_iter_next(model, &iter));
6107 #define IS_IN_CUSTOM_HEADER(header) \
6108 (compose->account->add_customhdr && \
6109 custom_header_find(compose->account->customhdr_list, header) != NULL)
6111 static void compose_add_headerfield_from_headerlist(Compose *compose,
6113 const gchar *fieldname,
6114 const gchar *seperator)
6116 gchar *str, *fieldname_w_colon;
6117 gboolean add_field = FALSE;
6119 ComposeHeaderEntry *headerentry;
6120 const gchar *headerentryname;
6121 const gchar *trans_fieldname;
6124 if (IS_IN_CUSTOM_HEADER(fieldname))
6127 debug_print("Adding %s-fields\n", fieldname);
6129 fieldstr = g_string_sized_new(64);
6131 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6132 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6134 for (list = compose->header_list; list; list = list->next) {
6135 headerentry = ((ComposeHeaderEntry *)list->data);
6136 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6138 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6139 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6141 if (str[0] != '\0') {
6143 g_string_append(fieldstr, seperator);
6144 g_string_append(fieldstr, str);
6153 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6154 compose_convert_header
6155 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6156 strlen(fieldname) + 2, TRUE);
6157 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6161 g_free(fieldname_w_colon);
6162 g_string_free(fieldstr, TRUE);
6167 static gchar *compose_get_manual_headers_info(Compose *compose)
6169 GString *sh_header = g_string_new(" ");
6171 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6173 for (list = compose->header_list; list; list = list->next) {
6174 ComposeHeaderEntry *headerentry;
6177 gchar *headername_wcolon;
6178 const gchar *headername_trans;
6180 gboolean standard_header = FALSE;
6182 headerentry = ((ComposeHeaderEntry *)list->data);
6184 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6186 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6191 if (!strstr(tmp, ":")) {
6192 headername_wcolon = g_strconcat(tmp, ":", NULL);
6193 headername = g_strdup(tmp);
6195 headername_wcolon = g_strdup(tmp);
6196 headername = g_strdup(strtok(tmp, ":"));
6200 string = std_headers;
6201 while (*string != NULL) {
6202 headername_trans = prefs_common_translated_header_name(*string);
6203 if (!strcmp(headername_trans, headername_wcolon))
6204 standard_header = TRUE;
6207 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6208 g_string_append_printf(sh_header, "%s ", headername);
6210 g_free(headername_wcolon);
6212 g_string_truncate(sh_header, strlen(sh_header->str) - 1); /* remove last space */
6213 return g_string_free(sh_header, FALSE);
6216 static gchar *compose_get_header(Compose *compose)
6218 gchar buf[BUFFSIZE];
6219 const gchar *entry_str;
6223 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6225 gchar *from_name = NULL, *from_address = NULL;
6228 cm_return_val_if_fail(compose->account != NULL, NULL);
6229 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6231 header = g_string_sized_new(64);
6234 get_rfc822_date(buf, sizeof(buf));
6235 g_string_append_printf(header, "Date: %s\n", buf);
6239 if (compose->account->name && *compose->account->name) {
6241 QUOTE_IF_REQUIRED(buf, compose->account->name);
6242 tmp = g_strdup_printf("%s <%s>",
6243 buf, compose->account->address);
6245 tmp = g_strdup_printf("%s",
6246 compose->account->address);
6248 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6249 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6251 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6252 from_address = g_strdup(compose->account->address);
6254 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6255 /* extract name and address */
6256 if (strstr(spec, " <") && strstr(spec, ">")) {
6257 from_address = g_strdup(strrchr(spec, '<')+1);
6258 *(strrchr(from_address, '>')) = '\0';
6259 from_name = g_strdup(spec);
6260 *(strrchr(from_name, '<')) = '\0';
6263 from_address = g_strdup(spec);
6270 if (from_name && *from_name) {
6271 compose_convert_header
6272 (compose, buf, sizeof(buf), from_name,
6273 strlen("From: "), TRUE);
6274 QUOTE_IF_REQUIRED(name, buf);
6276 g_string_append_printf(header, "From: %s <%s>\n",
6277 name, from_address);
6279 g_string_append_printf(header, "From: %s\n", from_address);
6282 g_free(from_address);
6285 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6288 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6291 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6295 * If this account is a NNTP account remove Bcc header from
6296 * message body since it otherwise will be publicly shown
6298 if (compose->account->protocol != A_NNTP)
6299 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6302 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6304 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6307 compose_convert_header(compose, buf, sizeof(buf), str,
6308 strlen("Subject: "), FALSE);
6309 g_string_append_printf(header, "Subject: %s\n", buf);
6315 if (compose->account->set_domain && compose->account->domain) {
6316 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6317 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6318 g_snprintf(buf, sizeof(buf), "%s",
6319 strchr(compose->account->address, '@') ?
6320 strchr(compose->account->address, '@')+1 :
6321 compose->account->address);
6323 g_snprintf(buf, sizeof(buf), "%s", "");
6326 if (compose->account->gen_msgid) {
6328 if (compose->account->msgid_with_addr) {
6329 addr = compose->account->address;
6331 generate_msgid(buf, sizeof(buf), addr);
6332 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6333 compose->msgid = g_strdup(buf);
6335 compose->msgid = NULL;
6338 if (compose->remove_references == FALSE) {
6340 if (compose->inreplyto && compose->to_list)
6341 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6344 if (compose->references)
6345 g_string_append_printf(header, "References: %s\n", compose->references);
6349 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6352 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6355 if (compose->account->organization &&
6356 strlen(compose->account->organization) &&
6357 !IS_IN_CUSTOM_HEADER("Organization")) {
6358 compose_convert_header(compose, buf, sizeof(buf),
6359 compose->account->organization,
6360 strlen("Organization: "), FALSE);
6361 g_string_append_printf(header, "Organization: %s\n", buf);
6364 /* Program version and system info */
6365 if (compose->account->gen_xmailer &&
6366 g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6367 !compose->newsgroup_list) {
6368 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6370 gtk_major_version, gtk_minor_version, gtk_micro_version,
6373 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6374 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6376 gtk_major_version, gtk_minor_version, gtk_micro_version,
6380 /* custom headers */
6381 if (compose->account->add_customhdr) {
6384 for (cur = compose->account->customhdr_list; cur != NULL;
6386 CustomHeader *chdr = (CustomHeader *)cur->data;
6388 if (custom_header_is_allowed(chdr->name)
6389 && chdr->value != NULL
6390 && *(chdr->value) != '\0') {
6391 compose_convert_header
6392 (compose, buf, sizeof(buf),
6394 strlen(chdr->name) + 2, FALSE);
6395 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6400 /* Automatic Faces and X-Faces */
6401 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6402 g_string_append_printf(header, "X-Face: %s\n", buf);
6404 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6405 g_string_append_printf(header, "X-Face: %s\n", buf);
6407 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6408 g_string_append_printf(header, "Face: %s\n", buf);
6410 else if (get_default_face (buf, sizeof(buf)) == 0) {
6411 g_string_append_printf(header, "Face: %s\n", buf);
6415 switch (compose->priority) {
6416 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6417 "X-Priority: 1 (Highest)\n");
6419 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6420 "X-Priority: 2 (High)\n");
6422 case PRIORITY_NORMAL: break;
6423 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6424 "X-Priority: 4 (Low)\n");
6426 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6427 "X-Priority: 5 (Lowest)\n");
6429 default: debug_print("compose: priority unknown : %d\n",
6433 /* Request Return Receipt */
6434 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6435 if (compose->return_receipt) {
6436 if (compose->account->name
6437 && *compose->account->name) {
6438 compose_convert_header(compose, buf, sizeof(buf),
6439 compose->account->name,
6440 strlen("Disposition-Notification-To: "),
6442 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6444 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6448 /* get special headers */
6449 for (list = compose->header_list; list; list = list->next) {
6450 ComposeHeaderEntry *headerentry;
6453 gchar *headername_wcolon;
6454 const gchar *headername_trans;
6457 gboolean standard_header = FALSE;
6459 headerentry = ((ComposeHeaderEntry *)list->data);
6461 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6463 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6468 if (!strstr(tmp, ":")) {
6469 headername_wcolon = g_strconcat(tmp, ":", NULL);
6470 headername = g_strdup(tmp);
6472 headername_wcolon = g_strdup(tmp);
6473 headername = g_strdup(strtok(tmp, ":"));
6477 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6478 Xstrdup_a(headervalue, entry_str, return NULL);
6479 subst_char(headervalue, '\r', ' ');
6480 subst_char(headervalue, '\n', ' ');
6481 string = std_headers;
6482 while (*string != NULL) {
6483 headername_trans = prefs_common_translated_header_name(*string);
6484 if (!strcmp(headername_trans, headername_wcolon))
6485 standard_header = TRUE;
6488 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6489 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6492 g_free(headername_wcolon);
6496 g_string_free(header, FALSE);
6501 #undef IS_IN_CUSTOM_HEADER
6503 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6504 gint header_len, gboolean addr_field)
6506 gchar *tmpstr = NULL;
6507 const gchar *out_codeset = NULL;
6509 cm_return_if_fail(src != NULL);
6510 cm_return_if_fail(dest != NULL);
6512 if (len < 1) return;
6514 tmpstr = g_strdup(src);
6516 subst_char(tmpstr, '\n', ' ');
6517 subst_char(tmpstr, '\r', ' ');
6520 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6521 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6522 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6527 codeconv_set_strict(TRUE);
6528 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6529 conv_get_charset_str(compose->out_encoding));
6530 codeconv_set_strict(FALSE);
6532 if (!dest || *dest == '\0') {
6533 gchar *test_conv_global_out = NULL;
6534 gchar *test_conv_reply = NULL;
6536 /* automatic mode. be automatic. */
6537 codeconv_set_strict(TRUE);
6539 out_codeset = conv_get_outgoing_charset_str();
6541 debug_print("trying to convert to %s\n", out_codeset);
6542 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6545 if (!test_conv_global_out && compose->orig_charset
6546 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6547 out_codeset = compose->orig_charset;
6548 debug_print("failure; trying to convert to %s\n", out_codeset);
6549 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6552 if (!test_conv_global_out && !test_conv_reply) {
6554 out_codeset = CS_INTERNAL;
6555 debug_print("finally using %s\n", out_codeset);
6557 g_free(test_conv_global_out);
6558 g_free(test_conv_reply);
6559 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6561 codeconv_set_strict(FALSE);
6566 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6570 cm_return_if_fail(user_data != NULL);
6572 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6573 g_strstrip(address);
6574 if (*address != '\0') {
6575 gchar *name = procheader_get_fromname(address);
6576 extract_address(address);
6577 #ifndef USE_NEW_ADDRBOOK
6578 addressbook_add_contact(name, address, NULL, NULL);
6580 debug_print("%s: %s\n", name, address);
6581 if (addressadd_selection(name, address, NULL, NULL)) {
6582 debug_print( "addressbook_add_contact - added\n" );
6589 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6591 GtkWidget *menuitem;
6594 cm_return_if_fail(menu != NULL);
6595 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6597 menuitem = gtk_separator_menu_item_new();
6598 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6599 gtk_widget_show(menuitem);
6601 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6602 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6604 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6605 g_strstrip(address);
6606 if (*address == '\0') {
6607 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6610 g_signal_connect(G_OBJECT(menuitem), "activate",
6611 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6612 gtk_widget_show(menuitem);
6615 static void compose_create_header_entry(Compose *compose)
6617 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6624 const gchar *header = NULL;
6625 ComposeHeaderEntry *headerentry;
6626 gboolean standard_header = FALSE;
6627 GtkListStore *model;
6629 #if !(GTK_CHECK_VERSION(2,12,0))
6630 GtkTooltips *tips = compose->tooltips;
6633 headerentry = g_new0(ComposeHeaderEntry, 1);
6636 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6637 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6638 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6640 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6642 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6644 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6645 COMPOSE_NEWSGROUPS);
6646 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6648 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6649 COMPOSE_FOLLOWUPTO);
6651 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6652 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo))), "grab_focus",
6653 G_CALLBACK(compose_grab_focus_cb), compose);
6654 gtk_widget_show(combo);
6657 l = g_list_prepend(l, gtk_bin_get_child(GTK_BIN(combo)));
6658 gtk_container_set_focus_chain(GTK_CONTAINER(combo), l);
6661 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6662 compose->header_nextrow, compose->header_nextrow+1,
6663 GTK_SHRINK, GTK_FILL, 0, 0);
6664 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6665 const gchar *last_header_entry = gtk_entry_get_text(
6666 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6668 while (*string != NULL) {
6669 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6670 standard_header = TRUE;
6673 if (standard_header)
6674 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6676 if (!compose->header_last || !standard_header) {
6677 switch(compose->account->protocol) {
6679 header = prefs_common_translated_header_name("Newsgroups:");
6682 header = prefs_common_translated_header_name("To:");
6687 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6689 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6690 G_CALLBACK(compose_grab_focus_cb), compose);
6692 /* Entry field with cleanup button */
6693 button = gtk_button_new();
6694 gtk_button_set_image(GTK_BUTTON(button),
6695 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6696 gtk_widget_show(button);
6697 CLAWS_SET_TIP(button,
6698 _("Delete entry contents"));
6699 entry = gtk_entry_new();
6700 gtk_widget_show(entry);
6701 CLAWS_SET_TIP(entry,
6702 _("Use <tab> to autocomplete from addressbook"));
6703 hbox = gtk_hbox_new (FALSE, 0);
6704 gtk_widget_show(hbox);
6705 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6706 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6707 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6708 compose->header_nextrow, compose->header_nextrow+1,
6709 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6711 g_signal_connect(G_OBJECT(entry), "key-press-event",
6712 G_CALLBACK(compose_headerentry_key_press_event_cb),
6714 g_signal_connect(G_OBJECT(entry), "changed",
6715 G_CALLBACK(compose_headerentry_changed_cb),
6717 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6718 G_CALLBACK(compose_grab_focus_cb), compose);
6720 g_signal_connect(G_OBJECT(button), "clicked",
6721 G_CALLBACK(compose_headerentry_button_clicked_cb),
6725 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6726 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6727 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6728 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6729 G_CALLBACK(compose_header_drag_received_cb),
6731 g_signal_connect(G_OBJECT(entry), "drag-drop",
6732 G_CALLBACK(compose_drag_drop),
6734 g_signal_connect(G_OBJECT(entry), "populate-popup",
6735 G_CALLBACK(compose_entry_popup_extend),
6738 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6740 headerentry->compose = compose;
6741 headerentry->combo = combo;
6742 headerentry->entry = entry;
6743 headerentry->button = button;
6744 headerentry->hbox = hbox;
6745 headerentry->headernum = compose->header_nextrow;
6746 headerentry->type = PREF_NONE;
6748 compose->header_nextrow++;
6749 compose->header_last = headerentry;
6750 compose->header_list =
6751 g_slist_append(compose->header_list,
6755 static void compose_add_header_entry(Compose *compose, const gchar *header,
6756 gchar *text, ComposePrefType pref_type)
6758 ComposeHeaderEntry *last_header = compose->header_last;
6759 gchar *tmp = g_strdup(text), *email;
6760 gboolean replyto_hdr;
6762 replyto_hdr = (!strcasecmp(header,
6763 prefs_common_translated_header_name("Reply-To:")) ||
6765 prefs_common_translated_header_name("Followup-To:")) ||
6767 prefs_common_translated_header_name("In-Reply-To:")));
6769 extract_address(tmp);
6770 email = g_utf8_strdown(tmp, -1);
6772 if (replyto_hdr == FALSE &&
6773 g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6775 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6776 header, text, (gint) pref_type);
6782 if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6783 gtk_entry_set_text(GTK_ENTRY(
6784 gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6786 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6787 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6788 last_header->type = pref_type;
6790 if (replyto_hdr == FALSE)
6791 g_hash_table_insert(compose->email_hashtable, email,
6792 GUINT_TO_POINTER(1));
6799 static void compose_destroy_headerentry(Compose *compose,
6800 ComposeHeaderEntry *headerentry)
6802 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6805 extract_address(text);
6806 email = g_utf8_strdown(text, -1);
6807 g_hash_table_remove(compose->email_hashtable, email);
6811 gtk_widget_destroy(headerentry->combo);
6812 gtk_widget_destroy(headerentry->entry);
6813 gtk_widget_destroy(headerentry->button);
6814 gtk_widget_destroy(headerentry->hbox);
6815 g_free(headerentry);
6818 static void compose_remove_header_entries(Compose *compose)
6821 for (list = compose->header_list; list; list = list->next)
6822 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6824 compose->header_last = NULL;
6825 g_slist_free(compose->header_list);
6826 compose->header_list = NULL;
6827 compose->header_nextrow = 1;
6828 compose_create_header_entry(compose);
6831 static GtkWidget *compose_create_header(Compose *compose)
6833 GtkWidget *from_optmenu_hbox;
6834 GtkWidget *header_scrolledwin_main;
6835 GtkWidget *header_table_main;
6836 GtkWidget *header_scrolledwin;
6837 GtkWidget *header_table;
6839 /* parent with account selection and from header */
6840 header_scrolledwin_main = gtk_scrolled_window_new(NULL, NULL);
6841 gtk_widget_show(header_scrolledwin_main);
6842 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin_main), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6844 header_table_main = gtk_table_new(2, 2, FALSE);
6845 gtk_widget_show(header_table_main);
6846 gtk_container_set_border_width(GTK_CONTAINER(header_table_main), BORDER_WIDTH);
6847 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin_main), header_table_main);
6848 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin_main)))), GTK_SHADOW_NONE);
6850 from_optmenu_hbox = compose_account_option_menu_create(compose);
6851 gtk_table_attach(GTK_TABLE(header_table_main), from_optmenu_hbox,
6852 0, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6854 /* child with header labels and entries */
6855 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6856 gtk_widget_show(header_scrolledwin);
6857 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6859 header_table = gtk_table_new(2, 2, FALSE);
6860 gtk_widget_show(header_table);
6861 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6862 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6863 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6865 gtk_table_attach(GTK_TABLE(header_table_main), header_scrolledwin,
6866 0, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 2);
6868 compose->header_table = header_table;
6869 compose->header_list = NULL;
6870 compose->header_nextrow = 0;
6872 compose_create_header_entry(compose);
6874 compose->table = NULL;
6876 return header_scrolledwin_main;
6879 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6881 Compose *compose = (Compose *)data;
6882 GdkEventButton event;
6885 event.time = gtk_get_current_event_time();
6887 return attach_button_pressed(compose->attach_clist, &event, compose);
6890 static GtkWidget *compose_create_attach(Compose *compose)
6892 GtkWidget *attach_scrwin;
6893 GtkWidget *attach_clist;
6895 GtkListStore *store;
6896 GtkCellRenderer *renderer;
6897 GtkTreeViewColumn *column;
6898 GtkTreeSelection *selection;
6900 /* attachment list */
6901 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6902 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6903 GTK_POLICY_AUTOMATIC,
6904 GTK_POLICY_AUTOMATIC);
6905 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6907 store = gtk_list_store_new(N_ATTACH_COLS,
6913 G_TYPE_AUTO_POINTER,
6915 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6916 (GTK_TREE_MODEL(store)));
6917 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6918 g_object_unref(store);
6920 renderer = gtk_cell_renderer_text_new();
6921 column = gtk_tree_view_column_new_with_attributes
6922 (_("Mime type"), renderer, "text",
6923 COL_MIMETYPE, NULL);
6924 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6926 renderer = gtk_cell_renderer_text_new();
6927 column = gtk_tree_view_column_new_with_attributes
6928 (_("Size"), renderer, "text",
6930 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6932 renderer = gtk_cell_renderer_text_new();
6933 column = gtk_tree_view_column_new_with_attributes
6934 (_("Name"), renderer, "text",
6936 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6938 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6939 prefs_common.use_stripes_everywhere);
6940 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6941 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6943 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6944 G_CALLBACK(attach_selected), compose);
6945 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6946 G_CALLBACK(attach_button_pressed), compose);
6948 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6949 G_CALLBACK(popup_attach_button_pressed), compose);
6951 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6952 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6953 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6954 G_CALLBACK(popup_attach_button_pressed), compose);
6956 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6957 G_CALLBACK(attach_key_pressed), compose);
6960 gtk_drag_dest_set(attach_clist,
6961 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6962 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6963 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6964 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6965 G_CALLBACK(compose_attach_drag_received_cb),
6967 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6968 G_CALLBACK(compose_drag_drop),
6971 compose->attach_scrwin = attach_scrwin;
6972 compose->attach_clist = attach_clist;
6974 return attach_scrwin;
6977 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6978 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6980 static GtkWidget *compose_create_others(Compose *compose)
6983 GtkWidget *savemsg_checkbtn;
6984 GtkWidget *savemsg_combo;
6985 GtkWidget *savemsg_select;
6988 gchar *folderidentifier;
6990 /* Table for settings */
6991 table = gtk_table_new(3, 1, FALSE);
6992 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6993 gtk_widget_show(table);
6994 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6997 /* Save Message to folder */
6998 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6999 gtk_widget_show(savemsg_checkbtn);
7000 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7001 if (account_get_special_folder(compose->account, F_OUTBOX)) {
7002 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
7004 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
7005 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
7007 savemsg_combo = gtk_combo_box_entry_new_text();
7008 compose->savemsg_checkbtn = savemsg_checkbtn;
7009 compose->savemsg_combo = savemsg_combo;
7010 gtk_widget_show(savemsg_combo);
7012 if (prefs_common.compose_save_to_history)
7013 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
7014 prefs_common.compose_save_to_history);
7016 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
7017 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
7018 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
7019 G_CALLBACK(compose_grab_focus_cb), compose);
7020 if (account_get_special_folder(compose->account, F_OUTBOX)) {
7021 folderidentifier = folder_item_get_identifier(account_get_special_folder
7022 (compose->account, F_OUTBOX));
7023 compose_set_save_to(compose, folderidentifier);
7024 g_free(folderidentifier);
7027 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
7028 gtk_widget_show(savemsg_select);
7029 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7030 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
7031 G_CALLBACK(compose_savemsg_select_cb),
7037 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
7039 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
7040 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
7043 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
7048 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
7051 path = folder_item_get_identifier(dest);
7053 compose_set_save_to(compose, path);
7057 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
7058 GdkAtom clip, GtkTextIter *insert_place);
7061 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
7065 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7067 if (event->button == 3) {
7069 GtkTextIter sel_start, sel_end;
7070 gboolean stuff_selected;
7072 /* move the cursor to allow GtkAspell to check the word
7073 * under the mouse */
7074 if (event->x && event->y) {
7075 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7076 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7078 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7081 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
7082 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7085 stuff_selected = gtk_text_buffer_get_selection_bounds(
7087 &sel_start, &sel_end);
7089 gtk_text_buffer_place_cursor (buffer, &iter);
7090 /* reselect stuff */
7092 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
7093 gtk_text_buffer_select_range(buffer,
7094 &sel_start, &sel_end);
7096 return FALSE; /* pass the event so that the right-click goes through */
7099 if (event->button == 2) {
7104 /* get the middle-click position to paste at the correct place */
7105 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7106 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7108 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7111 entry_paste_clipboard(compose, text,
7112 prefs_common.linewrap_pastes,
7113 GDK_SELECTION_PRIMARY, &iter);
7121 static void compose_spell_menu_changed(void *data)
7123 Compose *compose = (Compose *)data;
7125 GtkWidget *menuitem;
7126 GtkWidget *parent_item;
7127 GtkMenu *menu = GTK_MENU(gtk_menu_new());
7130 if (compose->gtkaspell == NULL)
7133 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
7134 "/Menu/Spelling/Options");
7136 /* setting the submenu removes /Spelling/Options from the factory
7137 * so we need to save it */
7139 if (parent_item == NULL) {
7140 parent_item = compose->aspell_options_menu;
7141 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
7143 compose->aspell_options_menu = parent_item;
7145 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
7147 spell_menu = g_slist_reverse(spell_menu);
7148 for (items = spell_menu;
7149 items; items = items->next) {
7150 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
7151 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
7152 gtk_widget_show(GTK_WIDGET(menuitem));
7154 g_slist_free(spell_menu);
7156 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7157 gtk_widget_show(parent_item);
7160 static void compose_dict_changed(void *data)
7162 Compose *compose = (Compose *) data;
7164 if(compose->gtkaspell &&
7165 compose->gtkaspell->recheck_when_changing_dict == FALSE)
7168 gtkaspell_highlight_all(compose->gtkaspell);
7169 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7173 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7175 Compose *compose = (Compose *)data;
7176 GdkEventButton event;
7179 event.time = gtk_get_current_event_time();
7183 return text_clicked(compose->text, &event, compose);
7186 static gboolean compose_force_window_origin = TRUE;
7187 static Compose *compose_create(PrefsAccount *account,
7196 GtkWidget *handlebox;
7198 GtkWidget *notebook;
7200 GtkWidget *attach_hbox;
7201 GtkWidget *attach_lab1;
7202 GtkWidget *attach_lab2;
7207 GtkWidget *subject_hbox;
7208 GtkWidget *subject_frame;
7209 GtkWidget *subject_entry;
7213 GtkWidget *edit_vbox;
7214 GtkWidget *ruler_hbox;
7216 GtkWidget *scrolledwin;
7218 GtkTextBuffer *buffer;
7219 GtkClipboard *clipboard;
7221 UndoMain *undostruct;
7223 gchar *titles[N_ATTACH_COLS];
7224 GtkWidget *popupmenu;
7225 GtkWidget *tmpl_menu;
7226 GtkActionGroup *action_group = NULL;
7229 GtkAspell * gtkaspell = NULL;
7232 static GdkGeometry geometry;
7234 cm_return_val_if_fail(account != NULL, NULL);
7236 debug_print("Creating compose window...\n");
7237 compose = g_new0(Compose, 1);
7239 titles[COL_MIMETYPE] = _("MIME type");
7240 titles[COL_SIZE] = _("Size");
7241 titles[COL_NAME] = _("Name");
7242 titles[COL_CHARSET] = _("Charset");
7244 compose->batch = batch;
7245 compose->account = account;
7246 compose->folder = folder;
7248 compose->mutex = g_mutex_new();
7249 compose->set_cursor_pos = -1;
7251 #if !(GTK_CHECK_VERSION(2,12,0))
7252 compose->tooltips = tips;
7255 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7257 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7258 gtk_widget_set_size_request(window, prefs_common.compose_width,
7259 prefs_common.compose_height);
7261 if (!geometry.max_width) {
7262 geometry.max_width = gdk_screen_width();
7263 geometry.max_height = gdk_screen_height();
7266 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7267 &geometry, GDK_HINT_MAX_SIZE);
7268 if (!geometry.min_width) {
7269 geometry.min_width = 600;
7270 geometry.min_height = 440;
7272 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7273 &geometry, GDK_HINT_MIN_SIZE);
7275 #ifndef GENERIC_UMPC
7276 if (compose_force_window_origin)
7277 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7278 prefs_common.compose_y);
7280 g_signal_connect(G_OBJECT(window), "delete_event",
7281 G_CALLBACK(compose_delete_cb), compose);
7282 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7283 gtk_widget_realize(window);
7285 gtkut_widget_set_composer_icon(window);
7287 vbox = gtk_vbox_new(FALSE, 0);
7288 gtk_container_add(GTK_CONTAINER(window), vbox);
7290 compose->ui_manager = gtk_ui_manager_new();
7291 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7292 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7293 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7294 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7295 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7296 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7297 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7298 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7299 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7300 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7303 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7305 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7308 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7309 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7311 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7313 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7314 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7315 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7318 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7319 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7320 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7321 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7322 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7323 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7324 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7325 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7326 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7327 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM)
7328 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7329 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7332 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7333 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7334 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7336 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7337 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7338 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7340 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7341 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7342 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7343 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7345 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7347 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7348 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7349 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7350 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7351 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7352 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7353 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7354 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7355 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7356 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7357 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7358 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7359 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7360 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7361 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7363 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7365 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7366 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7367 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7368 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7369 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7371 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7373 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7377 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7378 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7379 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7380 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7381 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7382 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7386 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7387 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7388 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7389 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7390 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7392 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7393 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7394 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7395 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7396 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7399 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7400 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7401 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7402 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7403 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7404 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7405 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7407 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7408 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7409 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7410 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7411 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7413 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7415 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7416 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7417 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7418 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7419 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7421 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7422 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)
7423 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)
7424 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7426 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7428 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7429 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)
7430 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)
7432 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7434 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7435 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)
7436 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7438 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7439 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)
7440 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7442 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7444 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7445 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)
7446 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7447 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7448 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7450 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7451 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)
7452 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)
7453 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7454 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7456 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7457 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7458 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7459 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7460 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7461 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7463 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7464 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7465 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)
7467 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7468 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7469 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7473 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7474 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7475 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7476 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7477 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7478 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7481 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7483 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7484 gtk_widget_show_all(menubar);
7486 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7488 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7490 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7493 if (prefs_common.toolbar_detachable) {
7494 handlebox = gtk_handle_box_new();
7496 handlebox = gtk_hbox_new(FALSE, 0);
7498 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7500 gtk_widget_realize(handlebox);
7502 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7505 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7509 vbox2 = gtk_vbox_new(FALSE, 2);
7510 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7511 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7514 notebook = gtk_notebook_new();
7515 gtk_widget_set_size_request(notebook, -1, prefs_common.compose_notebook_height);
7516 gtk_widget_show(notebook);
7518 /* header labels and entries */
7519 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7520 compose_create_header(compose),
7521 gtk_label_new_with_mnemonic(_("Hea_der")));
7522 /* attachment list */
7523 attach_hbox = gtk_hbox_new(FALSE, 0);
7524 gtk_widget_show(attach_hbox);
7526 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7527 gtk_widget_show(attach_lab1);
7528 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7530 attach_lab2 = gtk_label_new("");
7531 gtk_widget_show(attach_lab2);
7532 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7534 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7535 compose_create_attach(compose),
7538 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7539 compose_create_others(compose),
7540 gtk_label_new_with_mnemonic(_("Othe_rs")));
7543 subject_hbox = gtk_hbox_new(FALSE, 0);
7544 gtk_widget_show(subject_hbox);
7546 subject_frame = gtk_frame_new(NULL);
7547 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7548 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7549 gtk_widget_show(subject_frame);
7551 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7552 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7553 gtk_widget_show(subject);
7555 label = gtk_label_new(_("Subject:"));
7556 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7557 gtk_widget_show(label);
7560 subject_entry = claws_spell_entry_new();
7562 subject_entry = gtk_entry_new();
7564 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7565 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7566 G_CALLBACK(compose_grab_focus_cb), compose);
7567 gtk_widget_show(subject_entry);
7568 compose->subject_entry = subject_entry;
7569 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7571 edit_vbox = gtk_vbox_new(FALSE, 0);
7573 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7576 ruler_hbox = gtk_hbox_new(FALSE, 0);
7577 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7579 ruler = gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL);
7580 gtk_shruler_set_range(GTK_SHRULER(ruler), 0.0, 100.0, 1.0);
7581 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7585 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7586 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7587 GTK_POLICY_AUTOMATIC,
7588 GTK_POLICY_AUTOMATIC);
7589 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7591 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7593 text = gtk_text_view_new();
7594 if (prefs_common.show_compose_margin) {
7595 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7596 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7598 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7599 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7600 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7601 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7602 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7604 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7605 g_signal_connect(G_OBJECT(notebook), "size_allocate",
7606 G_CALLBACK(compose_notebook_size_alloc), compose);
7607 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7608 G_CALLBACK(compose_edit_size_alloc),
7610 g_signal_connect(G_OBJECT(buffer), "changed",
7611 G_CALLBACK(compose_changed_cb), compose);
7612 g_signal_connect(G_OBJECT(text), "grab_focus",
7613 G_CALLBACK(compose_grab_focus_cb), compose);
7614 g_signal_connect(G_OBJECT(buffer), "insert_text",
7615 G_CALLBACK(text_inserted), compose);
7616 g_signal_connect(G_OBJECT(text), "button_press_event",
7617 G_CALLBACK(text_clicked), compose);
7619 g_signal_connect(G_OBJECT(text), "popup-menu",
7620 G_CALLBACK(compose_popup_menu), compose);
7622 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7623 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7624 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7625 G_CALLBACK(compose_popup_menu), compose);
7627 g_signal_connect(G_OBJECT(subject_entry), "changed",
7628 G_CALLBACK(compose_changed_cb), compose);
7631 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7632 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7633 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7634 g_signal_connect(G_OBJECT(text), "drag_data_received",
7635 G_CALLBACK(compose_insert_drag_received_cb),
7637 g_signal_connect(G_OBJECT(text), "drag-drop",
7638 G_CALLBACK(compose_drag_drop),
7640 g_signal_connect(G_OBJECT(text), "key-press-event",
7641 G_CALLBACK(completion_set_focus_to_subject),
7643 gtk_widget_show_all(vbox);
7645 /* pane between attach clist and text */
7646 paned = gtk_vpaned_new();
7647 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7649 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7650 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7652 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7654 gtk_paned_add1(GTK_PANED(paned), notebook);
7655 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7656 gtk_widget_show_all(paned);
7659 if (prefs_common.textfont) {
7660 PangoFontDescription *font_desc;
7662 font_desc = pango_font_description_from_string
7663 (prefs_common.textfont);
7665 gtk_widget_modify_font(text, font_desc);
7666 pango_font_description_free(font_desc);
7670 gtk_action_group_add_actions(action_group, compose_popup_entries,
7671 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7672 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7673 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7674 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7675 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7676 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7677 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7679 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7681 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7682 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7683 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7685 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7687 undostruct = undo_init(text);
7688 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7691 address_completion_start(window);
7693 compose->window = window;
7694 compose->vbox = vbox;
7695 compose->menubar = menubar;
7696 compose->handlebox = handlebox;
7698 compose->vbox2 = vbox2;
7700 compose->paned = paned;
7702 compose->attach_label = attach_lab2;
7704 compose->notebook = notebook;
7705 compose->edit_vbox = edit_vbox;
7706 compose->ruler_hbox = ruler_hbox;
7707 compose->ruler = ruler;
7708 compose->scrolledwin = scrolledwin;
7709 compose->text = text;
7711 compose->focused_editable = NULL;
7713 compose->popupmenu = popupmenu;
7715 compose->tmpl_menu = tmpl_menu;
7717 compose->mode = mode;
7718 compose->rmode = mode;
7720 compose->targetinfo = NULL;
7721 compose->replyinfo = NULL;
7722 compose->fwdinfo = NULL;
7724 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7725 g_str_equal, (GDestroyNotify) g_free, NULL);
7727 compose->replyto = NULL;
7729 compose->bcc = NULL;
7730 compose->followup_to = NULL;
7732 compose->ml_post = NULL;
7734 compose->inreplyto = NULL;
7735 compose->references = NULL;
7736 compose->msgid = NULL;
7737 compose->boundary = NULL;
7739 compose->autowrap = prefs_common.autowrap;
7740 compose->autoindent = prefs_common.auto_indent;
7741 compose->use_signing = FALSE;
7742 compose->use_encryption = FALSE;
7743 compose->privacy_system = NULL;
7745 compose->modified = FALSE;
7747 compose->return_receipt = FALSE;
7749 compose->to_list = NULL;
7750 compose->newsgroup_list = NULL;
7752 compose->undostruct = undostruct;
7754 compose->sig_str = NULL;
7756 compose->exteditor_file = NULL;
7757 compose->exteditor_pid = -1;
7758 compose->exteditor_tag = -1;
7759 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7762 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7763 if (mode != COMPOSE_REDIRECT) {
7764 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7765 strcmp(prefs_common.dictionary, "")) {
7766 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7767 prefs_common.alt_dictionary,
7768 conv_get_locale_charset_str(),
7769 prefs_common.misspelled_col,
7770 prefs_common.check_while_typing,
7771 prefs_common.recheck_when_changing_dict,
7772 prefs_common.use_alternate,
7773 prefs_common.use_both_dicts,
7774 GTK_TEXT_VIEW(text),
7775 GTK_WINDOW(compose->window),
7776 compose_dict_changed,
7777 compose_spell_menu_changed,
7780 alertpanel_error(_("Spell checker could not "
7782 gtkaspell_checkers_strerror());
7783 gtkaspell_checkers_reset_error();
7785 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7789 compose->gtkaspell = gtkaspell;
7790 compose_spell_menu_changed(compose);
7791 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7794 compose_select_account(compose, account, TRUE);
7796 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7797 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7799 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7800 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7802 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7803 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7805 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7806 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7808 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7809 if (account->protocol != A_NNTP)
7810 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7811 prefs_common_translated_header_name("To:"));
7813 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7814 prefs_common_translated_header_name("Newsgroups:"));
7816 #ifndef USE_NEW_ADDRBOOK
7817 addressbook_set_target_compose(compose);
7819 if (mode != COMPOSE_REDIRECT)
7820 compose_set_template_menu(compose);
7822 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7825 compose_list = g_list_append(compose_list, compose);
7827 if (!prefs_common.show_ruler)
7828 gtk_widget_hide(ruler_hbox);
7830 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7833 compose->priority = PRIORITY_NORMAL;
7834 compose_update_priority_menu_item(compose);
7836 compose_set_out_encoding(compose);
7839 compose_update_actions_menu(compose);
7841 /* Privacy Systems menu */
7842 compose_update_privacy_systems_menu(compose);
7844 activate_privacy_system(compose, account, TRUE);
7845 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7847 gtk_widget_realize(window);
7849 gtk_widget_show(window);
7851 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7852 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7859 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7864 GtkWidget *optmenubox;
7867 GtkWidget *from_name = NULL;
7868 #if !(GTK_CHECK_VERSION(2,12,0))
7869 GtkTooltips *tips = compose->tooltips;
7872 gint num = 0, def_menu = 0;
7874 accounts = account_get_list();
7875 cm_return_val_if_fail(accounts != NULL, NULL);
7877 optmenubox = gtk_event_box_new();
7878 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7879 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7881 hbox = gtk_hbox_new(FALSE, 6);
7882 from_name = gtk_entry_new();
7884 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7885 G_CALLBACK(compose_grab_focus_cb), compose);
7887 for (; accounts != NULL; accounts = accounts->next, num++) {
7888 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7889 gchar *name, *from = NULL;
7891 if (ac == compose->account) def_menu = num;
7893 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7896 if (ac == compose->account) {
7897 if (ac->name && *ac->name) {
7899 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7900 from = g_strdup_printf("%s <%s>",
7902 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7904 from = g_strdup_printf("%s",
7906 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7909 COMBOBOX_ADD(menu, name, ac->account_id);
7914 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7916 g_signal_connect(G_OBJECT(optmenu), "changed",
7917 G_CALLBACK(account_activated),
7919 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7920 G_CALLBACK(compose_entry_popup_extend),
7923 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7924 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7926 CLAWS_SET_TIP(optmenubox,
7927 _("Account to use for this email"));
7928 CLAWS_SET_TIP(from_name,
7929 _("Sender address to be used"));
7931 compose->account_combo = optmenu;
7932 compose->from_name = from_name;
7937 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7939 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7940 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7941 Compose *compose = (Compose *) data;
7943 compose->priority = value;
7947 static void compose_reply_change_mode(Compose *compose,
7950 gboolean was_modified = compose->modified;
7952 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7954 cm_return_if_fail(compose->replyinfo != NULL);
7956 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7958 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7960 if (action == COMPOSE_REPLY_TO_ALL)
7962 if (action == COMPOSE_REPLY_TO_SENDER)
7964 if (action == COMPOSE_REPLY_TO_LIST)
7967 compose_remove_header_entries(compose);
7968 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7969 if (compose->account->set_autocc && compose->account->auto_cc)
7970 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7972 if (compose->account->set_autobcc && compose->account->auto_bcc)
7973 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7975 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7976 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7977 compose_show_first_last_header(compose, TRUE);
7978 compose->modified = was_modified;
7979 compose_set_title(compose);
7982 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7984 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7985 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7986 Compose *compose = (Compose *) data;
7989 compose_reply_change_mode(compose, value);
7992 static void compose_update_priority_menu_item(Compose * compose)
7994 GtkWidget *menuitem = NULL;
7995 switch (compose->priority) {
7996 case PRIORITY_HIGHEST:
7997 menuitem = gtk_ui_manager_get_widget
7998 (compose->ui_manager, "/Menu/Options/Priority/Highest");
8001 menuitem = gtk_ui_manager_get_widget
8002 (compose->ui_manager, "/Menu/Options/Priority/High");
8004 case PRIORITY_NORMAL:
8005 menuitem = gtk_ui_manager_get_widget
8006 (compose->ui_manager, "/Menu/Options/Priority/Normal");
8009 menuitem = gtk_ui_manager_get_widget
8010 (compose->ui_manager, "/Menu/Options/Priority/Low");
8012 case PRIORITY_LOWEST:
8013 menuitem = gtk_ui_manager_get_widget
8014 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
8017 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8020 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
8022 Compose *compose = (Compose *) data;
8024 gboolean can_sign = FALSE, can_encrypt = FALSE;
8026 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
8028 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
8031 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
8032 g_free(compose->privacy_system);
8033 compose->privacy_system = NULL;
8034 if (systemid != NULL) {
8035 compose->privacy_system = g_strdup(systemid);
8037 can_sign = privacy_system_can_sign(systemid);
8038 can_encrypt = privacy_system_can_encrypt(systemid);
8041 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
8043 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8044 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8047 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
8049 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8050 GtkWidget *menuitem = NULL;
8051 GList *children, *amenu;
8052 gboolean can_sign = FALSE, can_encrypt = FALSE;
8053 gboolean found = FALSE;
8055 if (compose->privacy_system != NULL) {
8057 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8058 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
8059 cm_return_if_fail(menuitem != NULL);
8061 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
8064 while (amenu != NULL) {
8065 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
8066 if (systemid != NULL) {
8067 if (strcmp(systemid, compose->privacy_system) == 0 &&
8068 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8069 menuitem = GTK_WIDGET(amenu->data);
8071 can_sign = privacy_system_can_sign(systemid);
8072 can_encrypt = privacy_system_can_encrypt(systemid);
8076 } else if (strlen(compose->privacy_system) == 0 &&
8077 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8078 menuitem = GTK_WIDGET(amenu->data);
8081 can_encrypt = FALSE;
8086 amenu = amenu->next;
8088 g_list_free(children);
8089 if (menuitem != NULL)
8090 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8092 if (warn && !found && strlen(compose->privacy_system)) {
8093 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8094 "will not be able to sign or encrypt this message."),
8095 compose->privacy_system);
8099 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8100 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8103 static void compose_set_out_encoding(Compose *compose)
8105 CharSet out_encoding;
8106 const gchar *branch = NULL;
8107 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
8109 switch(out_encoding) {
8110 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8111 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
8112 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
8113 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
8114 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
8115 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
8116 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
8117 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
8118 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
8119 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
8120 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
8121 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
8122 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
8123 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
8124 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
8125 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
8126 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
8127 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
8128 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
8129 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
8130 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
8131 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
8132 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
8133 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
8134 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
8135 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
8136 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
8137 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
8138 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
8139 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
8140 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
8141 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
8142 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8144 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8147 static void compose_set_template_menu(Compose *compose)
8149 GSList *tmpl_list, *cur;
8153 tmpl_list = template_get_config();
8155 menu = gtk_menu_new();
8157 gtk_menu_set_accel_group (GTK_MENU (menu),
8158 gtk_ui_manager_get_accel_group(compose->ui_manager));
8159 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8160 Template *tmpl = (Template *)cur->data;
8161 gchar *accel_path = NULL;
8162 item = gtk_menu_item_new_with_label(tmpl->name);
8163 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8164 g_signal_connect(G_OBJECT(item), "activate",
8165 G_CALLBACK(compose_template_activate_cb),
8167 g_object_set_data(G_OBJECT(item), "template", tmpl);
8168 gtk_widget_show(item);
8169 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8170 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8174 gtk_widget_show(menu);
8175 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8178 void compose_update_actions_menu(Compose *compose)
8180 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8183 static void compose_update_privacy_systems_menu(Compose *compose)
8185 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8186 GSList *systems, *cur;
8188 GtkWidget *system_none;
8190 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8191 GtkWidget *privacy_menu = gtk_menu_new();
8193 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8194 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8196 g_signal_connect(G_OBJECT(system_none), "activate",
8197 G_CALLBACK(compose_set_privacy_system_cb), compose);
8199 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8200 gtk_widget_show(system_none);
8202 systems = privacy_get_system_ids();
8203 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8204 gchar *systemid = cur->data;
8206 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8207 widget = gtk_radio_menu_item_new_with_label(group,
8208 privacy_system_get_name(systemid));
8209 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8210 g_strdup(systemid), g_free);
8211 g_signal_connect(G_OBJECT(widget), "activate",
8212 G_CALLBACK(compose_set_privacy_system_cb), compose);
8214 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8215 gtk_widget_show(widget);
8218 g_slist_free(systems);
8219 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8220 gtk_widget_show_all(privacy_menu);
8221 gtk_widget_show_all(privacy_menuitem);
8224 void compose_reflect_prefs_all(void)
8229 for (cur = compose_list; cur != NULL; cur = cur->next) {
8230 compose = (Compose *)cur->data;
8231 compose_set_template_menu(compose);
8235 void compose_reflect_prefs_pixmap_theme(void)
8240 for (cur = compose_list; cur != NULL; cur = cur->next) {
8241 compose = (Compose *)cur->data;
8242 toolbar_update(TOOLBAR_COMPOSE, compose);
8246 static const gchar *compose_quote_char_from_context(Compose *compose)
8248 const gchar *qmark = NULL;
8250 cm_return_val_if_fail(compose != NULL, NULL);
8252 switch (compose->mode) {
8253 /* use forward-specific quote char */
8254 case COMPOSE_FORWARD:
8255 case COMPOSE_FORWARD_AS_ATTACH:
8256 case COMPOSE_FORWARD_INLINE:
8257 if (compose->folder && compose->folder->prefs &&
8258 compose->folder->prefs->forward_with_format)
8259 qmark = compose->folder->prefs->forward_quotemark;
8260 else if (compose->account->forward_with_format)
8261 qmark = compose->account->forward_quotemark;
8263 qmark = prefs_common.fw_quotemark;
8266 /* use reply-specific quote char in all other modes */
8268 if (compose->folder && compose->folder->prefs &&
8269 compose->folder->prefs->reply_with_format)
8270 qmark = compose->folder->prefs->reply_quotemark;
8271 else if (compose->account->reply_with_format)
8272 qmark = compose->account->reply_quotemark;
8274 qmark = prefs_common.quotemark;
8278 if (qmark == NULL || *qmark == '\0')
8284 static void compose_template_apply(Compose *compose, Template *tmpl,
8288 GtkTextBuffer *buffer;
8292 gchar *parsed_str = NULL;
8293 gint cursor_pos = 0;
8294 const gchar *err_msg = _("The body of the template has an error at line %d.");
8297 /* process the body */
8299 text = GTK_TEXT_VIEW(compose->text);
8300 buffer = gtk_text_view_get_buffer(text);
8303 qmark = compose_quote_char_from_context(compose);
8305 if (compose->replyinfo != NULL) {
8308 gtk_text_buffer_set_text(buffer, "", -1);
8309 mark = gtk_text_buffer_get_insert(buffer);
8310 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8312 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8313 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8315 } else if (compose->fwdinfo != NULL) {
8318 gtk_text_buffer_set_text(buffer, "", -1);
8319 mark = gtk_text_buffer_get_insert(buffer);
8320 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8322 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8323 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8326 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8328 GtkTextIter start, end;
8331 gtk_text_buffer_get_start_iter(buffer, &start);
8332 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8333 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8335 /* clear the buffer now */
8337 gtk_text_buffer_set_text(buffer, "", -1);
8339 parsed_str = compose_quote_fmt(compose, dummyinfo,
8340 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8341 procmsg_msginfo_free( dummyinfo );
8347 gtk_text_buffer_set_text(buffer, "", -1);
8348 mark = gtk_text_buffer_get_insert(buffer);
8349 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8352 if (replace && parsed_str && compose->account->auto_sig)
8353 compose_insert_sig(compose, FALSE);
8355 if (replace && parsed_str) {
8356 gtk_text_buffer_get_start_iter(buffer, &iter);
8357 gtk_text_buffer_place_cursor(buffer, &iter);
8361 cursor_pos = quote_fmt_get_cursor_pos();
8362 compose->set_cursor_pos = cursor_pos;
8363 if (cursor_pos == -1)
8365 gtk_text_buffer_get_start_iter(buffer, &iter);
8366 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8367 gtk_text_buffer_place_cursor(buffer, &iter);
8370 /* process the other fields */
8372 compose_template_apply_fields(compose, tmpl);
8373 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8374 quote_fmt_reset_vartable();
8375 compose_changed_cb(NULL, compose);
8378 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8379 gtkaspell_highlight_all(compose->gtkaspell);
8383 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8385 MsgInfo* dummyinfo = NULL;
8386 MsgInfo *msginfo = NULL;
8389 if (compose->replyinfo != NULL)
8390 msginfo = compose->replyinfo;
8391 else if (compose->fwdinfo != NULL)
8392 msginfo = compose->fwdinfo;
8394 dummyinfo = compose_msginfo_new_from_compose(compose);
8395 msginfo = dummyinfo;
8398 if (tmpl->from && *tmpl->from != '\0') {
8400 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8401 compose->gtkaspell);
8403 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8405 quote_fmt_scan_string(tmpl->from);
8408 buf = quote_fmt_get_buffer();
8410 alertpanel_error(_("Template From format error."));
8412 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8416 if (tmpl->to && *tmpl->to != '\0') {
8418 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8419 compose->gtkaspell);
8421 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8423 quote_fmt_scan_string(tmpl->to);
8426 buf = quote_fmt_get_buffer();
8428 alertpanel_error(_("Template To format error."));
8430 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8434 if (tmpl->cc && *tmpl->cc != '\0') {
8436 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8437 compose->gtkaspell);
8439 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8441 quote_fmt_scan_string(tmpl->cc);
8444 buf = quote_fmt_get_buffer();
8446 alertpanel_error(_("Template Cc format error."));
8448 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8452 if (tmpl->bcc && *tmpl->bcc != '\0') {
8454 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8455 compose->gtkaspell);
8457 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8459 quote_fmt_scan_string(tmpl->bcc);
8462 buf = quote_fmt_get_buffer();
8464 alertpanel_error(_("Template Bcc format error."));
8466 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8470 /* process the subject */
8471 if (tmpl->subject && *tmpl->subject != '\0') {
8473 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8474 compose->gtkaspell);
8476 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8478 quote_fmt_scan_string(tmpl->subject);
8481 buf = quote_fmt_get_buffer();
8483 alertpanel_error(_("Template subject format error."));
8485 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8489 procmsg_msginfo_free( dummyinfo );
8492 static void compose_destroy(Compose *compose)
8494 GtkAllocation allocation;
8495 GtkTextBuffer *buffer;
8496 GtkClipboard *clipboard;
8498 compose_list = g_list_remove(compose_list, compose);
8500 if (compose->updating) {
8501 debug_print("danger, not destroying anything now\n");
8502 compose->deferred_destroy = TRUE;
8505 /* NOTE: address_completion_end() does nothing with the window
8506 * however this may change. */
8507 address_completion_end(compose->window);
8509 slist_free_strings(compose->to_list);
8510 g_slist_free(compose->to_list);
8511 slist_free_strings(compose->newsgroup_list);
8512 g_slist_free(compose->newsgroup_list);
8513 slist_free_strings(compose->header_list);
8514 g_slist_free(compose->header_list);
8516 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8518 g_hash_table_destroy(compose->email_hashtable);
8520 procmsg_msginfo_free(compose->targetinfo);
8521 procmsg_msginfo_free(compose->replyinfo);
8522 procmsg_msginfo_free(compose->fwdinfo);
8524 g_free(compose->replyto);
8525 g_free(compose->cc);
8526 g_free(compose->bcc);
8527 g_free(compose->newsgroups);
8528 g_free(compose->followup_to);
8530 g_free(compose->ml_post);
8532 g_free(compose->inreplyto);
8533 g_free(compose->references);
8534 g_free(compose->msgid);
8535 g_free(compose->boundary);
8537 g_free(compose->redirect_filename);
8538 if (compose->undostruct)
8539 undo_destroy(compose->undostruct);
8541 g_free(compose->sig_str);
8543 g_free(compose->exteditor_file);
8545 g_free(compose->orig_charset);
8547 g_free(compose->privacy_system);
8549 #ifndef USE_NEW_ADDRBOOK
8550 if (addressbook_get_target_compose() == compose)
8551 addressbook_set_target_compose(NULL);
8554 if (compose->gtkaspell) {
8555 gtkaspell_delete(compose->gtkaspell);
8556 compose->gtkaspell = NULL;
8560 if (!compose->batch) {
8561 gtk_widget_get_allocation(compose->window, &allocation);
8562 prefs_common.compose_width = allocation.width;
8563 prefs_common.compose_height = allocation.height;
8566 if (!gtk_widget_get_parent(compose->paned))
8567 gtk_widget_destroy(compose->paned);
8568 gtk_widget_destroy(compose->popupmenu);
8570 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8571 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8572 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8574 gtk_widget_destroy(compose->window);
8575 toolbar_destroy(compose->toolbar);
8576 g_free(compose->toolbar);
8577 g_mutex_free(compose->mutex);
8581 static void compose_attach_info_free(AttachInfo *ainfo)
8583 g_free(ainfo->file);
8584 g_free(ainfo->content_type);
8585 g_free(ainfo->name);
8586 g_free(ainfo->charset);
8590 static void compose_attach_update_label(Compose *compose)
8595 GtkTreeModel *model;
8600 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8601 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8602 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8606 while(gtk_tree_model_iter_next(model, &iter))
8609 text = g_strdup_printf("(%d)", i);
8610 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8614 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8616 Compose *compose = (Compose *)data;
8617 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8618 GtkTreeSelection *selection;
8620 GtkTreeModel *model;
8622 selection = gtk_tree_view_get_selection(tree_view);
8623 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8628 for (cur = sel; cur != NULL; cur = cur->next) {
8629 GtkTreePath *path = cur->data;
8630 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8633 gtk_tree_path_free(path);
8636 for (cur = sel; cur != NULL; cur = cur->next) {
8637 GtkTreeRowReference *ref = cur->data;
8638 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8641 if (gtk_tree_model_get_iter(model, &iter, path))
8642 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8644 gtk_tree_path_free(path);
8645 gtk_tree_row_reference_free(ref);
8649 compose_attach_update_label(compose);
8652 static struct _AttachProperty
8655 GtkWidget *mimetype_entry;
8656 GtkWidget *encoding_optmenu;
8657 GtkWidget *path_entry;
8658 GtkWidget *filename_entry;
8660 GtkWidget *cancel_btn;
8663 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8665 gtk_tree_path_free((GtkTreePath *)ptr);
8668 static void compose_attach_property(GtkAction *action, gpointer data)
8670 Compose *compose = (Compose *)data;
8671 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8673 GtkComboBox *optmenu;
8674 GtkTreeSelection *selection;
8676 GtkTreeModel *model;
8679 static gboolean cancelled;
8681 /* only if one selected */
8682 selection = gtk_tree_view_get_selection(tree_view);
8683 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8686 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8690 path = (GtkTreePath *) sel->data;
8691 gtk_tree_model_get_iter(model, &iter, path);
8692 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8695 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8701 if (!attach_prop.window)
8702 compose_attach_property_create(&cancelled);
8703 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8704 gtk_widget_grab_focus(attach_prop.ok_btn);
8705 gtk_widget_show(attach_prop.window);
8706 gtk_window_set_transient_for(GTK_WINDOW(attach_prop.window),
8707 GTK_WINDOW(compose->window));
8709 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8710 if (ainfo->encoding == ENC_UNKNOWN)
8711 combobox_select_by_data(optmenu, ENC_BASE64);
8713 combobox_select_by_data(optmenu, ainfo->encoding);
8715 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8716 ainfo->content_type ? ainfo->content_type : "");
8717 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8718 ainfo->file ? ainfo->file : "");
8719 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8720 ainfo->name ? ainfo->name : "");
8723 const gchar *entry_text;
8725 gchar *cnttype = NULL;
8732 gtk_widget_hide(attach_prop.window);
8733 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8738 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8739 if (*entry_text != '\0') {
8742 text = g_strstrip(g_strdup(entry_text));
8743 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8744 cnttype = g_strdup(text);
8747 alertpanel_error(_("Invalid MIME type."));
8753 ainfo->encoding = combobox_get_active_data(optmenu);
8755 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8756 if (*entry_text != '\0') {
8757 if (is_file_exist(entry_text) &&
8758 (size = get_file_size(entry_text)) > 0)
8759 file = g_strdup(entry_text);
8762 (_("File doesn't exist or is empty."));
8768 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8769 if (*entry_text != '\0') {
8770 g_free(ainfo->name);
8771 ainfo->name = g_strdup(entry_text);
8775 g_free(ainfo->content_type);
8776 ainfo->content_type = cnttype;
8779 g_free(ainfo->file);
8783 ainfo->size = (goffset)size;
8785 /* update tree store */
8786 text = to_human_readable(ainfo->size);
8787 gtk_tree_model_get_iter(model, &iter, path);
8788 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8789 COL_MIMETYPE, ainfo->content_type,
8791 COL_NAME, ainfo->name,
8792 COL_CHARSET, ainfo->charset,
8798 gtk_tree_path_free(path);
8801 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8803 label = gtk_label_new(str); \
8804 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8805 GTK_FILL, 0, 0, 0); \
8806 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8808 entry = gtk_entry_new(); \
8809 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8810 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8813 static void compose_attach_property_create(gboolean *cancelled)
8819 GtkWidget *mimetype_entry;
8822 GtkListStore *optmenu_menu;
8823 GtkWidget *path_entry;
8824 GtkWidget *filename_entry;
8827 GtkWidget *cancel_btn;
8828 GList *mime_type_list, *strlist;
8831 debug_print("Creating attach_property window...\n");
8833 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8834 gtk_widget_set_size_request(window, 480, -1);
8835 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8836 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8837 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8838 g_signal_connect(G_OBJECT(window), "delete_event",
8839 G_CALLBACK(attach_property_delete_event),
8841 g_signal_connect(G_OBJECT(window), "key_press_event",
8842 G_CALLBACK(attach_property_key_pressed),
8845 vbox = gtk_vbox_new(FALSE, 8);
8846 gtk_container_add(GTK_CONTAINER(window), vbox);
8848 table = gtk_table_new(4, 2, FALSE);
8849 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8850 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8851 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8853 label = gtk_label_new(_("MIME type"));
8854 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8856 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8857 mimetype_entry = gtk_combo_box_entry_new_text();
8858 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8859 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8861 /* stuff with list */
8862 mime_type_list = procmime_get_mime_type_list();
8864 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8865 MimeType *type = (MimeType *) mime_type_list->data;
8868 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8870 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8873 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8874 (GCompareFunc)strcmp2);
8877 for (mime_type_list = strlist; mime_type_list != NULL;
8878 mime_type_list = mime_type_list->next) {
8879 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8880 g_free(mime_type_list->data);
8882 g_list_free(strlist);
8883 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8884 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8886 label = gtk_label_new(_("Encoding"));
8887 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8889 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8891 hbox = gtk_hbox_new(FALSE, 0);
8892 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8893 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8895 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8896 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8898 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8899 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8900 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8901 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8902 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8904 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8906 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8907 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8909 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8910 &ok_btn, GTK_STOCK_OK,
8912 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8913 gtk_widget_grab_default(ok_btn);
8915 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8916 G_CALLBACK(attach_property_ok),
8918 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8919 G_CALLBACK(attach_property_cancel),
8922 gtk_widget_show_all(vbox);
8924 attach_prop.window = window;
8925 attach_prop.mimetype_entry = mimetype_entry;
8926 attach_prop.encoding_optmenu = optmenu;
8927 attach_prop.path_entry = path_entry;
8928 attach_prop.filename_entry = filename_entry;
8929 attach_prop.ok_btn = ok_btn;
8930 attach_prop.cancel_btn = cancel_btn;
8933 #undef SET_LABEL_AND_ENTRY
8935 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8941 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8947 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8948 gboolean *cancelled)
8956 static gboolean attach_property_key_pressed(GtkWidget *widget,
8958 gboolean *cancelled)
8960 if (event && event->keyval == GDK_KEY_Escape) {
8964 if (event && event->keyval == GDK_KEY_Return) {
8972 static void compose_exec_ext_editor(Compose *compose)
8979 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8980 G_DIR_SEPARATOR, compose);
8982 if (pipe(pipe_fds) < 0) {
8988 if ((pid = fork()) < 0) {
8995 /* close the write side of the pipe */
8998 compose->exteditor_file = g_strdup(tmp);
8999 compose->exteditor_pid = pid;
9001 compose_set_ext_editor_sensitive(compose, FALSE);
9004 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
9006 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
9008 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
9012 } else { /* process-monitoring process */
9018 /* close the read side of the pipe */
9021 if (compose_write_body_to_file(compose, tmp) < 0) {
9022 fd_write_all(pipe_fds[1], "2\n", 2);
9026 pid_ed = compose_exec_ext_editor_real(tmp);
9028 fd_write_all(pipe_fds[1], "1\n", 2);
9032 /* wait until editor is terminated */
9033 waitpid(pid_ed, NULL, 0);
9035 fd_write_all(pipe_fds[1], "0\n", 2);
9042 #endif /* G_OS_UNIX */
9046 static gint compose_exec_ext_editor_real(const gchar *file)
9053 cm_return_val_if_fail(file != NULL, -1);
9055 if ((pid = fork()) < 0) {
9060 if (pid != 0) return pid;
9062 /* grandchild process */
9064 if (setpgid(0, getppid()))
9067 if (prefs_common_get_ext_editor_cmd() &&
9068 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
9069 *(p + 1) == 's' && !strchr(p + 2, '%')) {
9070 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
9072 if (prefs_common_get_ext_editor_cmd())
9073 g_warning("External editor command-line is invalid: '%s'\n",
9074 prefs_common_get_ext_editor_cmd());
9075 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
9078 cmdline = strsplit_with_quote(buf, " ", 1024);
9079 execvp(cmdline[0], cmdline);
9082 g_strfreev(cmdline);
9087 static gboolean compose_ext_editor_kill(Compose *compose)
9089 pid_t pgid = compose->exteditor_pid * -1;
9092 ret = kill(pgid, 0);
9094 if (ret == 0 || (ret == -1 && EPERM == errno)) {
9098 msg = g_strdup_printf
9099 (_("The external editor is still working.\n"
9100 "Force terminating the process?\n"
9101 "process group id: %d"), -pgid);
9102 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
9103 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
9107 if (val == G_ALERTALTERNATE) {
9108 g_source_remove(compose->exteditor_tag);
9109 g_io_channel_shutdown(compose->exteditor_ch,
9111 g_io_channel_unref(compose->exteditor_ch);
9113 if (kill(pgid, SIGTERM) < 0) perror("kill");
9114 waitpid(compose->exteditor_pid, NULL, 0);
9116 g_warning("Terminated process group id: %d", -pgid);
9117 g_warning("Temporary file: %s",
9118 compose->exteditor_file);
9120 compose_set_ext_editor_sensitive(compose, TRUE);
9122 g_free(compose->exteditor_file);
9123 compose->exteditor_file = NULL;
9124 compose->exteditor_pid = -1;
9125 compose->exteditor_ch = NULL;
9126 compose->exteditor_tag = -1;
9134 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
9138 Compose *compose = (Compose *)data;
9141 debug_print("Compose: input from monitoring process\n");
9143 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
9145 g_io_channel_shutdown(source, FALSE, NULL);
9146 g_io_channel_unref(source);
9148 waitpid(compose->exteditor_pid, NULL, 0);
9150 if (buf[0] == '0') { /* success */
9151 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9152 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9154 gtk_text_buffer_set_text(buffer, "", -1);
9155 compose_insert_file(compose, compose->exteditor_file);
9156 compose_changed_cb(NULL, compose);
9157 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9159 if (claws_unlink(compose->exteditor_file) < 0)
9160 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9161 } else if (buf[0] == '1') { /* failed */
9162 g_warning("Couldn't exec external editor\n");
9163 if (claws_unlink(compose->exteditor_file) < 0)
9164 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9165 } else if (buf[0] == '2') {
9166 g_warning("Couldn't write to file\n");
9167 } else if (buf[0] == '3') {
9168 g_warning("Pipe read failed\n");
9171 compose_set_ext_editor_sensitive(compose, TRUE);
9173 g_free(compose->exteditor_file);
9174 compose->exteditor_file = NULL;
9175 compose->exteditor_pid = -1;
9176 compose->exteditor_ch = NULL;
9177 compose->exteditor_tag = -1;
9182 static void compose_set_ext_editor_sensitive(Compose *compose,
9185 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9186 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9187 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9188 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9189 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9190 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9191 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9193 gtk_widget_set_sensitive(compose->text, sensitive);
9194 if (compose->toolbar->send_btn)
9195 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
9196 if (compose->toolbar->sendl_btn)
9197 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
9198 if (compose->toolbar->draft_btn)
9199 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
9200 if (compose->toolbar->insert_btn)
9201 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
9202 if (compose->toolbar->sig_btn)
9203 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
9204 if (compose->toolbar->exteditor_btn)
9205 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9206 if (compose->toolbar->linewrap_current_btn)
9207 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9208 if (compose->toolbar->linewrap_all_btn)
9209 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9211 #endif /* G_OS_UNIX */
9214 * compose_undo_state_changed:
9216 * Change the sensivity of the menuentries undo and redo
9218 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9219 gint redo_state, gpointer data)
9221 Compose *compose = (Compose *)data;
9223 switch (undo_state) {
9224 case UNDO_STATE_TRUE:
9225 if (!undostruct->undo_state) {
9226 undostruct->undo_state = TRUE;
9227 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9230 case UNDO_STATE_FALSE:
9231 if (undostruct->undo_state) {
9232 undostruct->undo_state = FALSE;
9233 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9236 case UNDO_STATE_UNCHANGED:
9238 case UNDO_STATE_REFRESH:
9239 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9242 g_warning("Undo state not recognized");
9246 switch (redo_state) {
9247 case UNDO_STATE_TRUE:
9248 if (!undostruct->redo_state) {
9249 undostruct->redo_state = TRUE;
9250 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9253 case UNDO_STATE_FALSE:
9254 if (undostruct->redo_state) {
9255 undostruct->redo_state = FALSE;
9256 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9259 case UNDO_STATE_UNCHANGED:
9261 case UNDO_STATE_REFRESH:
9262 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9265 g_warning("Redo state not recognized");
9270 /* callback functions */
9272 static void compose_notebook_size_alloc(GtkNotebook *notebook,
9273 GtkAllocation *allocation,
9276 prefs_common.compose_notebook_height = allocation->height;
9279 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9280 * includes "non-client" (windows-izm) in calculation, so this calculation
9281 * may not be accurate.
9283 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9284 GtkAllocation *allocation,
9285 GtkSHRuler *shruler)
9287 if (prefs_common.show_ruler) {
9288 gint char_width = 0, char_height = 0;
9289 gint line_width_in_chars;
9291 gtkut_get_font_size(GTK_WIDGET(widget),
9292 &char_width, &char_height);
9293 line_width_in_chars =
9294 (allocation->width - allocation->x) / char_width;
9296 /* got the maximum */
9297 gtk_shruler_set_range(GTK_SHRULER(shruler),
9298 0.0, line_width_in_chars, 0);
9307 ComposePrefType type;
9308 gboolean entry_marked;
9311 static void account_activated(GtkComboBox *optmenu, gpointer data)
9313 Compose *compose = (Compose *)data;
9316 gchar *folderidentifier;
9317 gint account_id = 0;
9320 GSList *list, *saved_list = NULL;
9321 HeaderEntryState *state;
9322 GtkRcStyle *style = NULL;
9323 static GdkColor yellow;
9324 static gboolean color_set = FALSE;
9326 /* Get ID of active account in the combo box */
9327 menu = gtk_combo_box_get_model(optmenu);
9328 gtk_combo_box_get_active_iter(optmenu, &iter);
9329 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9331 ac = account_find_from_id(account_id);
9332 cm_return_if_fail(ac != NULL);
9334 if (ac != compose->account) {
9335 compose_select_account(compose, ac, FALSE);
9337 for (list = compose->header_list; list; list = list->next) {
9338 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9340 if (hentry->type == PREF_ACCOUNT || !list->next) {
9341 compose_destroy_headerentry(compose, hentry);
9345 state = g_malloc0(sizeof(HeaderEntryState));
9346 state->header = gtk_editable_get_chars(GTK_EDITABLE(
9347 gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9348 state->entry = gtk_editable_get_chars(
9349 GTK_EDITABLE(hentry->entry), 0, -1);
9350 state->type = hentry->type;
9353 gdk_color_parse("#f5f6be", &yellow);
9354 color_set = gdk_colormap_alloc_color(
9355 gdk_colormap_get_system(),
9356 &yellow, FALSE, TRUE);
9359 style = gtk_widget_get_modifier_style(hentry->entry);
9360 state->entry_marked = gdk_color_equal(&yellow,
9361 &style->base[GTK_STATE_NORMAL]);
9363 saved_list = g_slist_append(saved_list, state);
9364 compose_destroy_headerentry(compose, hentry);
9367 compose->header_last = NULL;
9368 g_slist_free(compose->header_list);
9369 compose->header_list = NULL;
9370 compose->header_nextrow = 1;
9371 compose_create_header_entry(compose);
9373 if (ac->set_autocc && ac->auto_cc)
9374 compose_entry_append(compose, ac->auto_cc,
9375 COMPOSE_CC, PREF_ACCOUNT);
9377 if (ac->set_autobcc && ac->auto_bcc)
9378 compose_entry_append(compose, ac->auto_bcc,
9379 COMPOSE_BCC, PREF_ACCOUNT);
9381 if (ac->set_autoreplyto && ac->auto_replyto)
9382 compose_entry_append(compose, ac->auto_replyto,
9383 COMPOSE_REPLYTO, PREF_ACCOUNT);
9385 for (list = saved_list; list; list = list->next) {
9386 state = (HeaderEntryState *) list->data;
9388 compose_add_header_entry(compose, state->header,
9389 state->entry, state->type);
9390 if (state->entry_marked)
9391 compose_entry_mark_default_to(compose, state->entry);
9393 g_free(state->header);
9394 g_free(state->entry);
9397 g_slist_free(saved_list);
9399 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9400 (ac->protocol == A_NNTP) ?
9401 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9404 /* Set message save folder */
9405 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9406 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9408 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9409 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9411 compose_set_save_to(compose, NULL);
9412 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9413 folderidentifier = folder_item_get_identifier(account_get_special_folder
9414 (compose->account, F_OUTBOX));
9415 compose_set_save_to(compose, folderidentifier);
9416 g_free(folderidentifier);
9420 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9421 GtkTreeViewColumn *column, Compose *compose)
9423 compose_attach_property(NULL, compose);
9426 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9429 Compose *compose = (Compose *)data;
9430 GtkTreeSelection *attach_selection;
9431 gint attach_nr_selected;
9433 if (!event) return FALSE;
9435 if (event->button == 3) {
9436 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9437 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9439 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", (attach_nr_selected > 0));
9440 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", (attach_nr_selected > 0));
9442 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9443 NULL, NULL, event->button, event->time);
9450 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9453 Compose *compose = (Compose *)data;
9455 if (!event) return FALSE;
9457 switch (event->keyval) {
9458 case GDK_KEY_Delete:
9459 compose_attach_remove_selected(NULL, compose);
9465 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9467 toolbar_comp_set_sensitive(compose, allow);
9468 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9469 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9471 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9473 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9474 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9475 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9477 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9481 static void compose_send_cb(GtkAction *action, gpointer data)
9483 Compose *compose = (Compose *)data;
9485 if (prefs_common.work_offline &&
9486 !inc_offline_should_override(TRUE,
9487 _("Claws Mail needs network access in order "
9488 "to send this email.")))
9491 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9492 g_source_remove(compose->draft_timeout_tag);
9493 compose->draft_timeout_tag = -1;
9496 compose_send(compose);
9499 static void compose_send_later_cb(GtkAction *action, gpointer data)
9501 Compose *compose = (Compose *)data;
9505 compose_allow_user_actions(compose, FALSE);
9506 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9507 compose_allow_user_actions(compose, TRUE);
9511 compose_close(compose);
9512 } else if (val == -1) {
9513 alertpanel_error(_("Could not queue message."));
9514 } else if (val == -2) {
9515 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9516 } else if (val == -3) {
9517 if (privacy_peek_error())
9518 alertpanel_error(_("Could not queue message for sending:\n\n"
9519 "Signature failed: %s"), privacy_get_error());
9520 } else if (val == -4) {
9521 alertpanel_error(_("Could not queue message for sending:\n\n"
9522 "Charset conversion failed."));
9523 } else if (val == -5) {
9524 alertpanel_error(_("Could not queue message for sending:\n\n"
9525 "Couldn't get recipient encryption key."));
9526 } else if (val == -6) {
9529 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9532 #define DRAFTED_AT_EXIT "drafted_at_exit"
9533 static void compose_register_draft(MsgInfo *info)
9535 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9536 DRAFTED_AT_EXIT, NULL);
9537 FILE *fp = g_fopen(filepath, "ab");
9540 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9548 gboolean compose_draft (gpointer data, guint action)
9550 Compose *compose = (Compose *)data;
9555 MsgFlags flag = {0, 0};
9556 static gboolean lock = FALSE;
9557 MsgInfo *newmsginfo;
9559 gboolean target_locked = FALSE;
9560 gboolean err = FALSE;
9562 if (lock) return FALSE;
9564 if (compose->sending)
9567 draft = account_get_special_folder(compose->account, F_DRAFT);
9568 cm_return_val_if_fail(draft != NULL, FALSE);
9570 if (!g_mutex_trylock(compose->mutex)) {
9571 /* we don't want to lock the mutex once it's available,
9572 * because as the only other part of compose.c locking
9573 * it is compose_close - which means once unlocked,
9574 * the compose struct will be freed */
9575 debug_print("couldn't lock mutex, probably sending\n");
9581 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9582 G_DIR_SEPARATOR, compose);
9583 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9584 FILE_OP_ERROR(tmp, "fopen");
9588 /* chmod for security */
9589 if (change_file_mode_rw(fp, tmp) < 0) {
9590 FILE_OP_ERROR(tmp, "chmod");
9591 g_warning("can't change file mode\n");
9594 /* Save draft infos */
9595 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9596 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9598 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9599 gchar *savefolderid;
9601 savefolderid = compose_get_save_to(compose);
9602 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9603 g_free(savefolderid);
9605 if (compose->return_receipt) {
9606 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9608 if (compose->privacy_system) {
9609 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9610 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9611 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9614 /* Message-ID of message replying to */
9615 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9618 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9619 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9622 /* Message-ID of message forwarding to */
9623 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9626 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9627 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9631 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9632 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9634 sheaders = compose_get_manual_headers_info(compose);
9635 err |= (fprintf(fp, "X-Claws-Manual-Headers:%s\n", sheaders) < 0);
9638 /* end of headers */
9639 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9646 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9650 if (fclose(fp) == EOF) {
9654 if (compose->targetinfo) {
9655 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9656 flag.perm_flags = target_locked?MSG_LOCKED:0;
9658 flag.tmp_flags = MSG_DRAFT;
9660 folder_item_scan(draft);
9661 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9662 MsgInfo *tmpinfo = NULL;
9663 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9664 if (compose->msgid) {
9665 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9668 msgnum = tmpinfo->msgnum;
9669 procmsg_msginfo_free(tmpinfo);
9670 debug_print("got draft msgnum %d from scanning\n", msgnum);
9672 debug_print("didn't get draft msgnum after scanning\n");
9675 debug_print("got draft msgnum %d from adding\n", msgnum);
9681 if (action != COMPOSE_AUTO_SAVE) {
9682 if (action != COMPOSE_DRAFT_FOR_EXIT)
9683 alertpanel_error(_("Could not save draft."));
9686 gtkut_window_popup(compose->window);
9687 val = alertpanel_full(_("Could not save draft"),
9688 _("Could not save draft.\n"
9689 "Do you want to cancel exit or discard this email?"),
9690 _("_Cancel exit"), _("_Discard email"), NULL,
9691 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9692 if (val == G_ALERTALTERNATE) {
9694 g_mutex_unlock(compose->mutex); /* must be done before closing */
9695 compose_close(compose);
9699 g_mutex_unlock(compose->mutex); /* must be done before closing */
9708 if (compose->mode == COMPOSE_REEDIT) {
9709 compose_remove_reedit_target(compose, TRUE);
9712 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9715 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9717 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9719 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9720 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9721 procmsg_msginfo_set_flags(newmsginfo, 0,
9722 MSG_HAS_ATTACHMENT);
9724 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9725 compose_register_draft(newmsginfo);
9727 procmsg_msginfo_free(newmsginfo);
9730 folder_item_scan(draft);
9732 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9734 g_mutex_unlock(compose->mutex); /* must be done before closing */
9735 compose_close(compose);
9741 path = folder_item_fetch_msg(draft, msgnum);
9743 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9746 if (g_stat(path, &s) < 0) {
9747 FILE_OP_ERROR(path, "stat");
9753 procmsg_msginfo_free(compose->targetinfo);
9754 compose->targetinfo = procmsg_msginfo_new();
9755 compose->targetinfo->msgnum = msgnum;
9756 compose->targetinfo->size = (goffset)s.st_size;
9757 compose->targetinfo->mtime = s.st_mtime;
9758 compose->targetinfo->folder = draft;
9760 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9761 compose->mode = COMPOSE_REEDIT;
9763 if (action == COMPOSE_AUTO_SAVE) {
9764 compose->autosaved_draft = compose->targetinfo;
9766 compose->modified = FALSE;
9767 compose_set_title(compose);
9771 g_mutex_unlock(compose->mutex);
9775 void compose_clear_exit_drafts(void)
9777 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9778 DRAFTED_AT_EXIT, NULL);
9779 if (is_file_exist(filepath))
9780 claws_unlink(filepath);
9785 void compose_reopen_exit_drafts(void)
9787 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9788 DRAFTED_AT_EXIT, NULL);
9789 FILE *fp = g_fopen(filepath, "rb");
9793 while (fgets(buf, sizeof(buf), fp)) {
9794 gchar **parts = g_strsplit(buf, "\t", 2);
9795 const gchar *folder = parts[0];
9796 int msgnum = parts[1] ? atoi(parts[1]):-1;
9798 if (folder && *folder && msgnum > -1) {
9799 FolderItem *item = folder_find_item_from_identifier(folder);
9800 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9802 compose_reedit(info, FALSE);
9809 compose_clear_exit_drafts();
9812 static void compose_save_cb(GtkAction *action, gpointer data)
9814 Compose *compose = (Compose *)data;
9815 compose_draft(compose, COMPOSE_KEEP_EDITING);
9816 compose->rmode = COMPOSE_REEDIT;
9819 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9821 if (compose && file_list) {
9824 for ( tmp = file_list; tmp; tmp = tmp->next) {
9825 gchar *file = (gchar *) tmp->data;
9826 gchar *utf8_filename = conv_filename_to_utf8(file);
9827 compose_attach_append(compose, file, utf8_filename, NULL, NULL);
9828 compose_changed_cb(NULL, compose);
9833 g_free(utf8_filename);
9838 static void compose_attach_cb(GtkAction *action, gpointer data)
9840 Compose *compose = (Compose *)data;
9843 if (compose->redirect_filename != NULL)
9846 /* Set focus_window properly, in case we were called via popup menu,
9847 * which unsets it (via focus_out_event callback on compose window). */
9848 manage_window_focus_in(compose->window, NULL, NULL);
9850 file_list = filesel_select_multiple_files_open(_("Select file"));
9853 compose_attach_from_list(compose, file_list, TRUE);
9854 g_list_free(file_list);
9858 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9860 Compose *compose = (Compose *)data;
9862 gint files_inserted = 0;
9864 file_list = filesel_select_multiple_files_open(_("Select file"));
9869 for ( tmp = file_list; tmp; tmp = tmp->next) {
9870 gchar *file = (gchar *) tmp->data;
9871 gchar *filedup = g_strdup(file);
9872 gchar *shortfile = g_path_get_basename(filedup);
9873 ComposeInsertResult res;
9874 /* insert the file if the file is short or if the user confirmed that
9875 he/she wants to insert the large file */
9876 res = compose_insert_file(compose, file);
9877 if (res == COMPOSE_INSERT_READ_ERROR) {
9878 alertpanel_error(_("File '%s' could not be read."), shortfile);
9879 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9880 alertpanel_error(_("File '%s' contained invalid characters\n"
9881 "for the current encoding, insertion may be incorrect."),
9883 } else if (res == COMPOSE_INSERT_SUCCESS)
9890 g_list_free(file_list);
9894 if (files_inserted > 0 && compose->gtkaspell &&
9895 compose->gtkaspell->check_while_typing)
9896 gtkaspell_highlight_all(compose->gtkaspell);
9900 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9902 Compose *compose = (Compose *)data;
9904 compose_insert_sig(compose, FALSE);
9907 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9911 Compose *compose = (Compose *)data;
9913 gtkut_widget_get_uposition(widget, &x, &y);
9914 if (!compose->batch) {
9915 prefs_common.compose_x = x;
9916 prefs_common.compose_y = y;
9918 if (compose->sending || compose->updating)
9920 compose_close_cb(NULL, compose);
9924 void compose_close_toolbar(Compose *compose)
9926 compose_close_cb(NULL, compose);
9929 static void compose_close_cb(GtkAction *action, gpointer data)
9931 Compose *compose = (Compose *)data;
9935 if (compose->exteditor_tag != -1) {
9936 if (!compose_ext_editor_kill(compose))
9941 if (compose->modified) {
9942 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9943 if (!g_mutex_trylock(compose->mutex)) {
9944 /* we don't want to lock the mutex once it's available,
9945 * because as the only other part of compose.c locking
9946 * it is compose_close - which means once unlocked,
9947 * the compose struct will be freed */
9948 debug_print("couldn't lock mutex, probably sending\n");
9952 val = alertpanel(_("Discard message"),
9953 _("This message has been modified. Discard it?"),
9954 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9956 val = alertpanel(_("Save changes"),
9957 _("This message has been modified. Save the latest changes?"),
9958 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9960 g_mutex_unlock(compose->mutex);
9962 case G_ALERTDEFAULT:
9963 if (prefs_common.autosave && !reedit)
9964 compose_remove_draft(compose);
9966 case G_ALERTALTERNATE:
9967 compose_draft(data, COMPOSE_QUIT_EDITING);
9974 compose_close(compose);
9977 static void compose_print_cb(GtkAction *action, gpointer data)
9979 Compose *compose = (Compose *) data;
9981 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9982 if (compose->targetinfo)
9983 messageview_print(compose->targetinfo, FALSE, -1, -1, 0);
9986 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9988 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9989 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9990 Compose *compose = (Compose *) data;
9993 compose->out_encoding = (CharSet)value;
9996 static void compose_address_cb(GtkAction *action, gpointer data)
9998 Compose *compose = (Compose *)data;
10000 #ifndef USE_NEW_ADDRBOOK
10001 addressbook_open(compose);
10003 GError* error = NULL;
10004 addressbook_connect_signals(compose);
10005 addressbook_dbus_open(TRUE, &error);
10007 g_warning("%s", error->message);
10008 g_error_free(error);
10013 static void about_show_cb(GtkAction *action, gpointer data)
10018 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
10020 Compose *compose = (Compose *)data;
10025 tmpl = g_object_get_data(G_OBJECT(widget), "template");
10026 cm_return_if_fail(tmpl != NULL);
10028 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
10030 val = alertpanel(_("Apply template"), msg,
10031 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
10034 if (val == G_ALERTDEFAULT)
10035 compose_template_apply(compose, tmpl, TRUE);
10036 else if (val == G_ALERTALTERNATE)
10037 compose_template_apply(compose, tmpl, FALSE);
10040 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
10042 Compose *compose = (Compose *)data;
10044 compose_exec_ext_editor(compose);
10047 static void compose_undo_cb(GtkAction *action, gpointer data)
10049 Compose *compose = (Compose *)data;
10050 gboolean prev_autowrap = compose->autowrap;
10052 compose->autowrap = FALSE;
10053 undo_undo(compose->undostruct);
10054 compose->autowrap = prev_autowrap;
10057 static void compose_redo_cb(GtkAction *action, gpointer data)
10059 Compose *compose = (Compose *)data;
10060 gboolean prev_autowrap = compose->autowrap;
10062 compose->autowrap = FALSE;
10063 undo_redo(compose->undostruct);
10064 compose->autowrap = prev_autowrap;
10067 static void entry_cut_clipboard(GtkWidget *entry)
10069 if (GTK_IS_EDITABLE(entry))
10070 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
10071 else if (GTK_IS_TEXT_VIEW(entry))
10072 gtk_text_buffer_cut_clipboard(
10073 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10074 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
10078 static void entry_copy_clipboard(GtkWidget *entry)
10080 if (GTK_IS_EDITABLE(entry))
10081 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
10082 else if (GTK_IS_TEXT_VIEW(entry))
10083 gtk_text_buffer_copy_clipboard(
10084 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10085 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
10088 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
10089 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
10091 if (GTK_IS_TEXT_VIEW(entry)) {
10092 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10093 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
10094 GtkTextIter start_iter, end_iter;
10096 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
10098 if (contents == NULL)
10101 /* we shouldn't delete the selection when middle-click-pasting, or we
10102 * can't mid-click-paste our own selection */
10103 if (clip != GDK_SELECTION_PRIMARY) {
10104 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
10105 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
10108 if (insert_place == NULL) {
10109 /* if insert_place isn't specified, insert at the cursor.
10110 * used for Ctrl-V pasting */
10111 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10112 start = gtk_text_iter_get_offset(&start_iter);
10113 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
10115 /* if insert_place is specified, paste here.
10116 * used for mid-click-pasting */
10117 start = gtk_text_iter_get_offset(insert_place);
10118 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
10119 if (prefs_common.primary_paste_unselects)
10120 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
10124 /* paste unwrapped: mark the paste so it's not wrapped later */
10125 end = start + strlen(contents);
10126 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
10127 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
10128 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
10129 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
10130 /* rewrap paragraph now (after a mid-click-paste) */
10131 mark_start = gtk_text_buffer_get_insert(buffer);
10132 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10133 gtk_text_iter_backward_char(&start_iter);
10134 compose_beautify_paragraph(compose, &start_iter, TRUE);
10136 } else if (GTK_IS_EDITABLE(entry))
10137 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
10139 compose->modified = TRUE;
10142 static void entry_allsel(GtkWidget *entry)
10144 if (GTK_IS_EDITABLE(entry))
10145 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
10146 else if (GTK_IS_TEXT_VIEW(entry)) {
10147 GtkTextIter startiter, enditer;
10148 GtkTextBuffer *textbuf;
10150 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10151 gtk_text_buffer_get_start_iter(textbuf, &startiter);
10152 gtk_text_buffer_get_end_iter(textbuf, &enditer);
10154 gtk_text_buffer_move_mark_by_name(textbuf,
10155 "selection_bound", &startiter);
10156 gtk_text_buffer_move_mark_by_name(textbuf,
10157 "insert", &enditer);
10161 static void compose_cut_cb(GtkAction *action, gpointer data)
10163 Compose *compose = (Compose *)data;
10164 if (compose->focused_editable
10165 #ifndef GENERIC_UMPC
10166 && gtk_widget_has_focus(compose->focused_editable)
10169 entry_cut_clipboard(compose->focused_editable);
10172 static void compose_copy_cb(GtkAction *action, gpointer data)
10174 Compose *compose = (Compose *)data;
10175 if (compose->focused_editable
10176 #ifndef GENERIC_UMPC
10177 && gtk_widget_has_focus(compose->focused_editable)
10180 entry_copy_clipboard(compose->focused_editable);
10183 static void compose_paste_cb(GtkAction *action, gpointer data)
10185 Compose *compose = (Compose *)data;
10186 gint prev_autowrap;
10187 GtkTextBuffer *buffer;
10189 if (compose->focused_editable &&
10190 #ifndef GENERIC_UMPC
10191 gtk_widget_has_focus(compose->focused_editable)
10194 entry_paste_clipboard(compose, compose->focused_editable,
10195 prefs_common.linewrap_pastes,
10196 GDK_SELECTION_CLIPBOARD, NULL);
10201 #ifndef GENERIC_UMPC
10202 gtk_widget_has_focus(compose->text) &&
10204 compose->gtkaspell &&
10205 compose->gtkaspell->check_while_typing)
10206 gtkaspell_highlight_all(compose->gtkaspell);
10210 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10212 Compose *compose = (Compose *)data;
10213 gint wrap_quote = prefs_common.linewrap_quote;
10214 if (compose->focused_editable
10215 #ifndef GENERIC_UMPC
10216 && gtk_widget_has_focus(compose->focused_editable)
10219 /* let text_insert() (called directly or at a later time
10220 * after the gtk_editable_paste_clipboard) know that
10221 * text is to be inserted as a quotation. implemented
10222 * by using a simple refcount... */
10223 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10224 G_OBJECT(compose->focused_editable),
10225 "paste_as_quotation"));
10226 g_object_set_data(G_OBJECT(compose->focused_editable),
10227 "paste_as_quotation",
10228 GINT_TO_POINTER(paste_as_quotation + 1));
10229 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10230 entry_paste_clipboard(compose, compose->focused_editable,
10231 prefs_common.linewrap_pastes,
10232 GDK_SELECTION_CLIPBOARD, NULL);
10233 prefs_common.linewrap_quote = wrap_quote;
10237 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10239 Compose *compose = (Compose *)data;
10240 gint prev_autowrap;
10241 GtkTextBuffer *buffer;
10243 if (compose->focused_editable
10244 #ifndef GENERIC_UMPC
10245 && gtk_widget_has_focus(compose->focused_editable)
10248 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10249 GDK_SELECTION_CLIPBOARD, NULL);
10254 #ifndef GENERIC_UMPC
10255 gtk_widget_has_focus(compose->text) &&
10257 compose->gtkaspell &&
10258 compose->gtkaspell->check_while_typing)
10259 gtkaspell_highlight_all(compose->gtkaspell);
10263 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10265 Compose *compose = (Compose *)data;
10266 gint prev_autowrap;
10267 GtkTextBuffer *buffer;
10269 if (compose->focused_editable
10270 #ifndef GENERIC_UMPC
10271 && gtk_widget_has_focus(compose->focused_editable)
10274 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10275 GDK_SELECTION_CLIPBOARD, NULL);
10280 #ifndef GENERIC_UMPC
10281 gtk_widget_has_focus(compose->text) &&
10283 compose->gtkaspell &&
10284 compose->gtkaspell->check_while_typing)
10285 gtkaspell_highlight_all(compose->gtkaspell);
10289 static void compose_allsel_cb(GtkAction *action, gpointer data)
10291 Compose *compose = (Compose *)data;
10292 if (compose->focused_editable
10293 #ifndef GENERIC_UMPC
10294 && gtk_widget_has_focus(compose->focused_editable)
10297 entry_allsel(compose->focused_editable);
10300 static void textview_move_beginning_of_line (GtkTextView *text)
10302 GtkTextBuffer *buffer;
10306 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10308 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10309 mark = gtk_text_buffer_get_insert(buffer);
10310 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10311 gtk_text_iter_set_line_offset(&ins, 0);
10312 gtk_text_buffer_place_cursor(buffer, &ins);
10315 static void textview_move_forward_character (GtkTextView *text)
10317 GtkTextBuffer *buffer;
10321 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10323 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10324 mark = gtk_text_buffer_get_insert(buffer);
10325 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10326 if (gtk_text_iter_forward_cursor_position(&ins))
10327 gtk_text_buffer_place_cursor(buffer, &ins);
10330 static void textview_move_backward_character (GtkTextView *text)
10332 GtkTextBuffer *buffer;
10336 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10338 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10339 mark = gtk_text_buffer_get_insert(buffer);
10340 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10341 if (gtk_text_iter_backward_cursor_position(&ins))
10342 gtk_text_buffer_place_cursor(buffer, &ins);
10345 static void textview_move_forward_word (GtkTextView *text)
10347 GtkTextBuffer *buffer;
10352 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10354 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10355 mark = gtk_text_buffer_get_insert(buffer);
10356 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10357 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10358 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10359 gtk_text_iter_backward_word_start(&ins);
10360 gtk_text_buffer_place_cursor(buffer, &ins);
10364 static void textview_move_backward_word (GtkTextView *text)
10366 GtkTextBuffer *buffer;
10371 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10373 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10374 mark = gtk_text_buffer_get_insert(buffer);
10375 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10376 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10377 if (gtk_text_iter_backward_word_starts(&ins, 1))
10378 gtk_text_buffer_place_cursor(buffer, &ins);
10381 static void textview_move_end_of_line (GtkTextView *text)
10383 GtkTextBuffer *buffer;
10387 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10389 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10390 mark = gtk_text_buffer_get_insert(buffer);
10391 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10392 if (gtk_text_iter_forward_to_line_end(&ins))
10393 gtk_text_buffer_place_cursor(buffer, &ins);
10396 static void textview_move_next_line (GtkTextView *text)
10398 GtkTextBuffer *buffer;
10403 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10405 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10406 mark = gtk_text_buffer_get_insert(buffer);
10407 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10408 offset = gtk_text_iter_get_line_offset(&ins);
10409 if (gtk_text_iter_forward_line(&ins)) {
10410 gtk_text_iter_set_line_offset(&ins, offset);
10411 gtk_text_buffer_place_cursor(buffer, &ins);
10415 static void textview_move_previous_line (GtkTextView *text)
10417 GtkTextBuffer *buffer;
10422 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10424 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10425 mark = gtk_text_buffer_get_insert(buffer);
10426 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10427 offset = gtk_text_iter_get_line_offset(&ins);
10428 if (gtk_text_iter_backward_line(&ins)) {
10429 gtk_text_iter_set_line_offset(&ins, offset);
10430 gtk_text_buffer_place_cursor(buffer, &ins);
10434 static void textview_delete_forward_character (GtkTextView *text)
10436 GtkTextBuffer *buffer;
10438 GtkTextIter ins, end_iter;
10440 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10442 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10443 mark = gtk_text_buffer_get_insert(buffer);
10444 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10446 if (gtk_text_iter_forward_char(&end_iter)) {
10447 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10451 static void textview_delete_backward_character (GtkTextView *text)
10453 GtkTextBuffer *buffer;
10455 GtkTextIter ins, end_iter;
10457 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10459 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10460 mark = gtk_text_buffer_get_insert(buffer);
10461 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10463 if (gtk_text_iter_backward_char(&end_iter)) {
10464 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10468 static void textview_delete_forward_word (GtkTextView *text)
10470 GtkTextBuffer *buffer;
10472 GtkTextIter ins, end_iter;
10474 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10476 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10477 mark = gtk_text_buffer_get_insert(buffer);
10478 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10480 if (gtk_text_iter_forward_word_end(&end_iter)) {
10481 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10485 static void textview_delete_backward_word (GtkTextView *text)
10487 GtkTextBuffer *buffer;
10489 GtkTextIter ins, end_iter;
10491 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10493 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10494 mark = gtk_text_buffer_get_insert(buffer);
10495 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10497 if (gtk_text_iter_backward_word_start(&end_iter)) {
10498 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10502 static void textview_delete_line (GtkTextView *text)
10504 GtkTextBuffer *buffer;
10506 GtkTextIter ins, start_iter, end_iter;
10508 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10510 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10511 mark = gtk_text_buffer_get_insert(buffer);
10512 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10515 gtk_text_iter_set_line_offset(&start_iter, 0);
10518 if (gtk_text_iter_ends_line(&end_iter)){
10519 if (!gtk_text_iter_forward_char(&end_iter))
10520 gtk_text_iter_backward_char(&start_iter);
10523 gtk_text_iter_forward_to_line_end(&end_iter);
10524 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10527 static void textview_delete_to_line_end (GtkTextView *text)
10529 GtkTextBuffer *buffer;
10531 GtkTextIter ins, end_iter;
10533 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10535 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10536 mark = gtk_text_buffer_get_insert(buffer);
10537 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10539 if (gtk_text_iter_ends_line(&end_iter))
10540 gtk_text_iter_forward_char(&end_iter);
10542 gtk_text_iter_forward_to_line_end(&end_iter);
10543 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10546 #define DO_ACTION(name, act) { \
10547 if(!strcmp(name, a_name)) { \
10551 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10553 const gchar *a_name = gtk_action_get_name(action);
10554 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10555 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10556 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10557 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10558 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10559 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10560 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10561 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10562 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10563 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10564 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10565 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10566 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10567 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10571 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10573 Compose *compose = (Compose *)data;
10574 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10575 ComposeCallAdvancedAction action = -1;
10577 action = compose_call_advanced_action_from_path(gaction);
10580 void (*do_action) (GtkTextView *text);
10581 } action_table[] = {
10582 {textview_move_beginning_of_line},
10583 {textview_move_forward_character},
10584 {textview_move_backward_character},
10585 {textview_move_forward_word},
10586 {textview_move_backward_word},
10587 {textview_move_end_of_line},
10588 {textview_move_next_line},
10589 {textview_move_previous_line},
10590 {textview_delete_forward_character},
10591 {textview_delete_backward_character},
10592 {textview_delete_forward_word},
10593 {textview_delete_backward_word},
10594 {textview_delete_line},
10595 {textview_delete_to_line_end}
10598 if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
10600 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10601 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10602 if (action_table[action].do_action)
10603 action_table[action].do_action(text);
10605 g_warning("Not implemented yet.");
10609 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10611 GtkAllocation allocation;
10615 if (GTK_IS_EDITABLE(widget)) {
10616 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10617 gtk_editable_set_position(GTK_EDITABLE(widget),
10620 if ((parent = gtk_widget_get_parent(widget))
10621 && (parent = gtk_widget_get_parent(parent))
10622 && (parent = gtk_widget_get_parent(parent))) {
10623 if (GTK_IS_SCROLLED_WINDOW(parent)) {
10624 gtk_widget_get_allocation(widget, &allocation);
10625 gint y = allocation.y;
10626 gint height = allocation.height;
10627 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10628 (GTK_SCROLLED_WINDOW(parent));
10630 gfloat value = gtk_adjustment_get_value(shown);
10631 gfloat upper = gtk_adjustment_get_upper(shown);
10632 gfloat page_size = gtk_adjustment_get_page_size(shown);
10633 if (y < (int)value) {
10634 gtk_adjustment_set_value(shown, y - 1);
10636 if ((y + height) > ((int)value + (int)page_size)) {
10637 if ((y - height - 1) < ((int)upper - (int)page_size)) {
10638 gtk_adjustment_set_value(shown,
10639 y + height - (int)page_size - 1);
10641 gtk_adjustment_set_value(shown,
10642 (int)upper - (int)page_size - 1);
10649 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10650 compose->focused_editable = widget;
10652 #ifdef GENERIC_UMPC
10653 if (GTK_IS_TEXT_VIEW(widget)
10654 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10655 g_object_ref(compose->notebook);
10656 g_object_ref(compose->edit_vbox);
10657 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10658 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10659 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10660 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10661 g_object_unref(compose->notebook);
10662 g_object_unref(compose->edit_vbox);
10663 g_signal_handlers_block_by_func(G_OBJECT(widget),
10664 G_CALLBACK(compose_grab_focus_cb),
10666 gtk_widget_grab_focus(widget);
10667 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10668 G_CALLBACK(compose_grab_focus_cb),
10670 } else if (!GTK_IS_TEXT_VIEW(widget)
10671 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10672 g_object_ref(compose->notebook);
10673 g_object_ref(compose->edit_vbox);
10674 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10675 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10676 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10677 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10678 g_object_unref(compose->notebook);
10679 g_object_unref(compose->edit_vbox);
10680 g_signal_handlers_block_by_func(G_OBJECT(widget),
10681 G_CALLBACK(compose_grab_focus_cb),
10683 gtk_widget_grab_focus(widget);
10684 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10685 G_CALLBACK(compose_grab_focus_cb),
10691 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10693 compose->modified = TRUE;
10694 // compose_beautify_paragraph(compose, NULL, TRUE);
10695 #ifndef GENERIC_UMPC
10696 compose_set_title(compose);
10700 static void compose_wrap_cb(GtkAction *action, gpointer data)
10702 Compose *compose = (Compose *)data;
10703 compose_beautify_paragraph(compose, NULL, TRUE);
10706 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10708 Compose *compose = (Compose *)data;
10709 compose_wrap_all_full(compose, TRUE);
10712 static void compose_find_cb(GtkAction *action, gpointer data)
10714 Compose *compose = (Compose *)data;
10716 message_search_compose(compose);
10719 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10722 Compose *compose = (Compose *)data;
10723 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10724 if (compose->autowrap)
10725 compose_wrap_all_full(compose, TRUE);
10726 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10729 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10732 Compose *compose = (Compose *)data;
10733 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10736 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10738 Compose *compose = (Compose *)data;
10740 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10743 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10745 Compose *compose = (Compose *)data;
10747 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10750 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10752 g_free(compose->privacy_system);
10754 compose->privacy_system = g_strdup(account->default_privacy_system);
10755 compose_update_privacy_system_menu_item(compose, warn);
10758 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10760 Compose *compose = (Compose *)data;
10762 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10763 gtk_widget_show(compose->ruler_hbox);
10764 prefs_common.show_ruler = TRUE;
10766 gtk_widget_hide(compose->ruler_hbox);
10767 gtk_widget_queue_resize(compose->edit_vbox);
10768 prefs_common.show_ruler = FALSE;
10772 static void compose_attach_drag_received_cb (GtkWidget *widget,
10773 GdkDragContext *context,
10776 GtkSelectionData *data,
10779 gpointer user_data)
10781 Compose *compose = (Compose *)user_data;
10785 type = gtk_selection_data_get_data_type(data);
10786 if (((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
10788 || (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND"))
10790 ) && gtk_drag_get_source_widget(context) !=
10791 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10792 list = uri_list_extract_filenames(
10793 (const gchar *)gtk_selection_data_get_data(data));
10794 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10795 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10796 compose_attach_append
10797 (compose, (const gchar *)tmp->data,
10798 utf8_filename, NULL, NULL);
10799 g_free(utf8_filename);
10801 if (list) compose_changed_cb(NULL, compose);
10802 list_free_strings(list);
10804 } else if (gtk_drag_get_source_widget(context)
10805 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10806 /* comes from our summaryview */
10807 SummaryView * summaryview = NULL;
10808 GSList * list = NULL, *cur = NULL;
10810 if (mainwindow_get_mainwindow())
10811 summaryview = mainwindow_get_mainwindow()->summaryview;
10814 list = summary_get_selected_msg_list(summaryview);
10816 for (cur = list; cur; cur = cur->next) {
10817 MsgInfo *msginfo = (MsgInfo *)cur->data;
10818 gchar *file = NULL;
10820 file = procmsg_get_message_file_full(msginfo,
10823 compose_attach_append(compose, (const gchar *)file,
10824 (const gchar *)file, "message/rfc822", NULL);
10828 g_slist_free(list);
10832 static gboolean compose_drag_drop(GtkWidget *widget,
10833 GdkDragContext *drag_context,
10835 guint time, gpointer user_data)
10837 /* not handling this signal makes compose_insert_drag_received_cb
10842 static gboolean completion_set_focus_to_subject
10843 (GtkWidget *widget,
10844 GdkEventKey *event,
10847 cm_return_val_if_fail(compose != NULL, FALSE);
10849 /* make backtab move to subject field */
10850 if(event->keyval == GDK_KEY_ISO_Left_Tab) {
10851 gtk_widget_grab_focus(compose->subject_entry);
10857 static void compose_insert_drag_received_cb (GtkWidget *widget,
10858 GdkDragContext *drag_context,
10861 GtkSelectionData *data,
10864 gpointer user_data)
10866 Compose *compose = (Compose *)user_data;
10870 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10872 type = gtk_selection_data_get_data_type(data);
10874 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
10876 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND")) {
10878 AlertValue val = G_ALERTDEFAULT;
10879 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
10881 list = uri_list_extract_filenames(ddata);
10882 if (list == NULL && strstr(ddata, "://")) {
10883 /* Assume a list of no files, and data has ://, is a remote link */
10884 gchar *tmpdata = g_strstrip(g_strdup(ddata));
10885 gchar *tmpfile = get_tmp_file();
10886 str_write_to_file(tmpdata, tmpfile);
10888 compose_insert_file(compose, tmpfile);
10889 claws_unlink(tmpfile);
10891 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10892 compose_beautify_paragraph(compose, NULL, TRUE);
10895 switch (prefs_common.compose_dnd_mode) {
10896 case COMPOSE_DND_ASK:
10897 val = alertpanel_full(_("Insert or attach?"),
10898 _("Do you want to insert the contents of the file(s) "
10899 "into the message body, or attach it to the email?"),
10900 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10901 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10903 case COMPOSE_DND_INSERT:
10904 val = G_ALERTALTERNATE;
10906 case COMPOSE_DND_ATTACH:
10907 val = G_ALERTOTHER;
10910 /* unexpected case */
10911 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10914 if (val & G_ALERTDISABLE) {
10915 val &= ~G_ALERTDISABLE;
10916 /* remember what action to perform by default, only if we don't click Cancel */
10917 if (val == G_ALERTALTERNATE)
10918 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10919 else if (val == G_ALERTOTHER)
10920 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10923 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10924 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10925 list_free_strings(list);
10928 } else if (val == G_ALERTOTHER) {
10929 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10930 list_free_strings(list);
10935 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10936 compose_insert_file(compose, (const gchar *)tmp->data);
10938 list_free_strings(list);
10940 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10945 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10948 static void compose_header_drag_received_cb (GtkWidget *widget,
10949 GdkDragContext *drag_context,
10952 GtkSelectionData *data,
10955 gpointer user_data)
10957 GtkEditable *entry = (GtkEditable *)user_data;
10958 const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
10960 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10963 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10964 gchar *decoded=g_new(gchar, strlen(email));
10967 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
10968 gtk_editable_delete_text(entry, 0, -1);
10969 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10970 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10974 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10977 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10979 Compose *compose = (Compose *)data;
10981 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10982 compose->return_receipt = TRUE;
10984 compose->return_receipt = FALSE;
10987 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10989 Compose *compose = (Compose *)data;
10991 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10992 compose->remove_references = TRUE;
10994 compose->remove_references = FALSE;
10997 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10998 ComposeHeaderEntry *headerentry)
11000 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
11004 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
11005 GdkEventKey *event,
11006 ComposeHeaderEntry *headerentry)
11008 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
11009 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
11010 !(event->state & GDK_MODIFIER_MASK) &&
11011 (event->keyval == GDK_KEY_BackSpace) &&
11012 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
11013 gtk_container_remove
11014 (GTK_CONTAINER(headerentry->compose->header_table),
11015 headerentry->combo);
11016 gtk_container_remove
11017 (GTK_CONTAINER(headerentry->compose->header_table),
11018 headerentry->entry);
11019 headerentry->compose->header_list =
11020 g_slist_remove(headerentry->compose->header_list,
11022 g_free(headerentry);
11023 } else if (event->keyval == GDK_KEY_Tab) {
11024 if (headerentry->compose->header_last == headerentry) {
11025 /* Override default next focus, and give it to subject_entry
11026 * instead of notebook tabs
11028 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
11029 gtk_widget_grab_focus(headerentry->compose->subject_entry);
11036 static gboolean scroll_postpone(gpointer data)
11038 Compose *compose = (Compose *)data;
11040 cm_return_val_if_fail(!compose->batch, FALSE);
11042 GTK_EVENTS_FLUSH();
11043 compose_show_first_last_header(compose, FALSE);
11047 static void compose_headerentry_changed_cb(GtkWidget *entry,
11048 ComposeHeaderEntry *headerentry)
11050 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
11051 compose_create_header_entry(headerentry->compose);
11052 g_signal_handlers_disconnect_matched
11053 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
11054 0, 0, NULL, NULL, headerentry);
11056 if (!headerentry->compose->batch)
11057 g_timeout_add(0, scroll_postpone, headerentry->compose);
11061 static gboolean compose_defer_auto_save_draft(Compose *compose)
11063 compose->draft_timeout_tag = -1;
11064 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
11068 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
11070 GtkAdjustment *vadj;
11072 cm_return_if_fail(compose);
11073 cm_return_if_fail(!compose->batch);
11074 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
11075 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
11076 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11077 gtk_widget_get_parent(compose->header_table)));
11078 gtk_adjustment_set_value(vadj, (show_first ?
11079 gtk_adjustment_get_lower(vadj) :
11080 (gtk_adjustment_get_upper(vadj) -
11081 gtk_adjustment_get_page_size(vadj))));
11082 gtk_adjustment_changed(vadj);
11085 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
11086 const gchar *text, gint len, Compose *compose)
11088 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
11089 (G_OBJECT(compose->text), "paste_as_quotation"));
11092 cm_return_if_fail(text != NULL);
11094 g_signal_handlers_block_by_func(G_OBJECT(buffer),
11095 G_CALLBACK(text_inserted),
11097 if (paste_as_quotation) {
11099 const gchar *qmark;
11101 GtkTextIter start_iter;
11104 len = strlen(text);
11106 new_text = g_strndup(text, len);
11108 qmark = compose_quote_char_from_context(compose);
11110 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11111 gtk_text_buffer_place_cursor(buffer, iter);
11113 pos = gtk_text_iter_get_offset(iter);
11115 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
11116 _("Quote format error at line %d."));
11117 quote_fmt_reset_vartable();
11119 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
11120 GINT_TO_POINTER(paste_as_quotation - 1));
11122 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11123 gtk_text_buffer_place_cursor(buffer, iter);
11124 gtk_text_buffer_delete_mark(buffer, mark);
11126 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
11127 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
11128 compose_beautify_paragraph(compose, &start_iter, FALSE);
11129 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
11130 gtk_text_buffer_delete_mark(buffer, mark);
11132 if (strcmp(text, "\n") || compose->automatic_break
11133 || gtk_text_iter_starts_line(iter)) {
11134 GtkTextIter before_ins;
11135 gtk_text_buffer_insert(buffer, iter, text, len);
11136 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
11137 before_ins = *iter;
11138 gtk_text_iter_backward_chars(&before_ins, len);
11139 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
11142 /* check if the preceding is just whitespace or quote */
11143 GtkTextIter start_line;
11144 gchar *tmp = NULL, *quote = NULL;
11145 gint quote_len = 0, is_normal = 0;
11146 start_line = *iter;
11147 gtk_text_iter_set_line_offset(&start_line, 0);
11148 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
11151 if (*tmp == '\0') {
11154 quote = compose_get_quote_str(buffer, &start_line, "e_len);
11162 gtk_text_buffer_insert(buffer, iter, text, len);
11164 gtk_text_buffer_insert_with_tags_by_name(buffer,
11165 iter, text, len, "no_join", NULL);
11170 if (!paste_as_quotation) {
11171 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11172 compose_beautify_paragraph(compose, iter, FALSE);
11173 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11174 gtk_text_buffer_delete_mark(buffer, mark);
11177 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
11178 G_CALLBACK(text_inserted),
11180 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
11182 if (prefs_common.autosave &&
11183 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
11184 compose->draft_timeout_tag != -2 /* disabled while loading */)
11185 compose->draft_timeout_tag = g_timeout_add
11186 (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
11190 static void compose_check_all(GtkAction *action, gpointer data)
11192 Compose *compose = (Compose *)data;
11193 if (!compose->gtkaspell)
11196 if (gtk_widget_has_focus(compose->subject_entry))
11197 claws_spell_entry_check_all(
11198 CLAWS_SPELL_ENTRY(compose->subject_entry));
11200 gtkaspell_check_all(compose->gtkaspell);
11203 static void compose_highlight_all(GtkAction *action, gpointer data)
11205 Compose *compose = (Compose *)data;
11206 if (compose->gtkaspell) {
11207 claws_spell_entry_recheck_all(
11208 CLAWS_SPELL_ENTRY(compose->subject_entry));
11209 gtkaspell_highlight_all(compose->gtkaspell);
11213 static void compose_check_backwards(GtkAction *action, gpointer data)
11215 Compose *compose = (Compose *)data;
11216 if (!compose->gtkaspell) {
11217 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11221 if (gtk_widget_has_focus(compose->subject_entry))
11222 claws_spell_entry_check_backwards(
11223 CLAWS_SPELL_ENTRY(compose->subject_entry));
11225 gtkaspell_check_backwards(compose->gtkaspell);
11228 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11230 Compose *compose = (Compose *)data;
11231 if (!compose->gtkaspell) {
11232 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11236 if (gtk_widget_has_focus(compose->subject_entry))
11237 claws_spell_entry_check_forwards_go(
11238 CLAWS_SPELL_ENTRY(compose->subject_entry));
11240 gtkaspell_check_forwards_go(compose->gtkaspell);
11245 *\brief Guess originating forward account from MsgInfo and several
11246 * "common preference" settings. Return NULL if no guess.
11248 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11250 PrefsAccount *account = NULL;
11252 cm_return_val_if_fail(msginfo, NULL);
11253 cm_return_val_if_fail(msginfo->folder, NULL);
11254 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11256 if (msginfo->folder->prefs->enable_default_account)
11257 account = account_find_from_id(msginfo->folder->prefs->default_account);
11260 account = msginfo->folder->folder->account;
11262 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11264 Xstrdup_a(to, msginfo->to, return NULL);
11265 extract_address(to);
11266 account = account_find_from_address(to, FALSE);
11269 if (!account && prefs_common.forward_account_autosel) {
11270 gchar cc[BUFFSIZE];
11271 if (!procheader_get_header_from_msginfo
11272 (msginfo, cc,sizeof cc , "Cc:")) {
11273 gchar *buf = cc + strlen("Cc:");
11274 extract_address(buf);
11275 account = account_find_from_address(buf, FALSE);
11279 if (!account && prefs_common.forward_account_autosel) {
11280 gchar deliveredto[BUFFSIZE];
11281 if (!procheader_get_header_from_msginfo
11282 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
11283 gchar *buf = deliveredto + strlen("Delivered-To:");
11284 extract_address(buf);
11285 account = account_find_from_address(buf, FALSE);
11292 gboolean compose_close(Compose *compose)
11296 if (!g_mutex_trylock(compose->mutex)) {
11297 /* we have to wait for the (possibly deferred by auto-save)
11298 * drafting to be done, before destroying the compose under
11300 debug_print("waiting for drafting to finish...\n");
11301 compose_allow_user_actions(compose, FALSE);
11302 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11305 cm_return_val_if_fail(compose, FALSE);
11306 gtkut_widget_get_uposition(compose->window, &x, &y);
11307 if (!compose->batch) {
11308 prefs_common.compose_x = x;
11309 prefs_common.compose_y = y;
11311 g_mutex_unlock(compose->mutex);
11312 compose_destroy(compose);
11317 * Add entry field for each address in list.
11318 * \param compose E-Mail composition object.
11319 * \param listAddress List of (formatted) E-Mail addresses.
11321 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11324 node = listAddress;
11326 addr = ( gchar * ) node->data;
11327 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11328 node = g_list_next( node );
11332 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11333 guint action, gboolean opening_multiple)
11335 gchar *body = NULL;
11336 GSList *new_msglist = NULL;
11337 MsgInfo *tmp_msginfo = NULL;
11338 gboolean originally_enc = FALSE;
11339 gboolean originally_sig = FALSE;
11340 Compose *compose = NULL;
11341 gchar *s_system = NULL;
11343 cm_return_if_fail(msgview != NULL);
11345 cm_return_if_fail(msginfo_list != NULL);
11347 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11348 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11349 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11351 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11352 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11353 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11354 orig_msginfo, mimeinfo);
11355 if (tmp_msginfo != NULL) {
11356 new_msglist = g_slist_append(NULL, tmp_msginfo);
11358 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11359 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11360 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11362 tmp_msginfo->folder = orig_msginfo->folder;
11363 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11364 if (orig_msginfo->tags) {
11365 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11366 tmp_msginfo->folder->tags_dirty = TRUE;
11372 if (!opening_multiple)
11373 body = messageview_get_selection(msgview);
11376 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11377 procmsg_msginfo_free(tmp_msginfo);
11378 g_slist_free(new_msglist);
11380 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11382 if (compose && originally_enc) {
11383 compose_force_encryption(compose, compose->account, FALSE, s_system);
11386 if (compose && originally_sig && compose->account->default_sign_reply) {
11387 compose_force_signing(compose, compose->account, s_system);
11391 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11394 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11397 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11398 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11399 GSList *cur = msginfo_list;
11400 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11401 "messages. Opening the windows "
11402 "could take some time. Do you "
11403 "want to continue?"),
11404 g_slist_length(msginfo_list));
11405 if (g_slist_length(msginfo_list) > 9
11406 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11407 != G_ALERTALTERNATE) {
11412 /* We'll open multiple compose windows */
11413 /* let the WM place the next windows */
11414 compose_force_window_origin = FALSE;
11415 for (; cur; cur = cur->next) {
11417 tmplist.data = cur->data;
11418 tmplist.next = NULL;
11419 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11421 compose_force_window_origin = TRUE;
11423 /* forwarding multiple mails as attachments is done via a
11424 * single compose window */
11425 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11429 void compose_check_for_email_account(Compose *compose)
11431 PrefsAccount *ac = NULL, *curr = NULL;
11437 if (compose->account && compose->account->protocol == A_NNTP) {
11438 ac = account_get_cur_account();
11439 if (ac->protocol == A_NNTP) {
11440 list = account_get_list();
11442 for( ; list != NULL ; list = g_list_next(list)) {
11443 curr = (PrefsAccount *) list->data;
11444 if (curr->protocol != A_NNTP) {
11450 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11455 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo,
11456 const gchar *address)
11458 GSList *msginfo_list = NULL;
11459 gchar *body = messageview_get_selection(msgview);
11462 msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11464 compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11465 compose_check_for_email_account(compose);
11466 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11467 compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11468 compose_reply_set_subject(compose, msginfo);
11471 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11474 void compose_set_position(Compose *compose, gint pos)
11476 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11478 gtkut_text_view_set_position(text, pos);
11481 gboolean compose_search_string(Compose *compose,
11482 const gchar *str, gboolean case_sens)
11484 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11486 return gtkut_text_view_search_string(text, str, case_sens);
11489 gboolean compose_search_string_backward(Compose *compose,
11490 const gchar *str, gboolean case_sens)
11492 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11494 return gtkut_text_view_search_string_backward(text, str, case_sens);
11497 /* allocate a msginfo structure and populate its data from a compose data structure */
11498 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11500 MsgInfo *newmsginfo;
11502 gchar buf[BUFFSIZE];
11504 cm_return_val_if_fail( compose != NULL, NULL );
11506 newmsginfo = procmsg_msginfo_new();
11509 get_rfc822_date(buf, sizeof(buf));
11510 newmsginfo->date = g_strdup(buf);
11513 if (compose->from_name) {
11514 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11515 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11519 if (compose->subject_entry)
11520 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11522 /* to, cc, reply-to, newsgroups */
11523 for (list = compose->header_list; list; list = list->next) {
11524 gchar *header = gtk_editable_get_chars(
11526 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11527 gchar *entry = gtk_editable_get_chars(
11528 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11530 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11531 if ( newmsginfo->to == NULL ) {
11532 newmsginfo->to = g_strdup(entry);
11533 } else if (entry && *entry) {
11534 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11535 g_free(newmsginfo->to);
11536 newmsginfo->to = tmp;
11539 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11540 if ( newmsginfo->cc == NULL ) {
11541 newmsginfo->cc = g_strdup(entry);
11542 } else if (entry && *entry) {
11543 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11544 g_free(newmsginfo->cc);
11545 newmsginfo->cc = tmp;
11548 if ( strcasecmp(header,
11549 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11550 if ( newmsginfo->newsgroups == NULL ) {
11551 newmsginfo->newsgroups = g_strdup(entry);
11552 } else if (entry && *entry) {
11553 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11554 g_free(newmsginfo->newsgroups);
11555 newmsginfo->newsgroups = tmp;
11563 /* other data is unset */
11569 /* update compose's dictionaries from folder dict settings */
11570 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11571 FolderItem *folder_item)
11573 cm_return_if_fail(compose != NULL);
11575 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11576 FolderItemPrefs *prefs = folder_item->prefs;
11578 if (prefs->enable_default_dictionary)
11579 gtkaspell_change_dict(compose->gtkaspell,
11580 prefs->default_dictionary, FALSE);
11581 if (folder_item->prefs->enable_default_alt_dictionary)
11582 gtkaspell_change_alt_dict(compose->gtkaspell,
11583 prefs->default_alt_dictionary);
11584 if (prefs->enable_default_dictionary
11585 || prefs->enable_default_alt_dictionary)
11586 compose_spell_menu_changed(compose);