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 #if !GTK_CHECK_VERSION(2, 24, 0)
879 color[0] = quote_color1;
880 color[1] = quote_color2;
881 color[2] = quote_color3;
882 color[3] = quote_bgcolor1;
883 color[4] = quote_bgcolor2;
884 color[5] = quote_bgcolor3;
885 color[6] = signature_color;
886 color[7] = uri_color;
888 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
889 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
891 for (i = 0; i < 8; i++) {
892 if (success[i] == FALSE) {
893 g_warning("Compose: color allocation failed.\n");
894 quote_color1 = quote_color2 = quote_color3 =
895 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
896 signature_color = uri_color = black;
902 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
905 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
908 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
910 return compose_generic_new(account, mailto, item, NULL, NULL);
913 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
915 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
918 #define SCROLL_TO_CURSOR(compose) { \
919 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
920 gtk_text_view_get_buffer( \
921 GTK_TEXT_VIEW(compose->text))); \
922 gtk_text_view_scroll_mark_onscreen( \
923 GTK_TEXT_VIEW(compose->text), \
927 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
930 if (folderidentifier) {
931 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
932 prefs_common.compose_save_to_history = add_history(
933 prefs_common.compose_save_to_history, folderidentifier);
934 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
935 prefs_common.compose_save_to_history);
938 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
939 if (folderidentifier)
940 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
942 gtk_entry_set_text(GTK_ENTRY(entry), "");
945 static gchar *compose_get_save_to(Compose *compose)
948 gchar *result = NULL;
949 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
950 result = gtk_editable_get_chars(entry, 0, -1);
953 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
954 prefs_common.compose_save_to_history = add_history(
955 prefs_common.compose_save_to_history, result);
956 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
957 prefs_common.compose_save_to_history);
962 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
963 GList *attach_files, GList *listAddress )
966 GtkTextView *textview;
967 GtkTextBuffer *textbuf;
969 const gchar *subject_format = NULL;
970 const gchar *body_format = NULL;
971 gchar *mailto_from = NULL;
972 PrefsAccount *mailto_account = NULL;
973 MsgInfo* dummyinfo = NULL;
974 gint cursor_pos = -1;
975 MailField mfield = NO_FIELD_PRESENT;
979 /* check if mailto defines a from */
980 if (mailto && *mailto != '\0') {
981 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
982 /* mailto defines a from, check if we can get account prefs from it,
983 if not, the account prefs will be guessed using other ways, but we'll keep
986 mailto_account = account_find_from_address(mailto_from, TRUE);
988 account = mailto_account;
991 /* if no account prefs set from mailto, set if from folder prefs (if any) */
992 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
993 account = account_find_from_id(item->prefs->default_account);
995 /* if no account prefs set, fallback to the current one */
996 if (!account) account = cur_account;
997 cm_return_val_if_fail(account != NULL, NULL);
999 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1001 /* override from name if mailto asked for it */
1003 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1004 g_free(mailto_from);
1006 /* override from name according to folder properties */
1007 if (item && item->prefs &&
1008 item->prefs->compose_with_format &&
1009 item->prefs->compose_override_from_format &&
1010 *item->prefs->compose_override_from_format != '\0') {
1015 dummyinfo = compose_msginfo_new_from_compose(compose);
1017 /* decode \-escape sequences in the internal representation of the quote format */
1018 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1019 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1022 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1023 compose->gtkaspell);
1025 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1027 quote_fmt_scan_string(tmp);
1030 buf = quote_fmt_get_buffer();
1032 alertpanel_error(_("New message From format error."));
1034 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1035 quote_fmt_reset_vartable();
1040 compose->replyinfo = NULL;
1041 compose->fwdinfo = NULL;
1043 textview = GTK_TEXT_VIEW(compose->text);
1044 textbuf = gtk_text_view_get_buffer(textview);
1045 compose_create_tags(textview, compose);
1047 undo_block(compose->undostruct);
1049 compose_set_dictionaries_from_folder_prefs(compose, item);
1052 if (account->auto_sig)
1053 compose_insert_sig(compose, FALSE);
1054 gtk_text_buffer_get_start_iter(textbuf, &iter);
1055 gtk_text_buffer_place_cursor(textbuf, &iter);
1057 if (account->protocol != A_NNTP) {
1058 if (mailto && *mailto != '\0') {
1059 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1062 compose_set_folder_prefs(compose, item, TRUE);
1064 if (item && item->ret_rcpt) {
1065 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1068 if (mailto && *mailto != '\0') {
1069 if (!strchr(mailto, '@'))
1070 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1072 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1073 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1074 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1075 mfield = TO_FIELD_PRESENT;
1078 * CLAWS: just don't allow return receipt request, even if the user
1079 * may want to send an email. simple but foolproof.
1081 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1083 compose_add_field_list( compose, listAddress );
1085 if (item && item->prefs && item->prefs->compose_with_format) {
1086 subject_format = item->prefs->compose_subject_format;
1087 body_format = item->prefs->compose_body_format;
1088 } else if (account->compose_with_format) {
1089 subject_format = account->compose_subject_format;
1090 body_format = account->compose_body_format;
1091 } else if (prefs_common.compose_with_format) {
1092 subject_format = prefs_common.compose_subject_format;
1093 body_format = prefs_common.compose_body_format;
1096 if (subject_format || body_format) {
1099 && *subject_format != '\0' )
1101 gchar *subject = NULL;
1106 dummyinfo = compose_msginfo_new_from_compose(compose);
1108 /* decode \-escape sequences in the internal representation of the quote format */
1109 tmp = g_malloc(strlen(subject_format)+1);
1110 pref_get_unescaped_pref(tmp, subject_format);
1112 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1114 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1115 compose->gtkaspell);
1117 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1119 quote_fmt_scan_string(tmp);
1122 buf = quote_fmt_get_buffer();
1124 alertpanel_error(_("New message subject format error."));
1126 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1127 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1128 quote_fmt_reset_vartable();
1132 mfield = SUBJECT_FIELD_PRESENT;
1136 && *body_format != '\0' )
1139 GtkTextBuffer *buffer;
1140 GtkTextIter start, end;
1144 dummyinfo = compose_msginfo_new_from_compose(compose);
1146 text = GTK_TEXT_VIEW(compose->text);
1147 buffer = gtk_text_view_get_buffer(text);
1148 gtk_text_buffer_get_start_iter(buffer, &start);
1149 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1150 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1152 compose_quote_fmt(compose, dummyinfo,
1154 NULL, tmp, FALSE, TRUE,
1155 _("The body of the \"New message\" template has an error at line %d."));
1156 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1157 quote_fmt_reset_vartable();
1161 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1162 gtkaspell_highlight_all(compose->gtkaspell);
1164 mfield = BODY_FIELD_PRESENT;
1168 procmsg_msginfo_free( dummyinfo );
1174 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1175 ainfo = (AttachInfo *) curr->data;
1176 compose_attach_append(compose, ainfo->file, ainfo->name,
1177 ainfo->content_type, ainfo->charset);
1181 compose_show_first_last_header(compose, TRUE);
1183 /* Set save folder */
1184 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1185 gchar *folderidentifier;
1187 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1188 folderidentifier = folder_item_get_identifier(item);
1189 compose_set_save_to(compose, folderidentifier);
1190 g_free(folderidentifier);
1193 /* Place cursor according to provided input (mfield) */
1195 case NO_FIELD_PRESENT:
1196 if (compose->header_last)
1197 gtk_widget_grab_focus(compose->header_last->entry);
1199 case TO_FIELD_PRESENT:
1200 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1202 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1205 gtk_widget_grab_focus(compose->subject_entry);
1207 case SUBJECT_FIELD_PRESENT:
1208 textview = GTK_TEXT_VIEW(compose->text);
1211 textbuf = gtk_text_view_get_buffer(textview);
1214 mark = gtk_text_buffer_get_insert(textbuf);
1215 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1216 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1218 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1219 * only defers where it comes to the variable body
1220 * is not null. If no body is present compose->text
1221 * will be null in which case you cannot place the
1222 * cursor inside the component so. An empty component
1223 * is therefore created before placing the cursor
1225 case BODY_FIELD_PRESENT:
1226 cursor_pos = quote_fmt_get_cursor_pos();
1227 if (cursor_pos == -1)
1228 gtk_widget_grab_focus(compose->header_last->entry);
1230 gtk_widget_grab_focus(compose->text);
1234 undo_unblock(compose->undostruct);
1236 if (prefs_common.auto_exteditor)
1237 compose_exec_ext_editor(compose);
1239 compose->draft_timeout_tag = -1;
1240 SCROLL_TO_CURSOR(compose);
1242 compose->modified = FALSE;
1243 compose_set_title(compose);
1245 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1250 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1251 gboolean override_pref, const gchar *system)
1253 const gchar *privacy = NULL;
1255 cm_return_if_fail(compose != NULL);
1256 cm_return_if_fail(account != NULL);
1258 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1263 else if (account->default_privacy_system
1264 && strlen(account->default_privacy_system)) {
1265 privacy = account->default_privacy_system;
1267 GSList *privacy_avail = privacy_get_system_ids();
1268 if (privacy_avail && g_slist_length(privacy_avail)) {
1269 privacy = (gchar *)(privacy_avail->data);
1272 if (privacy != NULL) {
1274 g_free(compose->privacy_system);
1275 compose->privacy_system = NULL;
1277 if (compose->privacy_system == NULL)
1278 compose->privacy_system = g_strdup(privacy);
1279 else if (*(compose->privacy_system) == '\0') {
1280 g_free(compose->privacy_system);
1281 compose->privacy_system = g_strdup(privacy);
1283 compose_update_privacy_system_menu_item(compose, FALSE);
1284 compose_use_encryption(compose, TRUE);
1288 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1290 const gchar *privacy = NULL;
1294 else if (account->default_privacy_system
1295 && strlen(account->default_privacy_system)) {
1296 privacy = account->default_privacy_system;
1298 GSList *privacy_avail = privacy_get_system_ids();
1299 if (privacy_avail && g_slist_length(privacy_avail)) {
1300 privacy = (gchar *)(privacy_avail->data);
1304 if (privacy != NULL) {
1306 g_free(compose->privacy_system);
1307 compose->privacy_system = NULL;
1309 if (compose->privacy_system == NULL)
1310 compose->privacy_system = g_strdup(privacy);
1311 compose_update_privacy_system_menu_item(compose, FALSE);
1312 compose_use_signing(compose, TRUE);
1316 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1320 Compose *compose = NULL;
1322 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1324 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1325 cm_return_val_if_fail(msginfo != NULL, NULL);
1327 list_len = g_slist_length(msginfo_list);
1331 case COMPOSE_REPLY_TO_ADDRESS:
1332 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1333 FALSE, prefs_common.default_reply_list, FALSE, body);
1335 case COMPOSE_REPLY_WITH_QUOTE:
1336 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1337 FALSE, prefs_common.default_reply_list, FALSE, body);
1339 case COMPOSE_REPLY_WITHOUT_QUOTE:
1340 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1341 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1343 case COMPOSE_REPLY_TO_SENDER:
1344 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1345 FALSE, FALSE, TRUE, body);
1347 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1348 compose = compose_followup_and_reply_to(msginfo,
1349 COMPOSE_QUOTE_CHECK,
1350 FALSE, FALSE, body);
1352 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1353 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1354 FALSE, FALSE, TRUE, body);
1356 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1357 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1358 FALSE, FALSE, TRUE, NULL);
1360 case COMPOSE_REPLY_TO_ALL:
1361 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1362 TRUE, FALSE, FALSE, body);
1364 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1365 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1366 TRUE, FALSE, FALSE, body);
1368 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1369 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1370 TRUE, FALSE, FALSE, NULL);
1372 case COMPOSE_REPLY_TO_LIST:
1373 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1374 FALSE, TRUE, FALSE, body);
1376 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1377 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1378 FALSE, TRUE, FALSE, body);
1380 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1381 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1382 FALSE, TRUE, FALSE, NULL);
1384 case COMPOSE_FORWARD:
1385 if (prefs_common.forward_as_attachment) {
1386 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1389 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1393 case COMPOSE_FORWARD_INLINE:
1394 /* check if we reply to more than one Message */
1395 if (list_len == 1) {
1396 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1399 /* more messages FALL THROUGH */
1400 case COMPOSE_FORWARD_AS_ATTACH:
1401 compose = compose_forward_multiple(NULL, msginfo_list);
1403 case COMPOSE_REDIRECT:
1404 compose = compose_redirect(NULL, msginfo, FALSE);
1407 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1410 if (compose == NULL) {
1411 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1415 compose->rmode = mode;
1416 switch (compose->rmode) {
1418 case COMPOSE_REPLY_WITH_QUOTE:
1419 case COMPOSE_REPLY_WITHOUT_QUOTE:
1420 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1421 debug_print("reply mode Normal\n");
1422 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1423 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1425 case COMPOSE_REPLY_TO_SENDER:
1426 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1427 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1428 debug_print("reply mode Sender\n");
1429 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1431 case COMPOSE_REPLY_TO_ALL:
1432 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1433 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1434 debug_print("reply mode All\n");
1435 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1437 case COMPOSE_REPLY_TO_LIST:
1438 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1439 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1440 debug_print("reply mode List\n");
1441 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1443 case COMPOSE_REPLY_TO_ADDRESS:
1444 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1452 static Compose *compose_reply(MsgInfo *msginfo,
1453 ComposeQuoteMode quote_mode,
1459 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1460 to_sender, FALSE, body);
1463 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1464 ComposeQuoteMode quote_mode,
1469 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1470 to_sender, TRUE, body);
1473 static void compose_extract_original_charset(Compose *compose)
1475 MsgInfo *info = NULL;
1476 if (compose->replyinfo) {
1477 info = compose->replyinfo;
1478 } else if (compose->fwdinfo) {
1479 info = compose->fwdinfo;
1480 } else if (compose->targetinfo) {
1481 info = compose->targetinfo;
1484 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1485 MimeInfo *partinfo = mimeinfo;
1486 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1487 partinfo = procmime_mimeinfo_next(partinfo);
1489 compose->orig_charset =
1490 g_strdup(procmime_mimeinfo_get_parameter(
1491 partinfo, "charset"));
1493 procmime_mimeinfo_free_all(mimeinfo);
1497 #define SIGNAL_BLOCK(buffer) { \
1498 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1499 G_CALLBACK(compose_changed_cb), \
1501 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1502 G_CALLBACK(text_inserted), \
1506 #define SIGNAL_UNBLOCK(buffer) { \
1507 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1508 G_CALLBACK(compose_changed_cb), \
1510 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1511 G_CALLBACK(text_inserted), \
1515 static Compose *compose_generic_reply(MsgInfo *msginfo,
1516 ComposeQuoteMode quote_mode,
1517 gboolean to_all, gboolean to_ml,
1519 gboolean followup_and_reply_to,
1523 PrefsAccount *account = NULL;
1524 GtkTextView *textview;
1525 GtkTextBuffer *textbuf;
1526 gboolean quote = FALSE;
1527 const gchar *qmark = NULL;
1528 const gchar *body_fmt = NULL;
1529 gchar *s_system = NULL;
1531 cm_return_val_if_fail(msginfo != NULL, NULL);
1532 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1534 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1536 cm_return_val_if_fail(account != NULL, NULL);
1538 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1540 compose->updating = TRUE;
1542 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1543 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1545 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1546 if (!compose->replyinfo)
1547 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1549 compose_extract_original_charset(compose);
1551 if (msginfo->folder && msginfo->folder->ret_rcpt)
1552 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1554 /* Set save folder */
1555 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1556 gchar *folderidentifier;
1558 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1559 folderidentifier = folder_item_get_identifier(msginfo->folder);
1560 compose_set_save_to(compose, folderidentifier);
1561 g_free(folderidentifier);
1564 if (compose_parse_header(compose, msginfo) < 0) {
1565 compose->updating = FALSE;
1566 compose_destroy(compose);
1570 /* override from name according to folder properties */
1571 if (msginfo->folder && msginfo->folder->prefs &&
1572 msginfo->folder->prefs->reply_with_format &&
1573 msginfo->folder->prefs->reply_override_from_format &&
1574 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1579 /* decode \-escape sequences in the internal representation of the quote format */
1580 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1581 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1584 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1585 compose->gtkaspell);
1587 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1589 quote_fmt_scan_string(tmp);
1592 buf = quote_fmt_get_buffer();
1594 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1596 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1597 quote_fmt_reset_vartable();
1602 textview = (GTK_TEXT_VIEW(compose->text));
1603 textbuf = gtk_text_view_get_buffer(textview);
1604 compose_create_tags(textview, compose);
1606 undo_block(compose->undostruct);
1608 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1609 gtkaspell_block_check(compose->gtkaspell);
1612 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1613 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1614 /* use the reply format of folder (if enabled), or the account's one
1615 (if enabled) or fallback to the global reply format, which is always
1616 enabled (even if empty), and use the relevant quotemark */
1618 if (msginfo->folder && msginfo->folder->prefs &&
1619 msginfo->folder->prefs->reply_with_format) {
1620 qmark = msginfo->folder->prefs->reply_quotemark;
1621 body_fmt = msginfo->folder->prefs->reply_body_format;
1623 } else if (account->reply_with_format) {
1624 qmark = account->reply_quotemark;
1625 body_fmt = account->reply_body_format;
1628 qmark = prefs_common.quotemark;
1629 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1630 body_fmt = gettext(prefs_common.quotefmt);
1637 /* empty quotemark is not allowed */
1638 if (qmark == NULL || *qmark == '\0')
1640 compose_quote_fmt(compose, compose->replyinfo,
1641 body_fmt, qmark, body, FALSE, TRUE,
1642 _("The body of the \"Reply\" template has an error at line %d."));
1643 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1644 quote_fmt_reset_vartable();
1647 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1648 compose_force_encryption(compose, account, FALSE, s_system);
1651 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1652 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1653 compose_force_signing(compose, account, s_system);
1657 SIGNAL_BLOCK(textbuf);
1659 if (account->auto_sig)
1660 compose_insert_sig(compose, FALSE);
1662 compose_wrap_all(compose);
1665 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1666 gtkaspell_highlight_all(compose->gtkaspell);
1667 gtkaspell_unblock_check(compose->gtkaspell);
1669 SIGNAL_UNBLOCK(textbuf);
1671 gtk_widget_grab_focus(compose->text);
1673 undo_unblock(compose->undostruct);
1675 if (prefs_common.auto_exteditor)
1676 compose_exec_ext_editor(compose);
1678 compose->modified = FALSE;
1679 compose_set_title(compose);
1681 compose->updating = FALSE;
1682 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1683 SCROLL_TO_CURSOR(compose);
1685 if (compose->deferred_destroy) {
1686 compose_destroy(compose);
1694 #define INSERT_FW_HEADER(var, hdr) \
1695 if (msginfo->var && *msginfo->var) { \
1696 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1697 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1698 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1701 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1702 gboolean as_attach, const gchar *body,
1703 gboolean no_extedit,
1707 GtkTextView *textview;
1708 GtkTextBuffer *textbuf;
1709 gint cursor_pos = -1;
1712 cm_return_val_if_fail(msginfo != NULL, NULL);
1713 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1716 !(account = compose_guess_forward_account_from_msginfo
1718 account = cur_account;
1720 if (!prefs_common.forward_as_attachment)
1721 mode = COMPOSE_FORWARD_INLINE;
1723 mode = COMPOSE_FORWARD;
1724 compose = compose_create(account, msginfo->folder, mode, batch);
1726 compose->updating = TRUE;
1727 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1728 if (!compose->fwdinfo)
1729 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1731 compose_extract_original_charset(compose);
1733 if (msginfo->subject && *msginfo->subject) {
1734 gchar *buf, *buf2, *p;
1736 buf = p = g_strdup(msginfo->subject);
1737 p += subject_get_prefix_length(p);
1738 memmove(buf, p, strlen(p) + 1);
1740 buf2 = g_strdup_printf("Fw: %s", buf);
1741 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1747 /* override from name according to folder properties */
1748 if (msginfo->folder && msginfo->folder->prefs &&
1749 msginfo->folder->prefs->forward_with_format &&
1750 msginfo->folder->prefs->forward_override_from_format &&
1751 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1755 MsgInfo *full_msginfo = NULL;
1758 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1760 full_msginfo = procmsg_msginfo_copy(msginfo);
1762 /* decode \-escape sequences in the internal representation of the quote format */
1763 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1764 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1767 gtkaspell_block_check(compose->gtkaspell);
1768 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1769 compose->gtkaspell);
1771 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1773 quote_fmt_scan_string(tmp);
1776 buf = quote_fmt_get_buffer();
1778 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1780 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1781 quote_fmt_reset_vartable();
1784 procmsg_msginfo_free(full_msginfo);
1787 textview = GTK_TEXT_VIEW(compose->text);
1788 textbuf = gtk_text_view_get_buffer(textview);
1789 compose_create_tags(textview, compose);
1791 undo_block(compose->undostruct);
1795 msgfile = procmsg_get_message_file(msginfo);
1796 if (!is_file_exist(msgfile))
1797 g_warning("%s: file not exist\n", msgfile);
1799 compose_attach_append(compose, msgfile, msgfile,
1800 "message/rfc822", NULL);
1804 const gchar *qmark = NULL;
1805 const gchar *body_fmt = NULL;
1806 MsgInfo *full_msginfo;
1808 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1810 full_msginfo = procmsg_msginfo_copy(msginfo);
1812 /* use the forward format of folder (if enabled), or the account's one
1813 (if enabled) or fallback to the global forward format, which is always
1814 enabled (even if empty), and use the relevant quotemark */
1815 if (msginfo->folder && msginfo->folder->prefs &&
1816 msginfo->folder->prefs->forward_with_format) {
1817 qmark = msginfo->folder->prefs->forward_quotemark;
1818 body_fmt = msginfo->folder->prefs->forward_body_format;
1820 } else if (account->forward_with_format) {
1821 qmark = account->forward_quotemark;
1822 body_fmt = account->forward_body_format;
1825 qmark = prefs_common.fw_quotemark;
1826 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1827 body_fmt = gettext(prefs_common.fw_quotefmt);
1832 /* empty quotemark is not allowed */
1833 if (qmark == NULL || *qmark == '\0')
1836 compose_quote_fmt(compose, full_msginfo,
1837 body_fmt, qmark, body, FALSE, TRUE,
1838 _("The body of the \"Forward\" template has an error at line %d."));
1839 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1840 quote_fmt_reset_vartable();
1841 compose_attach_parts(compose, msginfo);
1843 procmsg_msginfo_free(full_msginfo);
1846 SIGNAL_BLOCK(textbuf);
1848 if (account->auto_sig)
1849 compose_insert_sig(compose, FALSE);
1851 compose_wrap_all(compose);
1854 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1855 gtkaspell_highlight_all(compose->gtkaspell);
1856 gtkaspell_unblock_check(compose->gtkaspell);
1858 SIGNAL_UNBLOCK(textbuf);
1860 cursor_pos = quote_fmt_get_cursor_pos();
1861 if (cursor_pos == -1)
1862 gtk_widget_grab_focus(compose->header_last->entry);
1864 gtk_widget_grab_focus(compose->text);
1866 if (!no_extedit && prefs_common.auto_exteditor)
1867 compose_exec_ext_editor(compose);
1870 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1871 gchar *folderidentifier;
1873 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1874 folderidentifier = folder_item_get_identifier(msginfo->folder);
1875 compose_set_save_to(compose, folderidentifier);
1876 g_free(folderidentifier);
1879 undo_unblock(compose->undostruct);
1881 compose->modified = FALSE;
1882 compose_set_title(compose);
1884 compose->updating = FALSE;
1885 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1886 SCROLL_TO_CURSOR(compose);
1888 if (compose->deferred_destroy) {
1889 compose_destroy(compose);
1893 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1898 #undef INSERT_FW_HEADER
1900 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1903 GtkTextView *textview;
1904 GtkTextBuffer *textbuf;
1908 gboolean single_mail = TRUE;
1910 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1912 if (g_slist_length(msginfo_list) > 1)
1913 single_mail = FALSE;
1915 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1916 if (((MsgInfo *)msginfo->data)->folder == NULL)
1919 /* guess account from first selected message */
1921 !(account = compose_guess_forward_account_from_msginfo
1922 (msginfo_list->data)))
1923 account = cur_account;
1925 cm_return_val_if_fail(account != NULL, NULL);
1927 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1928 if (msginfo->data) {
1929 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1930 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1934 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1935 g_warning("no msginfo_list");
1939 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1941 compose->updating = TRUE;
1943 /* override from name according to folder properties */
1944 if (msginfo_list->data) {
1945 MsgInfo *msginfo = msginfo_list->data;
1947 if (msginfo->folder && msginfo->folder->prefs &&
1948 msginfo->folder->prefs->forward_with_format &&
1949 msginfo->folder->prefs->forward_override_from_format &&
1950 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1955 /* decode \-escape sequences in the internal representation of the quote format */
1956 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1957 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1960 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1961 compose->gtkaspell);
1963 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1965 quote_fmt_scan_string(tmp);
1968 buf = quote_fmt_get_buffer();
1970 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1972 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1973 quote_fmt_reset_vartable();
1979 textview = GTK_TEXT_VIEW(compose->text);
1980 textbuf = gtk_text_view_get_buffer(textview);
1981 compose_create_tags(textview, compose);
1983 undo_block(compose->undostruct);
1984 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1985 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1987 if (!is_file_exist(msgfile))
1988 g_warning("%s: file not exist\n", msgfile);
1990 compose_attach_append(compose, msgfile, msgfile,
1991 "message/rfc822", NULL);
1996 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1997 if (info->subject && *info->subject) {
1998 gchar *buf, *buf2, *p;
2000 buf = p = g_strdup(info->subject);
2001 p += subject_get_prefix_length(p);
2002 memmove(buf, p, strlen(p) + 1);
2004 buf2 = g_strdup_printf("Fw: %s", buf);
2005 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2011 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2012 _("Fw: multiple emails"));
2015 SIGNAL_BLOCK(textbuf);
2017 if (account->auto_sig)
2018 compose_insert_sig(compose, FALSE);
2020 compose_wrap_all(compose);
2022 SIGNAL_UNBLOCK(textbuf);
2024 gtk_text_buffer_get_start_iter(textbuf, &iter);
2025 gtk_text_buffer_place_cursor(textbuf, &iter);
2027 gtk_widget_grab_focus(compose->header_last->entry);
2028 undo_unblock(compose->undostruct);
2029 compose->modified = FALSE;
2030 compose_set_title(compose);
2032 compose->updating = FALSE;
2033 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2034 SCROLL_TO_CURSOR(compose);
2036 if (compose->deferred_destroy) {
2037 compose_destroy(compose);
2041 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2046 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2048 GtkTextIter start = *iter;
2049 GtkTextIter end_iter;
2050 int start_pos = gtk_text_iter_get_offset(&start);
2052 if (!compose->account->sig_sep)
2055 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2056 start_pos+strlen(compose->account->sig_sep));
2058 /* check sig separator */
2059 str = gtk_text_iter_get_text(&start, &end_iter);
2060 if (!strcmp(str, compose->account->sig_sep)) {
2062 /* check end of line (\n) */
2063 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2064 start_pos+strlen(compose->account->sig_sep));
2065 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2066 start_pos+strlen(compose->account->sig_sep)+1);
2067 tmp = gtk_text_iter_get_text(&start, &end_iter);
2068 if (!strcmp(tmp,"\n")) {
2080 static void compose_colorize_signature(Compose *compose)
2082 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2084 GtkTextIter end_iter;
2085 gtk_text_buffer_get_start_iter(buffer, &iter);
2086 while (gtk_text_iter_forward_line(&iter))
2087 if (compose_is_sig_separator(compose, buffer, &iter)) {
2088 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2089 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2093 #define BLOCK_WRAP() { \
2094 prev_autowrap = compose->autowrap; \
2095 buffer = gtk_text_view_get_buffer( \
2096 GTK_TEXT_VIEW(compose->text)); \
2097 compose->autowrap = FALSE; \
2099 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2100 G_CALLBACK(compose_changed_cb), \
2102 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2103 G_CALLBACK(text_inserted), \
2106 #define UNBLOCK_WRAP() { \
2107 compose->autowrap = prev_autowrap; \
2108 if (compose->autowrap) { \
2109 gint old = compose->draft_timeout_tag; \
2110 compose->draft_timeout_tag = -2; \
2111 compose_wrap_all(compose); \
2112 compose->draft_timeout_tag = old; \
2115 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2116 G_CALLBACK(compose_changed_cb), \
2118 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2119 G_CALLBACK(text_inserted), \
2123 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2125 Compose *compose = NULL;
2126 PrefsAccount *account = NULL;
2127 GtkTextView *textview;
2128 GtkTextBuffer *textbuf;
2132 gchar buf[BUFFSIZE];
2133 gboolean use_signing = FALSE;
2134 gboolean use_encryption = FALSE;
2135 gchar *privacy_system = NULL;
2136 int priority = PRIORITY_NORMAL;
2137 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2138 gboolean autowrap = prefs_common.autowrap;
2139 gboolean autoindent = prefs_common.auto_indent;
2140 HeaderEntry *manual_headers = NULL;
2142 cm_return_val_if_fail(msginfo != NULL, NULL);
2143 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2145 if (compose_put_existing_to_front(msginfo)) {
2149 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2150 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2151 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2152 gchar queueheader_buf[BUFFSIZE];
2155 /* Select Account from queue headers */
2156 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2157 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2158 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2159 account = account_find_from_id(id);
2161 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2162 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2163 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2164 account = account_find_from_id(id);
2166 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2167 sizeof(queueheader_buf), "NAID:")) {
2168 id = atoi(&queueheader_buf[strlen("NAID:")]);
2169 account = account_find_from_id(id);
2171 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2172 sizeof(queueheader_buf), "MAID:")) {
2173 id = atoi(&queueheader_buf[strlen("MAID:")]);
2174 account = account_find_from_id(id);
2176 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2177 sizeof(queueheader_buf), "S:")) {
2178 account = account_find_from_address(queueheader_buf, FALSE);
2180 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2181 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2182 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2183 use_signing = param;
2186 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2187 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2188 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2189 use_signing = param;
2192 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2193 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2194 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2195 use_encryption = param;
2197 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2198 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2199 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2200 use_encryption = param;
2202 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2203 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2204 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2207 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2208 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2209 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2212 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2213 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2214 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2216 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2217 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2218 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2220 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2221 sizeof(queueheader_buf), "X-Priority: ")) {
2222 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2225 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2226 sizeof(queueheader_buf), "RMID:")) {
2227 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2228 if (tokens[0] && tokens[1] && tokens[2]) {
2229 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2230 if (orig_item != NULL) {
2231 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2236 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2237 sizeof(queueheader_buf), "FMID:")) {
2238 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2239 if (tokens[0] && tokens[1] && tokens[2]) {
2240 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2241 if (orig_item != NULL) {
2242 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2247 /* Get manual headers */
2248 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2249 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2250 if (*listmh != '\0') {
2251 debug_print("Got manual headers: %s\n", listmh);
2252 manual_headers = procheader_entries_from_str(listmh);
2257 account = msginfo->folder->folder->account;
2260 if (!account && prefs_common.reedit_account_autosel) {
2261 gchar from[BUFFSIZE];
2262 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2263 extract_address(from);
2264 account = account_find_from_address(from, FALSE);
2268 account = cur_account;
2270 cm_return_val_if_fail(account != NULL, NULL);
2272 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2274 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2275 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2276 compose->autowrap = autowrap;
2277 compose->replyinfo = replyinfo;
2278 compose->fwdinfo = fwdinfo;
2280 compose->updating = TRUE;
2281 compose->priority = priority;
2283 if (privacy_system != NULL) {
2284 compose->privacy_system = privacy_system;
2285 compose_use_signing(compose, use_signing);
2286 compose_use_encryption(compose, use_encryption);
2287 compose_update_privacy_system_menu_item(compose, FALSE);
2289 activate_privacy_system(compose, account, FALSE);
2292 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2294 compose_extract_original_charset(compose);
2296 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2297 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2298 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2299 gchar queueheader_buf[BUFFSIZE];
2301 /* Set message save folder */
2302 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2303 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2304 compose_set_save_to(compose, &queueheader_buf[4]);
2306 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2307 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2309 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2314 if (compose_parse_header(compose, msginfo) < 0) {
2315 compose->updating = FALSE;
2316 compose_destroy(compose);
2319 compose_reedit_set_entry(compose, msginfo);
2321 textview = GTK_TEXT_VIEW(compose->text);
2322 textbuf = gtk_text_view_get_buffer(textview);
2323 compose_create_tags(textview, compose);
2325 mark = gtk_text_buffer_get_insert(textbuf);
2326 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2328 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2329 G_CALLBACK(compose_changed_cb),
2332 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2333 fp = procmime_get_first_encrypted_text_content(msginfo);
2335 compose_force_encryption(compose, account, TRUE, NULL);
2338 fp = procmime_get_first_text_content(msginfo);
2341 g_warning("Can't get text part\n");
2345 gboolean prev_autowrap;
2346 GtkTextBuffer *buffer;
2348 while (fgets(buf, sizeof(buf), fp) != NULL) {
2350 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2356 compose_attach_parts(compose, msginfo);
2358 compose_colorize_signature(compose);
2360 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2361 G_CALLBACK(compose_changed_cb),
2364 if (manual_headers != NULL) {
2365 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2366 procheader_entries_free(manual_headers);
2367 compose->updating = FALSE;
2368 compose_destroy(compose);
2371 procheader_entries_free(manual_headers);
2374 gtk_widget_grab_focus(compose->text);
2376 if (prefs_common.auto_exteditor) {
2377 compose_exec_ext_editor(compose);
2379 compose->modified = FALSE;
2380 compose_set_title(compose);
2382 compose->updating = FALSE;
2383 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2384 SCROLL_TO_CURSOR(compose);
2386 if (compose->deferred_destroy) {
2387 compose_destroy(compose);
2391 compose->sig_str = account_get_signature_str(compose->account);
2393 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2398 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2405 cm_return_val_if_fail(msginfo != NULL, NULL);
2408 account = account_get_reply_account(msginfo,
2409 prefs_common.reply_account_autosel);
2410 cm_return_val_if_fail(account != NULL, NULL);
2412 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2414 compose->updating = TRUE;
2416 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2417 compose->replyinfo = NULL;
2418 compose->fwdinfo = NULL;
2420 compose_show_first_last_header(compose, TRUE);
2422 gtk_widget_grab_focus(compose->header_last->entry);
2424 filename = procmsg_get_message_file(msginfo);
2426 if (filename == NULL) {
2427 compose->updating = FALSE;
2428 compose_destroy(compose);
2433 compose->redirect_filename = filename;
2435 /* Set save folder */
2436 item = msginfo->folder;
2437 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2438 gchar *folderidentifier;
2440 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2441 folderidentifier = folder_item_get_identifier(item);
2442 compose_set_save_to(compose, folderidentifier);
2443 g_free(folderidentifier);
2446 compose_attach_parts(compose, msginfo);
2448 if (msginfo->subject)
2449 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2451 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2453 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2454 _("The body of the \"Redirect\" template has an error at line %d."));
2455 quote_fmt_reset_vartable();
2456 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2458 compose_colorize_signature(compose);
2461 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2462 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2463 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2465 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2466 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2467 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2468 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2469 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2470 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2471 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2472 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2474 if (compose->toolbar->draft_btn)
2475 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2476 if (compose->toolbar->insert_btn)
2477 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2478 if (compose->toolbar->attach_btn)
2479 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2480 if (compose->toolbar->sig_btn)
2481 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2482 if (compose->toolbar->exteditor_btn)
2483 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2484 if (compose->toolbar->linewrap_current_btn)
2485 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2486 if (compose->toolbar->linewrap_all_btn)
2487 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2489 compose->modified = FALSE;
2490 compose_set_title(compose);
2491 compose->updating = FALSE;
2492 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2493 SCROLL_TO_CURSOR(compose);
2495 if (compose->deferred_destroy) {
2496 compose_destroy(compose);
2500 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2505 GList *compose_get_compose_list(void)
2507 return compose_list;
2510 void compose_entry_append(Compose *compose, const gchar *address,
2511 ComposeEntryType type, ComposePrefType pref_type)
2513 const gchar *header;
2515 gboolean in_quote = FALSE;
2516 if (!address || *address == '\0') return;
2523 header = N_("Bcc:");
2525 case COMPOSE_REPLYTO:
2526 header = N_("Reply-To:");
2528 case COMPOSE_NEWSGROUPS:
2529 header = N_("Newsgroups:");
2531 case COMPOSE_FOLLOWUPTO:
2532 header = N_( "Followup-To:");
2534 case COMPOSE_INREPLYTO:
2535 header = N_( "In-Reply-To:");
2542 header = prefs_common_translated_header_name(header);
2544 cur = begin = (gchar *)address;
2546 /* we separate the line by commas, but not if we're inside a quoted
2548 while (*cur != '\0') {
2550 in_quote = !in_quote;
2551 if (*cur == ',' && !in_quote) {
2552 gchar *tmp = g_strdup(begin);
2554 tmp[cur-begin]='\0';
2557 while (*tmp == ' ' || *tmp == '\t')
2559 compose_add_header_entry(compose, header, tmp, pref_type);
2566 gchar *tmp = g_strdup(begin);
2568 tmp[cur-begin]='\0';
2569 while (*tmp == ' ' || *tmp == '\t')
2571 compose_add_header_entry(compose, header, tmp, pref_type);
2576 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2578 #if !GTK_CHECK_VERSION(3, 0, 0)
2579 static GdkColor yellow;
2580 static GdkColor black;
2581 static gboolean yellow_initialised = FALSE;
2583 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2584 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2589 #if !GTK_CHECK_VERSION(3, 0, 0)
2590 if (!yellow_initialised) {
2591 gdk_color_parse("#f5f6be", &yellow);
2592 gdk_color_parse("#000000", &black);
2593 yellow_initialised = gdk_colormap_alloc_color(
2594 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2595 yellow_initialised &= gdk_colormap_alloc_color(
2596 gdk_colormap_get_system(), &black, FALSE, TRUE);
2600 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2601 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2602 if (gtk_entry_get_text(entry) &&
2603 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2604 #if !GTK_CHECK_VERSION(3, 0, 0)
2605 if (yellow_initialised) {
2607 gtk_widget_modify_base(
2608 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2609 GTK_STATE_NORMAL, &yellow);
2610 gtk_widget_modify_text(
2611 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2612 GTK_STATE_NORMAL, &black);
2613 #if !GTK_CHECK_VERSION(3, 0, 0)
2620 void compose_toolbar_cb(gint action, gpointer data)
2622 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2623 Compose *compose = (Compose*)toolbar_item->parent;
2625 cm_return_if_fail(compose != NULL);
2629 compose_send_cb(NULL, compose);
2632 compose_send_later_cb(NULL, compose);
2635 compose_draft(compose, COMPOSE_QUIT_EDITING);
2638 compose_insert_file_cb(NULL, compose);
2641 compose_attach_cb(NULL, compose);
2644 compose_insert_sig(compose, FALSE);
2647 compose_ext_editor_cb(NULL, compose);
2649 case A_LINEWRAP_CURRENT:
2650 compose_beautify_paragraph(compose, NULL, TRUE);
2652 case A_LINEWRAP_ALL:
2653 compose_wrap_all_full(compose, TRUE);
2656 compose_address_cb(NULL, compose);
2659 case A_CHECK_SPELLING:
2660 compose_check_all(NULL, compose);
2668 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2673 gchar *subject = NULL;
2677 gchar **attach = NULL;
2678 gchar *inreplyto = NULL;
2679 MailField mfield = NO_FIELD_PRESENT;
2681 /* get mailto parts but skip from */
2682 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2685 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2686 mfield = TO_FIELD_PRESENT;
2689 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2691 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2693 if (!g_utf8_validate (subject, -1, NULL)) {
2694 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2695 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2698 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2700 mfield = SUBJECT_FIELD_PRESENT;
2703 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2704 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2707 gboolean prev_autowrap = compose->autowrap;
2709 compose->autowrap = FALSE;
2711 mark = gtk_text_buffer_get_insert(buffer);
2712 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2714 if (!g_utf8_validate (body, -1, NULL)) {
2715 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2716 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2719 gtk_text_buffer_insert(buffer, &iter, body, -1);
2721 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2723 compose->autowrap = prev_autowrap;
2724 if (compose->autowrap)
2725 compose_wrap_all(compose);
2726 mfield = BODY_FIELD_PRESENT;
2730 gint i = 0, att = 0;
2731 gchar *warn_files = NULL;
2732 while (attach[i] != NULL) {
2733 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2734 if (utf8_filename) {
2735 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2736 gchar *tmp = g_strdup_printf("%s%s\n",
2737 warn_files?warn_files:"",
2743 g_free(utf8_filename);
2745 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2750 alertpanel_notice(ngettext(
2751 "The following file has been attached: \n%s",
2752 "The following files have been attached: \n%s", att), warn_files);
2757 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2770 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2772 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2773 {"Cc:", NULL, TRUE},
2774 {"References:", NULL, FALSE},
2775 {"Bcc:", NULL, TRUE},
2776 {"Newsgroups:", NULL, TRUE},
2777 {"Followup-To:", NULL, TRUE},
2778 {"List-Post:", NULL, FALSE},
2779 {"X-Priority:", NULL, FALSE},
2780 {NULL, NULL, FALSE}};
2796 cm_return_val_if_fail(msginfo != NULL, -1);
2798 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2799 procheader_get_header_fields(fp, hentry);
2802 if (hentry[H_REPLY_TO].body != NULL) {
2803 if (hentry[H_REPLY_TO].body[0] != '\0') {
2805 conv_unmime_header(hentry[H_REPLY_TO].body,
2808 g_free(hentry[H_REPLY_TO].body);
2809 hentry[H_REPLY_TO].body = NULL;
2811 if (hentry[H_CC].body != NULL) {
2812 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2813 g_free(hentry[H_CC].body);
2814 hentry[H_CC].body = NULL;
2816 if (hentry[H_REFERENCES].body != NULL) {
2817 if (compose->mode == COMPOSE_REEDIT)
2818 compose->references = hentry[H_REFERENCES].body;
2820 compose->references = compose_parse_references
2821 (hentry[H_REFERENCES].body, msginfo->msgid);
2822 g_free(hentry[H_REFERENCES].body);
2824 hentry[H_REFERENCES].body = NULL;
2826 if (hentry[H_BCC].body != NULL) {
2827 if (compose->mode == COMPOSE_REEDIT)
2829 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2830 g_free(hentry[H_BCC].body);
2831 hentry[H_BCC].body = NULL;
2833 if (hentry[H_NEWSGROUPS].body != NULL) {
2834 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2835 hentry[H_NEWSGROUPS].body = NULL;
2837 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2838 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2839 compose->followup_to =
2840 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2843 g_free(hentry[H_FOLLOWUP_TO].body);
2844 hentry[H_FOLLOWUP_TO].body = NULL;
2846 if (hentry[H_LIST_POST].body != NULL) {
2847 gchar *to = NULL, *start = NULL;
2849 extract_address(hentry[H_LIST_POST].body);
2850 if (hentry[H_LIST_POST].body[0] != '\0') {
2851 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2853 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2854 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2857 g_free(compose->ml_post);
2858 compose->ml_post = to;
2861 g_free(hentry[H_LIST_POST].body);
2862 hentry[H_LIST_POST].body = NULL;
2865 /* CLAWS - X-Priority */
2866 if (compose->mode == COMPOSE_REEDIT)
2867 if (hentry[H_X_PRIORITY].body != NULL) {
2870 priority = atoi(hentry[H_X_PRIORITY].body);
2871 g_free(hentry[H_X_PRIORITY].body);
2873 hentry[H_X_PRIORITY].body = NULL;
2875 if (priority < PRIORITY_HIGHEST ||
2876 priority > PRIORITY_LOWEST)
2877 priority = PRIORITY_NORMAL;
2879 compose->priority = priority;
2882 if (compose->mode == COMPOSE_REEDIT) {
2883 if (msginfo->inreplyto && *msginfo->inreplyto)
2884 compose->inreplyto = g_strdup(msginfo->inreplyto);
2888 if (msginfo->msgid && *msginfo->msgid)
2889 compose->inreplyto = g_strdup(msginfo->msgid);
2891 if (!compose->references) {
2892 if (msginfo->msgid && *msginfo->msgid) {
2893 if (msginfo->inreplyto && *msginfo->inreplyto)
2894 compose->references =
2895 g_strdup_printf("<%s>\n\t<%s>",
2899 compose->references =
2900 g_strconcat("<", msginfo->msgid, ">",
2902 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2903 compose->references =
2904 g_strconcat("<", msginfo->inreplyto, ">",
2912 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2917 cm_return_val_if_fail(msginfo != NULL, -1);
2919 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2920 procheader_get_header_fields(fp, entries);
2924 while (he != NULL && he->name != NULL) {
2926 GtkListStore *model = NULL;
2928 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
2929 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
2930 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
2931 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
2932 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
2939 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2941 GSList *ref_id_list, *cur;
2945 ref_id_list = references_list_append(NULL, ref);
2946 if (!ref_id_list) return NULL;
2947 if (msgid && *msgid)
2948 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2953 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2954 /* "<" + Message-ID + ">" + CR+LF+TAB */
2955 len += strlen((gchar *)cur->data) + 5;
2957 if (len > MAX_REFERENCES_LEN) {
2958 /* remove second message-ID */
2959 if (ref_id_list && ref_id_list->next &&
2960 ref_id_list->next->next) {
2961 g_free(ref_id_list->next->data);
2962 ref_id_list = g_slist_remove
2963 (ref_id_list, ref_id_list->next->data);
2965 slist_free_strings(ref_id_list);
2966 g_slist_free(ref_id_list);
2973 new_ref = g_string_new("");
2974 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2975 if (new_ref->len > 0)
2976 g_string_append(new_ref, "\n\t");
2977 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2980 slist_free_strings(ref_id_list);
2981 g_slist_free(ref_id_list);
2983 new_ref_str = new_ref->str;
2984 g_string_free(new_ref, FALSE);
2989 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2990 const gchar *fmt, const gchar *qmark,
2991 const gchar *body, gboolean rewrap,
2992 gboolean need_unescape,
2993 const gchar *err_msg)
2995 MsgInfo* dummyinfo = NULL;
2996 gchar *quote_str = NULL;
2998 gboolean prev_autowrap;
2999 const gchar *trimmed_body = body;
3000 gint cursor_pos = -1;
3001 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3002 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3007 SIGNAL_BLOCK(buffer);
3010 dummyinfo = compose_msginfo_new_from_compose(compose);
3011 msginfo = dummyinfo;
3014 if (qmark != NULL) {
3016 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3017 compose->gtkaspell);
3019 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3021 quote_fmt_scan_string(qmark);
3024 buf = quote_fmt_get_buffer();
3026 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3028 Xstrdup_a(quote_str, buf, goto error)
3031 if (fmt && *fmt != '\0') {
3034 while (*trimmed_body == '\n')
3038 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3039 compose->gtkaspell);
3041 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3043 if (need_unescape) {
3046 /* decode \-escape sequences in the internal representation of the quote format */
3047 tmp = g_malloc(strlen(fmt)+1);
3048 pref_get_unescaped_pref(tmp, fmt);
3049 quote_fmt_scan_string(tmp);
3053 quote_fmt_scan_string(fmt);
3057 buf = quote_fmt_get_buffer();
3059 gint line = quote_fmt_get_line();
3060 alertpanel_error(err_msg, line);
3066 prev_autowrap = compose->autowrap;
3067 compose->autowrap = FALSE;
3069 mark = gtk_text_buffer_get_insert(buffer);
3070 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3071 if (g_utf8_validate(buf, -1, NULL)) {
3072 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3074 gchar *tmpout = NULL;
3075 tmpout = conv_codeset_strdup
3076 (buf, conv_get_locale_charset_str_no_utf8(),
3078 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3080 tmpout = g_malloc(strlen(buf)*2+1);
3081 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3083 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3087 cursor_pos = quote_fmt_get_cursor_pos();
3088 if (cursor_pos == -1)
3089 cursor_pos = gtk_text_iter_get_offset(&iter);
3090 compose->set_cursor_pos = cursor_pos;
3092 gtk_text_buffer_get_start_iter(buffer, &iter);
3093 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3094 gtk_text_buffer_place_cursor(buffer, &iter);
3096 compose->autowrap = prev_autowrap;
3097 if (compose->autowrap && rewrap)
3098 compose_wrap_all(compose);
3105 SIGNAL_UNBLOCK(buffer);
3107 procmsg_msginfo_free( dummyinfo );
3112 /* if ml_post is of type addr@host and from is of type
3113 * addr-anything@host, return TRUE
3115 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3117 gchar *left_ml = NULL;
3118 gchar *right_ml = NULL;
3119 gchar *left_from = NULL;
3120 gchar *right_from = NULL;
3121 gboolean result = FALSE;
3123 if (!ml_post || !from)
3126 left_ml = g_strdup(ml_post);
3127 if (strstr(left_ml, "@")) {
3128 right_ml = strstr(left_ml, "@")+1;
3129 *(strstr(left_ml, "@")) = '\0';
3132 left_from = g_strdup(from);
3133 if (strstr(left_from, "@")) {
3134 right_from = strstr(left_from, "@")+1;
3135 *(strstr(left_from, "@")) = '\0';
3138 if (left_ml && left_from && right_ml && right_from
3139 && !strncmp(left_from, left_ml, strlen(left_ml))
3140 && !strcmp(right_from, right_ml)) {
3149 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3150 gboolean respect_default_to)
3154 if (!folder || !folder->prefs)
3157 if (respect_default_to && folder->prefs->enable_default_to) {
3158 compose_entry_append(compose, folder->prefs->default_to,
3159 COMPOSE_TO, PREF_FOLDER);
3160 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3162 if (folder->prefs->enable_default_cc)
3163 compose_entry_append(compose, folder->prefs->default_cc,
3164 COMPOSE_CC, PREF_FOLDER);
3165 if (folder->prefs->enable_default_bcc)
3166 compose_entry_append(compose, folder->prefs->default_bcc,
3167 COMPOSE_BCC, PREF_FOLDER);
3168 if (folder->prefs->enable_default_replyto)
3169 compose_entry_append(compose, folder->prefs->default_replyto,
3170 COMPOSE_REPLYTO, PREF_FOLDER);
3173 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3178 if (!compose || !msginfo)
3181 if (msginfo->subject && *msginfo->subject) {
3182 buf = p = g_strdup(msginfo->subject);
3183 p += subject_get_prefix_length(p);
3184 memmove(buf, p, strlen(p) + 1);
3186 buf2 = g_strdup_printf("Re: %s", buf);
3187 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3192 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3195 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3196 gboolean to_all, gboolean to_ml,
3198 gboolean followup_and_reply_to)
3200 GSList *cc_list = NULL;
3203 gchar *replyto = NULL;
3204 gchar *ac_email = NULL;
3206 gboolean reply_to_ml = FALSE;
3207 gboolean default_reply_to = FALSE;
3209 cm_return_if_fail(compose->account != NULL);
3210 cm_return_if_fail(msginfo != NULL);
3212 reply_to_ml = to_ml && compose->ml_post;
3214 default_reply_to = msginfo->folder &&
3215 msginfo->folder->prefs->enable_default_reply_to;
3217 if (compose->account->protocol != A_NNTP) {
3218 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3220 if (reply_to_ml && !default_reply_to) {
3222 gboolean is_subscr = is_subscription(compose->ml_post,
3225 /* normal answer to ml post with a reply-to */
3226 compose_entry_append(compose,
3228 COMPOSE_TO, PREF_ML);
3229 if (compose->replyto)
3230 compose_entry_append(compose,
3232 COMPOSE_CC, PREF_ML);
3234 /* answer to subscription confirmation */
3235 if (compose->replyto)
3236 compose_entry_append(compose,
3238 COMPOSE_TO, PREF_ML);
3239 else if (msginfo->from)
3240 compose_entry_append(compose,
3242 COMPOSE_TO, PREF_ML);
3245 else if (!(to_all || to_sender) && default_reply_to) {
3246 compose_entry_append(compose,
3247 msginfo->folder->prefs->default_reply_to,
3248 COMPOSE_TO, PREF_FOLDER);
3249 compose_entry_mark_default_to(compose,
3250 msginfo->folder->prefs->default_reply_to);
3255 Xstrdup_a(tmp1, msginfo->from, return);
3256 extract_address(tmp1);
3257 if (to_all || to_sender ||
3258 !account_find_from_address(tmp1, FALSE))
3259 compose_entry_append(compose,
3260 (compose->replyto && !to_sender)
3261 ? compose->replyto :
3262 msginfo->from ? msginfo->from : "",
3263 COMPOSE_TO, PREF_NONE);
3264 else if (!to_all && !to_sender) {
3265 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3266 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3267 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3268 if (compose->replyto) {
3269 compose_entry_append(compose,
3271 COMPOSE_TO, PREF_NONE);
3273 compose_entry_append(compose,
3274 msginfo->from ? msginfo->from : "",
3275 COMPOSE_TO, PREF_NONE);
3278 /* replying to own mail, use original recp */
3279 compose_entry_append(compose,
3280 msginfo->to ? msginfo->to : "",
3281 COMPOSE_TO, PREF_NONE);
3282 compose_entry_append(compose,
3283 msginfo->cc ? msginfo->cc : "",
3284 COMPOSE_CC, PREF_NONE);
3289 if (to_sender || (compose->followup_to &&
3290 !strncmp(compose->followup_to, "poster", 6)))
3291 compose_entry_append
3293 (compose->replyto ? compose->replyto :
3294 msginfo->from ? msginfo->from : ""),
3295 COMPOSE_TO, PREF_NONE);
3297 else if (followup_and_reply_to || to_all) {
3298 compose_entry_append
3300 (compose->replyto ? compose->replyto :
3301 msginfo->from ? msginfo->from : ""),
3302 COMPOSE_TO, PREF_NONE);
3304 compose_entry_append
3306 compose->followup_to ? compose->followup_to :
3307 compose->newsgroups ? compose->newsgroups : "",
3308 COMPOSE_NEWSGROUPS, PREF_NONE);
3311 compose_entry_append
3313 compose->followup_to ? compose->followup_to :
3314 compose->newsgroups ? compose->newsgroups : "",
3315 COMPOSE_NEWSGROUPS, PREF_NONE);
3317 compose_reply_set_subject(compose, msginfo);
3319 if (to_ml && compose->ml_post) return;
3320 if (!to_all || compose->account->protocol == A_NNTP) return;
3322 if (compose->replyto) {
3323 Xstrdup_a(replyto, compose->replyto, return);
3324 extract_address(replyto);
3326 if (msginfo->from) {
3327 Xstrdup_a(from, msginfo->from, return);
3328 extract_address(from);
3331 if (replyto && from)
3332 cc_list = address_list_append_with_comments(cc_list, from);
3333 if (to_all && msginfo->folder &&
3334 msginfo->folder->prefs->enable_default_reply_to)
3335 cc_list = address_list_append_with_comments(cc_list,
3336 msginfo->folder->prefs->default_reply_to);
3337 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3338 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3340 ac_email = g_utf8_strdown(compose->account->address, -1);
3343 for (cur = cc_list; cur != NULL; cur = cur->next) {
3344 gchar *addr = g_utf8_strdown(cur->data, -1);
3345 extract_address(addr);
3347 if (strcmp(ac_email, addr))
3348 compose_entry_append(compose, (gchar *)cur->data,
3349 COMPOSE_CC, PREF_NONE);
3351 debug_print("Cc address same as compose account's, ignoring\n");
3356 slist_free_strings(cc_list);
3357 g_slist_free(cc_list);
3363 #define SET_ENTRY(entry, str) \
3366 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3369 #define SET_ADDRESS(type, str) \
3372 compose_entry_append(compose, str, type, PREF_NONE); \
3375 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3377 cm_return_if_fail(msginfo != NULL);
3379 SET_ENTRY(subject_entry, msginfo->subject);
3380 SET_ENTRY(from_name, msginfo->from);
3381 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3382 SET_ADDRESS(COMPOSE_CC, compose->cc);
3383 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3384 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3385 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3386 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3388 compose_update_priority_menu_item(compose);
3389 compose_update_privacy_system_menu_item(compose, FALSE);
3390 compose_show_first_last_header(compose, TRUE);
3396 static void compose_insert_sig(Compose *compose, gboolean replace)
3398 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3399 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3401 GtkTextIter iter, iter_end;
3402 gint cur_pos, ins_pos;
3403 gboolean prev_autowrap;
3404 gboolean found = FALSE;
3405 gboolean exists = FALSE;
3407 cm_return_if_fail(compose->account != NULL);
3411 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3412 G_CALLBACK(compose_changed_cb),
3415 mark = gtk_text_buffer_get_insert(buffer);
3416 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3417 cur_pos = gtk_text_iter_get_offset (&iter);
3420 gtk_text_buffer_get_end_iter(buffer, &iter);
3422 exists = (compose->sig_str != NULL);
3425 GtkTextIter first_iter, start_iter, end_iter;
3427 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3429 if (!exists || compose->sig_str[0] == '\0')
3432 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3433 compose->signature_tag);
3436 /* include previous \n\n */
3437 gtk_text_iter_backward_chars(&first_iter, 1);
3438 start_iter = first_iter;
3439 end_iter = first_iter;
3441 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3442 compose->signature_tag);
3443 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3444 compose->signature_tag);
3446 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3452 g_free(compose->sig_str);
3453 compose->sig_str = account_get_signature_str(compose->account);
3455 cur_pos = gtk_text_iter_get_offset(&iter);
3457 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3458 g_free(compose->sig_str);
3459 compose->sig_str = NULL;
3461 if (compose->sig_inserted == FALSE)
3462 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3463 compose->sig_inserted = TRUE;
3465 cur_pos = gtk_text_iter_get_offset(&iter);
3466 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3468 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3469 gtk_text_iter_forward_chars(&iter, 1);
3470 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3471 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3473 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3474 cur_pos = gtk_text_buffer_get_char_count (buffer);
3477 /* put the cursor where it should be
3478 * either where the quote_fmt says, either where it was */
3479 if (compose->set_cursor_pos < 0)
3480 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3482 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3483 compose->set_cursor_pos);
3485 compose->set_cursor_pos = -1;
3486 gtk_text_buffer_place_cursor(buffer, &iter);
3487 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3488 G_CALLBACK(compose_changed_cb),
3494 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3497 GtkTextBuffer *buffer;
3500 const gchar *cur_encoding;
3501 gchar buf[BUFFSIZE];
3504 gboolean prev_autowrap;
3505 gboolean badtxt = FALSE;
3506 struct stat file_stat;
3509 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3511 /* get the size of the file we are about to insert */
3512 ret = g_stat(file, &file_stat);
3514 gchar *shortfile = g_path_get_basename(file);
3515 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3517 return COMPOSE_INSERT_NO_FILE;
3518 } else if (prefs_common.warn_large_insert == TRUE) {
3520 /* ask user for confirmation if the file is large */
3521 if (prefs_common.warn_large_insert_size < 0 ||
3522 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3526 msg = g_strdup_printf(_("You are about to insert a file of %s "
3527 "in the message body. Are you sure you want to do that?"),
3528 to_human_readable(file_stat.st_size));
3529 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3530 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3533 /* do we ask for confirmation next time? */
3534 if (aval & G_ALERTDISABLE) {
3535 /* no confirmation next time, disable feature in preferences */
3536 aval &= ~G_ALERTDISABLE;
3537 prefs_common.warn_large_insert = FALSE;
3540 /* abort file insertion if user canceled action */
3541 if (aval != G_ALERTALTERNATE) {
3542 return COMPOSE_INSERT_NO_FILE;
3548 if ((fp = g_fopen(file, "rb")) == NULL) {
3549 FILE_OP_ERROR(file, "fopen");
3550 return COMPOSE_INSERT_READ_ERROR;
3553 prev_autowrap = compose->autowrap;
3554 compose->autowrap = FALSE;
3556 text = GTK_TEXT_VIEW(compose->text);
3557 buffer = gtk_text_view_get_buffer(text);
3558 mark = gtk_text_buffer_get_insert(buffer);
3559 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3561 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3562 G_CALLBACK(text_inserted),
3565 cur_encoding = conv_get_locale_charset_str_no_utf8();
3567 while (fgets(buf, sizeof(buf), fp) != NULL) {
3570 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3571 str = g_strdup(buf);
3573 str = conv_codeset_strdup
3574 (buf, cur_encoding, CS_INTERNAL);
3577 /* strip <CR> if DOS/Windows file,
3578 replace <CR> with <LF> if Macintosh file. */
3581 if (len > 0 && str[len - 1] != '\n') {
3583 if (str[len] == '\r') str[len] = '\n';
3586 gtk_text_buffer_insert(buffer, &iter, str, -1);
3590 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3591 G_CALLBACK(text_inserted),
3593 compose->autowrap = prev_autowrap;
3594 if (compose->autowrap)
3595 compose_wrap_all(compose);
3600 return COMPOSE_INSERT_INVALID_CHARACTER;
3602 return COMPOSE_INSERT_SUCCESS;
3605 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3606 const gchar *filename,
3607 const gchar *content_type,
3608 const gchar *charset)
3616 GtkListStore *store;
3618 gboolean has_binary = FALSE;
3620 if (!is_file_exist(file)) {
3621 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3622 gboolean result = FALSE;
3623 if (file_from_uri && is_file_exist(file_from_uri)) {
3624 result = compose_attach_append(
3625 compose, file_from_uri,
3626 filename, content_type,
3629 g_free(file_from_uri);
3632 alertpanel_error("File %s doesn't exist\n", filename);
3635 if ((size = get_file_size(file)) < 0) {
3636 alertpanel_error("Can't get file size of %s\n", filename);
3640 alertpanel_error(_("File %s is empty."), filename);
3643 if ((fp = g_fopen(file, "rb")) == NULL) {
3644 alertpanel_error(_("Can't read %s."), filename);
3649 ainfo = g_new0(AttachInfo, 1);
3650 auto_ainfo = g_auto_pointer_new_with_free
3651 (ainfo, (GFreeFunc) compose_attach_info_free);
3652 ainfo->file = g_strdup(file);
3655 ainfo->content_type = g_strdup(content_type);
3656 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3658 MsgFlags flags = {0, 0};
3660 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3661 ainfo->encoding = ENC_7BIT;
3663 ainfo->encoding = ENC_8BIT;
3665 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3666 if (msginfo && msginfo->subject)
3667 name = g_strdup(msginfo->subject);
3669 name = g_path_get_basename(filename ? filename : file);
3671 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3673 procmsg_msginfo_free(msginfo);
3675 if (!g_ascii_strncasecmp(content_type, "text/", 5)) {
3676 ainfo->charset = g_strdup(charset);
3677 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3679 ainfo->encoding = ENC_BASE64;
3681 name = g_path_get_basename(filename ? filename : file);
3682 ainfo->name = g_strdup(name);
3686 ainfo->content_type = procmime_get_mime_type(file);
3687 if (!ainfo->content_type) {
3688 ainfo->content_type =
3689 g_strdup("application/octet-stream");
3690 ainfo->encoding = ENC_BASE64;
3691 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text/", 5))
3693 procmime_get_encoding_for_text_file(file, &has_binary);
3695 ainfo->encoding = ENC_BASE64;
3696 name = g_path_get_basename(filename ? filename : file);
3697 ainfo->name = g_strdup(name);
3701 if (ainfo->name != NULL
3702 && !strcmp(ainfo->name, ".")) {
3703 g_free(ainfo->name);
3707 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3708 g_free(ainfo->content_type);
3709 ainfo->content_type = g_strdup("application/octet-stream");
3710 g_free(ainfo->charset);
3711 ainfo->charset = NULL;
3714 ainfo->size = (goffset)size;
3715 size_text = to_human_readable((goffset)size);
3717 store = GTK_LIST_STORE(gtk_tree_view_get_model
3718 (GTK_TREE_VIEW(compose->attach_clist)));
3720 gtk_list_store_append(store, &iter);
3721 gtk_list_store_set(store, &iter,
3722 COL_MIMETYPE, ainfo->content_type,
3723 COL_SIZE, size_text,
3724 COL_NAME, ainfo->name,
3725 COL_CHARSET, ainfo->charset,
3727 COL_AUTODATA, auto_ainfo,
3730 g_auto_pointer_free(auto_ainfo);
3731 compose_attach_update_label(compose);
3735 static void compose_use_signing(Compose *compose, gboolean use_signing)
3737 compose->use_signing = use_signing;
3738 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3741 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3743 compose->use_encryption = use_encryption;
3744 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3747 #define NEXT_PART_NOT_CHILD(info) \
3749 node = info->node; \
3750 while (node->children) \
3751 node = g_node_last_child(node); \
3752 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3755 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3759 MimeInfo *firsttext = NULL;
3760 MimeInfo *encrypted = NULL;
3763 const gchar *partname = NULL;
3765 mimeinfo = procmime_scan_message(msginfo);
3766 if (!mimeinfo) return;
3768 if (mimeinfo->node->children == NULL) {
3769 procmime_mimeinfo_free_all(mimeinfo);
3773 /* find first content part */
3774 child = (MimeInfo *) mimeinfo->node->children->data;
3775 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3776 child = (MimeInfo *)child->node->children->data;
3779 if (child->type == MIMETYPE_TEXT) {
3781 debug_print("First text part found\n");
3782 } else if (compose->mode == COMPOSE_REEDIT &&
3783 child->type == MIMETYPE_APPLICATION &&
3784 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3785 encrypted = (MimeInfo *)child->node->parent->data;
3788 child = (MimeInfo *) mimeinfo->node->children->data;
3789 while (child != NULL) {
3792 if (child == encrypted) {
3793 /* skip this part of tree */
3794 NEXT_PART_NOT_CHILD(child);
3798 if (child->type == MIMETYPE_MULTIPART) {
3799 /* get the actual content */
3800 child = procmime_mimeinfo_next(child);
3804 if (child == firsttext) {
3805 child = procmime_mimeinfo_next(child);
3809 outfile = procmime_get_tmp_file_name(child);
3810 if ((err = procmime_get_part(outfile, child)) < 0)
3811 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3813 gchar *content_type;
3815 content_type = procmime_get_content_type_str(child->type, child->subtype);
3817 /* if we meet a pgp signature, we don't attach it, but
3818 * we force signing. */
3819 if ((strcmp(content_type, "application/pgp-signature") &&
3820 strcmp(content_type, "application/pkcs7-signature") &&
3821 strcmp(content_type, "application/x-pkcs7-signature"))
3822 || compose->mode == COMPOSE_REDIRECT) {
3823 partname = procmime_mimeinfo_get_parameter(child, "filename");
3824 if (partname == NULL)
3825 partname = procmime_mimeinfo_get_parameter(child, "name");
3826 if (partname == NULL)
3828 compose_attach_append(compose, outfile,
3829 partname, content_type,
3830 procmime_mimeinfo_get_parameter(child, "charset"));
3832 compose_force_signing(compose, compose->account, NULL);
3834 g_free(content_type);
3837 NEXT_PART_NOT_CHILD(child);
3839 procmime_mimeinfo_free_all(mimeinfo);
3842 #undef NEXT_PART_NOT_CHILD
3847 WAIT_FOR_INDENT_CHAR,
3848 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3851 /* return indent length, we allow:
3852 indent characters followed by indent characters or spaces/tabs,
3853 alphabets and numbers immediately followed by indent characters,
3854 and the repeating sequences of the above
3855 If quote ends with multiple spaces, only the first one is included. */
3856 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3857 const GtkTextIter *start, gint *len)
3859 GtkTextIter iter = *start;
3863 IndentState state = WAIT_FOR_INDENT_CHAR;
3866 gint alnum_count = 0;
3867 gint space_count = 0;
3870 if (prefs_common.quote_chars == NULL) {
3874 while (!gtk_text_iter_ends_line(&iter)) {
3875 wc = gtk_text_iter_get_char(&iter);
3876 if (g_unichar_iswide(wc))
3878 clen = g_unichar_to_utf8(wc, ch);
3882 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3883 is_space = g_unichar_isspace(wc);
3885 if (state == WAIT_FOR_INDENT_CHAR) {
3886 if (!is_indent && !g_unichar_isalnum(wc))
3889 quote_len += alnum_count + space_count + 1;
3890 alnum_count = space_count = 0;
3891 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3894 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3895 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3899 else if (is_indent) {
3900 quote_len += alnum_count + space_count + 1;
3901 alnum_count = space_count = 0;
3904 state = WAIT_FOR_INDENT_CHAR;
3908 gtk_text_iter_forward_char(&iter);
3911 if (quote_len > 0 && space_count > 0)
3917 if (quote_len > 0) {
3919 gtk_text_iter_forward_chars(&iter, quote_len);
3920 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3926 /* return >0 if the line is itemized */
3927 static int compose_itemized_length(GtkTextBuffer *buffer,
3928 const GtkTextIter *start)
3930 GtkTextIter iter = *start;
3935 if (gtk_text_iter_ends_line(&iter))
3940 wc = gtk_text_iter_get_char(&iter);
3941 if (!g_unichar_isspace(wc))
3943 gtk_text_iter_forward_char(&iter);
3944 if (gtk_text_iter_ends_line(&iter))
3948 clen = g_unichar_to_utf8(wc, ch);
3952 if (!strchr("*-+", ch[0]))
3955 gtk_text_iter_forward_char(&iter);
3956 if (gtk_text_iter_ends_line(&iter))
3958 wc = gtk_text_iter_get_char(&iter);
3959 if (g_unichar_isspace(wc)) {
3965 /* return the string at the start of the itemization */
3966 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3967 const GtkTextIter *start)
3969 GtkTextIter iter = *start;
3972 GString *item_chars = g_string_new("");
3975 if (gtk_text_iter_ends_line(&iter))
3980 wc = gtk_text_iter_get_char(&iter);
3981 if (!g_unichar_isspace(wc))
3983 gtk_text_iter_forward_char(&iter);
3984 if (gtk_text_iter_ends_line(&iter))
3986 g_string_append_unichar(item_chars, wc);
3989 str = item_chars->str;
3990 g_string_free(item_chars, FALSE);
3994 /* return the number of spaces at a line's start */
3995 static int compose_left_offset_length(GtkTextBuffer *buffer,
3996 const GtkTextIter *start)
3998 GtkTextIter iter = *start;
4001 if (gtk_text_iter_ends_line(&iter))
4005 wc = gtk_text_iter_get_char(&iter);
4006 if (!g_unichar_isspace(wc))
4009 gtk_text_iter_forward_char(&iter);
4010 if (gtk_text_iter_ends_line(&iter))
4014 gtk_text_iter_forward_char(&iter);
4015 if (gtk_text_iter_ends_line(&iter))
4020 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
4021 const GtkTextIter *start,
4022 GtkTextIter *break_pos,
4026 GtkTextIter iter = *start, line_end = *start;
4027 PangoLogAttr *attrs;
4034 gboolean can_break = FALSE;
4035 gboolean do_break = FALSE;
4036 gboolean was_white = FALSE;
4037 gboolean prev_dont_break = FALSE;
4039 gtk_text_iter_forward_to_line_end(&line_end);
4040 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
4041 len = g_utf8_strlen(str, -1);
4045 g_warning("compose_get_line_break_pos: len = 0!\n");
4049 /* g_print("breaking line: %d: %s (len = %d)\n",
4050 gtk_text_iter_get_line(&iter), str, len); */
4052 attrs = g_new(PangoLogAttr, len + 1);
4054 pango_default_break(str, -1, NULL, attrs, len + 1);
4058 /* skip quote and leading spaces */
4059 for (i = 0; *p != '\0' && i < len; i++) {
4062 wc = g_utf8_get_char(p);
4063 if (i >= quote_len && !g_unichar_isspace(wc))
4065 if (g_unichar_iswide(wc))
4067 else if (*p == '\t')
4071 p = g_utf8_next_char(p);
4074 for (; *p != '\0' && i < len; i++) {
4075 PangoLogAttr *attr = attrs + i;
4079 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
4082 was_white = attr->is_white;
4084 /* don't wrap URI */
4085 if ((uri_len = get_uri_len(p)) > 0) {
4087 if (pos > 0 && col > max_col) {
4097 wc = g_utf8_get_char(p);
4098 if (g_unichar_iswide(wc)) {
4100 if (prev_dont_break && can_break && attr->is_line_break)
4102 } else if (*p == '\t')
4106 if (pos > 0 && col > max_col) {
4111 if (*p == '-' || *p == '/')
4112 prev_dont_break = TRUE;
4114 prev_dont_break = FALSE;
4116 p = g_utf8_next_char(p);
4120 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4125 *break_pos = *start;
4126 gtk_text_iter_set_line_offset(break_pos, pos);
4131 static gboolean compose_join_next_line(Compose *compose,
4132 GtkTextBuffer *buffer,
4134 const gchar *quote_str)
4136 GtkTextIter iter_ = *iter, cur, prev, next, end;
4137 PangoLogAttr attrs[3];
4139 gchar *next_quote_str;
4142 gboolean keep_cursor = FALSE;
4144 if (!gtk_text_iter_forward_line(&iter_) ||
4145 gtk_text_iter_ends_line(&iter_)) {
4148 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4150 if ((quote_str || next_quote_str) &&
4151 strcmp2(quote_str, next_quote_str) != 0) {
4152 g_free(next_quote_str);
4155 g_free(next_quote_str);
4158 if (quote_len > 0) {
4159 gtk_text_iter_forward_chars(&end, quote_len);
4160 if (gtk_text_iter_ends_line(&end)) {
4165 /* don't join itemized lines */
4166 if (compose_itemized_length(buffer, &end) > 0) {
4170 /* don't join signature separator */
4171 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4174 /* delete quote str */
4176 gtk_text_buffer_delete(buffer, &iter_, &end);
4178 /* don't join line breaks put by the user */
4180 gtk_text_iter_backward_char(&cur);
4181 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4182 gtk_text_iter_forward_char(&cur);
4186 gtk_text_iter_forward_char(&cur);
4187 /* delete linebreak and extra spaces */
4188 while (gtk_text_iter_backward_char(&cur)) {
4189 wc1 = gtk_text_iter_get_char(&cur);
4190 if (!g_unichar_isspace(wc1))
4195 while (!gtk_text_iter_ends_line(&cur)) {
4196 wc1 = gtk_text_iter_get_char(&cur);
4197 if (!g_unichar_isspace(wc1))
4199 gtk_text_iter_forward_char(&cur);
4202 if (!gtk_text_iter_equal(&prev, &next)) {
4205 mark = gtk_text_buffer_get_insert(buffer);
4206 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4207 if (gtk_text_iter_equal(&prev, &cur))
4209 gtk_text_buffer_delete(buffer, &prev, &next);
4213 /* insert space if required */
4214 gtk_text_iter_backward_char(&prev);
4215 wc1 = gtk_text_iter_get_char(&prev);
4216 wc2 = gtk_text_iter_get_char(&next);
4217 gtk_text_iter_forward_char(&next);
4218 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4219 pango_default_break(str, -1, NULL, attrs, 3);
4220 if (!attrs[1].is_line_break ||
4221 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4222 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4224 gtk_text_iter_backward_char(&iter_);
4225 gtk_text_buffer_place_cursor(buffer, &iter_);
4234 #define ADD_TXT_POS(bp_, ep_, pti_) \
4235 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4236 last = last->next; \
4237 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4238 last->next = NULL; \
4240 g_warning("alloc error scanning URIs\n"); \
4243 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4245 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4246 GtkTextBuffer *buffer;
4247 GtkTextIter iter, break_pos, end_of_line;
4248 gchar *quote_str = NULL;
4250 gboolean wrap_quote = prefs_common.linewrap_quote;
4251 gboolean prev_autowrap = compose->autowrap;
4252 gint startq_offset = -1, noq_offset = -1;
4253 gint uri_start = -1, uri_stop = -1;
4254 gint nouri_start = -1, nouri_stop = -1;
4255 gint num_blocks = 0;
4256 gint quotelevel = -1;
4257 gboolean modified = force;
4258 gboolean removed = FALSE;
4259 gboolean modified_before_remove = FALSE;
4261 gboolean start = TRUE;
4262 gint itemized_len = 0, rem_item_len = 0;
4263 gchar *itemized_chars = NULL;
4264 gboolean item_continuation = FALSE;
4269 if (compose->draft_timeout_tag == -2) {
4273 compose->autowrap = FALSE;
4275 buffer = gtk_text_view_get_buffer(text);
4276 undo_wrapping(compose->undostruct, TRUE);
4281 mark = gtk_text_buffer_get_insert(buffer);
4282 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4286 if (compose->draft_timeout_tag == -2) {
4287 if (gtk_text_iter_ends_line(&iter)) {
4288 while (gtk_text_iter_ends_line(&iter) &&
4289 gtk_text_iter_forward_line(&iter))
4292 while (gtk_text_iter_backward_line(&iter)) {
4293 if (gtk_text_iter_ends_line(&iter)) {
4294 gtk_text_iter_forward_line(&iter);
4300 /* move to line start */
4301 gtk_text_iter_set_line_offset(&iter, 0);
4304 itemized_len = compose_itemized_length(buffer, &iter);
4306 if (!itemized_len) {
4307 itemized_len = compose_left_offset_length(buffer, &iter);
4308 item_continuation = TRUE;
4312 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4314 /* go until paragraph end (empty line) */
4315 while (start || !gtk_text_iter_ends_line(&iter)) {
4316 gchar *scanpos = NULL;
4317 /* parse table - in order of priority */
4319 const gchar *needle; /* token */
4321 /* token search function */
4322 gchar *(*search) (const gchar *haystack,
4323 const gchar *needle);
4324 /* part parsing function */
4325 gboolean (*parse) (const gchar *start,
4326 const gchar *scanpos,
4330 /* part to URI function */
4331 gchar *(*build_uri) (const gchar *bp,
4335 static struct table parser[] = {
4336 {"http://", strcasestr, get_uri_part, make_uri_string},
4337 {"https://", strcasestr, get_uri_part, make_uri_string},
4338 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4339 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4340 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4341 {"www.", strcasestr, get_uri_part, make_http_string},
4342 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4343 {"@", strcasestr, get_email_part, make_email_string}
4345 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4346 gint last_index = PARSE_ELEMS;
4348 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4352 if (!prev_autowrap && num_blocks == 0) {
4354 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4355 G_CALLBACK(text_inserted),
4358 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4361 uri_start = uri_stop = -1;
4363 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4366 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4367 if (startq_offset == -1)
4368 startq_offset = gtk_text_iter_get_offset(&iter);
4369 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4370 if (quotelevel > 2) {
4371 /* recycle colors */
4372 if (prefs_common.recycle_quote_colors)
4381 if (startq_offset == -1)
4382 noq_offset = gtk_text_iter_get_offset(&iter);
4386 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4389 if (gtk_text_iter_ends_line(&iter)) {
4391 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4392 prefs_common.linewrap_len,
4394 GtkTextIter prev, next, cur;
4395 if (prev_autowrap != FALSE || force) {
4396 compose->automatic_break = TRUE;
4398 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4399 compose->automatic_break = FALSE;
4400 if (itemized_len && compose->autoindent) {
4401 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4402 if (!item_continuation)
4403 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4405 } else if (quote_str && wrap_quote) {
4406 compose->automatic_break = TRUE;
4408 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4409 compose->automatic_break = FALSE;
4410 if (itemized_len && compose->autoindent) {
4411 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4412 if (!item_continuation)
4413 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4417 /* remove trailing spaces */
4419 rem_item_len = itemized_len;
4420 while (compose->autoindent && rem_item_len-- > 0)
4421 gtk_text_iter_backward_char(&cur);
4422 gtk_text_iter_backward_char(&cur);
4425 while (!gtk_text_iter_starts_line(&cur)) {
4428 gtk_text_iter_backward_char(&cur);
4429 wc = gtk_text_iter_get_char(&cur);
4430 if (!g_unichar_isspace(wc))
4434 if (!gtk_text_iter_equal(&prev, &next)) {
4435 gtk_text_buffer_delete(buffer, &prev, &next);
4437 gtk_text_iter_forward_char(&break_pos);
4441 gtk_text_buffer_insert(buffer, &break_pos,
4445 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4447 /* move iter to current line start */
4448 gtk_text_iter_set_line_offset(&iter, 0);
4455 /* move iter to next line start */
4461 if (!prev_autowrap && num_blocks > 0) {
4463 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4464 G_CALLBACK(text_inserted),
4468 while (!gtk_text_iter_ends_line(&end_of_line)) {
4469 gtk_text_iter_forward_char(&end_of_line);
4471 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4473 nouri_start = gtk_text_iter_get_offset(&iter);
4474 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4476 walk_pos = gtk_text_iter_get_offset(&iter);
4477 /* FIXME: this looks phony. scanning for anything in the parse table */
4478 for (n = 0; n < PARSE_ELEMS; n++) {
4481 tmp = parser[n].search(walk, parser[n].needle);
4483 if (scanpos == NULL || tmp < scanpos) {
4492 /* check if URI can be parsed */
4493 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4494 (const gchar **)&ep, FALSE)
4495 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4499 strlen(parser[last_index].needle);
4502 uri_start = walk_pos + (bp - o_walk);
4503 uri_stop = walk_pos + (ep - o_walk);
4507 gtk_text_iter_forward_line(&iter);
4510 if (startq_offset != -1) {
4511 GtkTextIter startquote, endquote;
4512 gtk_text_buffer_get_iter_at_offset(
4513 buffer, &startquote, startq_offset);
4516 switch (quotelevel) {
4518 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4519 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4520 gtk_text_buffer_apply_tag_by_name(
4521 buffer, "quote0", &startquote, &endquote);
4522 gtk_text_buffer_remove_tag_by_name(
4523 buffer, "quote1", &startquote, &endquote);
4524 gtk_text_buffer_remove_tag_by_name(
4525 buffer, "quote2", &startquote, &endquote);
4530 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4531 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4532 gtk_text_buffer_apply_tag_by_name(
4533 buffer, "quote1", &startquote, &endquote);
4534 gtk_text_buffer_remove_tag_by_name(
4535 buffer, "quote0", &startquote, &endquote);
4536 gtk_text_buffer_remove_tag_by_name(
4537 buffer, "quote2", &startquote, &endquote);
4542 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4543 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4544 gtk_text_buffer_apply_tag_by_name(
4545 buffer, "quote2", &startquote, &endquote);
4546 gtk_text_buffer_remove_tag_by_name(
4547 buffer, "quote0", &startquote, &endquote);
4548 gtk_text_buffer_remove_tag_by_name(
4549 buffer, "quote1", &startquote, &endquote);
4555 } else if (noq_offset != -1) {
4556 GtkTextIter startnoquote, endnoquote;
4557 gtk_text_buffer_get_iter_at_offset(
4558 buffer, &startnoquote, noq_offset);
4561 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4562 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4563 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4564 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4565 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4566 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4567 gtk_text_buffer_remove_tag_by_name(
4568 buffer, "quote0", &startnoquote, &endnoquote);
4569 gtk_text_buffer_remove_tag_by_name(
4570 buffer, "quote1", &startnoquote, &endnoquote);
4571 gtk_text_buffer_remove_tag_by_name(
4572 buffer, "quote2", &startnoquote, &endnoquote);
4578 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4579 GtkTextIter nouri_start_iter, nouri_end_iter;
4580 gtk_text_buffer_get_iter_at_offset(
4581 buffer, &nouri_start_iter, nouri_start);
4582 gtk_text_buffer_get_iter_at_offset(
4583 buffer, &nouri_end_iter, nouri_stop);
4584 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4585 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4586 gtk_text_buffer_remove_tag_by_name(
4587 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4588 modified_before_remove = modified;
4593 if (uri_start >= 0 && uri_stop > 0) {
4594 GtkTextIter uri_start_iter, uri_end_iter, back;
4595 gtk_text_buffer_get_iter_at_offset(
4596 buffer, &uri_start_iter, uri_start);
4597 gtk_text_buffer_get_iter_at_offset(
4598 buffer, &uri_end_iter, uri_stop);
4599 back = uri_end_iter;
4600 gtk_text_iter_backward_char(&back);
4601 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4602 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4603 gtk_text_buffer_apply_tag_by_name(
4604 buffer, "link", &uri_start_iter, &uri_end_iter);
4606 if (removed && !modified_before_remove) {
4612 // debug_print("not modified, out after %d lines\n", lines);
4616 // debug_print("modified, out after %d lines\n", lines);
4618 g_free(itemized_chars);
4621 undo_wrapping(compose->undostruct, FALSE);
4622 compose->autowrap = prev_autowrap;
4627 void compose_action_cb(void *data)
4629 Compose *compose = (Compose *)data;
4630 compose_wrap_all(compose);
4633 static void compose_wrap_all(Compose *compose)
4635 compose_wrap_all_full(compose, FALSE);
4638 static void compose_wrap_all_full(Compose *compose, gboolean force)
4640 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4641 GtkTextBuffer *buffer;
4643 gboolean modified = TRUE;
4645 buffer = gtk_text_view_get_buffer(text);
4647 gtk_text_buffer_get_start_iter(buffer, &iter);
4648 while (!gtk_text_iter_is_end(&iter) && modified)
4649 modified = compose_beautify_paragraph(compose, &iter, force);
4653 static void compose_set_title(Compose *compose)
4659 edited = compose->modified ? _(" [Edited]") : "";
4661 subject = gtk_editable_get_chars(
4662 GTK_EDITABLE(compose->subject_entry), 0, -1);
4664 #ifndef GENERIC_UMPC
4665 if (subject && strlen(subject))
4666 str = g_strdup_printf(_("%s - Compose message%s"),
4669 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4671 str = g_strdup(_("Compose message"));
4674 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4680 * compose_current_mail_account:
4682 * Find a current mail account (the currently selected account, or the
4683 * default account, if a news account is currently selected). If a
4684 * mail account cannot be found, display an error message.
4686 * Return value: Mail account, or NULL if not found.
4688 static PrefsAccount *
4689 compose_current_mail_account(void)
4693 if (cur_account && cur_account->protocol != A_NNTP)
4696 ac = account_get_default();
4697 if (!ac || ac->protocol == A_NNTP) {
4698 alertpanel_error(_("Account for sending mail is not specified.\n"
4699 "Please select a mail account before sending."));
4706 #define QUOTE_IF_REQUIRED(out, str) \
4708 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4712 len = strlen(str) + 3; \
4713 if ((__tmp = alloca(len)) == NULL) { \
4714 g_warning("can't allocate memory\n"); \
4715 g_string_free(header, TRUE); \
4718 g_snprintf(__tmp, len, "\"%s\"", str); \
4723 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4724 g_warning("can't allocate memory\n"); \
4725 g_string_free(header, TRUE); \
4728 strcpy(__tmp, str); \
4734 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4736 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4740 len = strlen(str) + 3; \
4741 if ((__tmp = alloca(len)) == NULL) { \
4742 g_warning("can't allocate memory\n"); \
4745 g_snprintf(__tmp, len, "\"%s\"", str); \
4750 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4751 g_warning("can't allocate memory\n"); \
4754 strcpy(__tmp, str); \
4760 static void compose_select_account(Compose *compose, PrefsAccount *account,
4763 gchar *from = NULL, *header;
4764 ComposeHeaderEntry *header_entry;
4766 cm_return_if_fail(account != NULL);
4768 compose->account = account;
4769 if (account->name && *account->name) {
4771 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4772 from = g_strdup_printf("%s <%s>",
4773 buf, account->address);
4774 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4776 from = g_strdup_printf("<%s>",
4778 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4783 compose_set_title(compose);
4785 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4786 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4788 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4789 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4790 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4792 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4794 activate_privacy_system(compose, account, FALSE);
4796 if (!init && compose->mode != COMPOSE_REDIRECT) {
4797 undo_block(compose->undostruct);
4798 compose_insert_sig(compose, TRUE);
4799 undo_unblock(compose->undostruct);
4802 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4803 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4805 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4806 if (account->protocol == A_NNTP) {
4807 if (!strcmp(header, _("To:")))
4808 combobox_select_by_text(
4809 GTK_COMBO_BOX(header_entry->combo),
4812 if (!strcmp(header, _("Newsgroups:")))
4813 combobox_select_by_text(
4814 GTK_COMBO_BOX(header_entry->combo),
4822 /* use account's dict info if set */
4823 if (compose->gtkaspell) {
4824 if (account->enable_default_dictionary)
4825 gtkaspell_change_dict(compose->gtkaspell,
4826 account->default_dictionary, FALSE);
4827 if (account->enable_default_alt_dictionary)
4828 gtkaspell_change_alt_dict(compose->gtkaspell,
4829 account->default_alt_dictionary);
4830 if (account->enable_default_dictionary
4831 || account->enable_default_alt_dictionary)
4832 compose_spell_menu_changed(compose);
4837 gboolean compose_check_for_valid_recipient(Compose *compose) {
4838 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4839 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4840 gboolean recipient_found = FALSE;
4844 /* free to and newsgroup list */
4845 slist_free_strings(compose->to_list);
4846 g_slist_free(compose->to_list);
4847 compose->to_list = NULL;
4849 slist_free_strings(compose->newsgroup_list);
4850 g_slist_free(compose->newsgroup_list);
4851 compose->newsgroup_list = NULL;
4853 /* search header entries for to and newsgroup entries */
4854 for (list = compose->header_list; list; list = list->next) {
4857 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4858 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4861 if (entry[0] != '\0') {
4862 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4863 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4864 compose->to_list = address_list_append(compose->to_list, entry);
4865 recipient_found = TRUE;
4868 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4869 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4870 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4871 recipient_found = TRUE;
4878 return recipient_found;
4881 static gboolean compose_check_for_set_recipients(Compose *compose)
4883 if (compose->account->set_autocc && compose->account->auto_cc) {
4884 gboolean found_other = FALSE;
4886 /* search header entries for to and newsgroup entries */
4887 for (list = compose->header_list; list; list = list->next) {
4890 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4891 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4894 if (strcmp(entry, compose->account->auto_cc)
4895 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4905 if (compose->batch) {
4906 gtk_widget_show_all(compose->window);
4908 aval = alertpanel(_("Send"),
4909 _("The only recipient is the default CC address. Send anyway?"),
4910 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4911 if (aval != G_ALERTALTERNATE)
4915 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4916 gboolean found_other = FALSE;
4918 /* search header entries for to and newsgroup entries */
4919 for (list = compose->header_list; list; list = list->next) {
4922 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4923 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4926 if (strcmp(entry, compose->account->auto_bcc)
4927 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4937 if (compose->batch) {
4938 gtk_widget_show_all(compose->window);
4940 aval = alertpanel(_("Send"),
4941 _("The only recipient is the default BCC address. Send anyway?"),
4942 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4943 if (aval != G_ALERTALTERNATE)
4950 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4954 if (compose_check_for_valid_recipient(compose) == FALSE) {
4955 if (compose->batch) {
4956 gtk_widget_show_all(compose->window);
4958 alertpanel_error(_("Recipient is not specified."));
4962 if (compose_check_for_set_recipients(compose) == FALSE) {
4966 if (!compose->batch) {
4967 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4968 if (*str == '\0' && check_everything == TRUE &&
4969 compose->mode != COMPOSE_REDIRECT) {
4971 gchar *button_label;
4974 if (compose->sending)
4975 button_label = _("+_Send");
4977 button_label = _("+_Queue");
4978 message = g_strdup_printf(_("Subject is empty. %s"),
4979 compose->sending?_("Send it anyway?"):
4980 _("Queue it anyway?"));
4982 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4983 GTK_STOCK_CANCEL, button_label, NULL);
4985 if (aval != G_ALERTALTERNATE)
4990 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4996 gint compose_send(Compose *compose)
4999 FolderItem *folder = NULL;
5001 gchar *msgpath = NULL;
5002 gboolean discard_window = FALSE;
5003 gchar *errstr = NULL;
5004 gchar *tmsgid = NULL;
5005 MainWindow *mainwin = mainwindow_get_mainwindow();
5006 gboolean queued_removed = FALSE;
5008 if (prefs_common.send_dialog_invisible
5009 || compose->batch == TRUE)
5010 discard_window = TRUE;
5012 compose_allow_user_actions (compose, FALSE);
5013 compose->sending = TRUE;
5015 if (compose_check_entries(compose, TRUE) == FALSE) {
5016 if (compose->batch) {
5017 gtk_widget_show_all(compose->window);
5023 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
5026 if (compose->batch) {
5027 gtk_widget_show_all(compose->window);
5030 alertpanel_error(_("Could not queue message for sending:\n\n"
5031 "Charset conversion failed."));
5032 } else if (val == -5) {
5033 alertpanel_error(_("Could not queue message for sending:\n\n"
5034 "Couldn't get recipient encryption key."));
5035 } else if (val == -6) {
5037 } else if (val == -3) {
5038 if (privacy_peek_error())
5039 alertpanel_error(_("Could not queue message for sending:\n\n"
5040 "Signature failed: %s"), privacy_get_error());
5041 } else if (val == -2 && errno != 0) {
5042 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
5044 alertpanel_error(_("Could not queue message for sending."));
5049 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
5050 if (discard_window) {
5051 compose->sending = FALSE;
5052 compose_close(compose);
5053 /* No more compose access in the normal codepath
5054 * after this point! */
5059 alertpanel_error(_("The message was queued but could not be "
5060 "sent.\nUse \"Send queued messages\" from "
5061 "the main window to retry."));
5062 if (!discard_window) {
5069 if (msgpath == NULL) {
5070 msgpath = folder_item_fetch_msg(folder, msgnum);
5071 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5074 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5075 claws_unlink(msgpath);
5078 if (!discard_window) {
5080 if (!queued_removed)
5081 folder_item_remove_msg(folder, msgnum);
5082 folder_item_scan(folder);
5084 /* make sure we delete that */
5085 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5087 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5088 folder_item_remove_msg(folder, tmp->msgnum);
5089 procmsg_msginfo_free(tmp);
5096 if (!queued_removed)
5097 folder_item_remove_msg(folder, msgnum);
5098 folder_item_scan(folder);
5100 /* make sure we delete that */
5101 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5103 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5104 folder_item_remove_msg(folder, tmp->msgnum);
5105 procmsg_msginfo_free(tmp);
5108 if (!discard_window) {
5109 compose->sending = FALSE;
5110 compose_allow_user_actions (compose, TRUE);
5111 compose_close(compose);
5115 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5116 "the main window to retry."), errstr);
5119 alertpanel_error_log(_("The message was queued but could not be "
5120 "sent.\nUse \"Send queued messages\" from "
5121 "the main window to retry."));
5123 if (!discard_window) {
5132 toolbar_main_set_sensitive(mainwin);
5133 main_window_set_menu_sensitive(mainwin);
5139 compose_allow_user_actions (compose, TRUE);
5140 compose->sending = FALSE;
5141 compose->modified = TRUE;
5142 toolbar_main_set_sensitive(mainwin);
5143 main_window_set_menu_sensitive(mainwin);
5148 static gboolean compose_use_attach(Compose *compose)
5150 GtkTreeModel *model = gtk_tree_view_get_model
5151 (GTK_TREE_VIEW(compose->attach_clist));
5152 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5155 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5158 gchar buf[BUFFSIZE];
5160 gboolean first_to_address;
5161 gboolean first_cc_address;
5163 ComposeHeaderEntry *headerentry;
5164 const gchar *headerentryname;
5165 const gchar *cc_hdr;
5166 const gchar *to_hdr;
5167 gboolean err = FALSE;
5169 debug_print("Writing redirect header\n");
5171 cc_hdr = prefs_common_translated_header_name("Cc:");
5172 to_hdr = prefs_common_translated_header_name("To:");
5174 first_to_address = TRUE;
5175 for (list = compose->header_list; list; list = list->next) {
5176 headerentry = ((ComposeHeaderEntry *)list->data);
5177 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5179 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5180 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5181 Xstrdup_a(str, entstr, return -1);
5183 if (str[0] != '\0') {
5184 compose_convert_header
5185 (compose, buf, sizeof(buf), str,
5186 strlen("Resent-To") + 2, TRUE);
5188 if (first_to_address) {
5189 err |= (fprintf(fp, "Resent-To: ") < 0);
5190 first_to_address = FALSE;
5192 err |= (fprintf(fp, ",") < 0);
5194 err |= (fprintf(fp, "%s", buf) < 0);
5198 if (!first_to_address) {
5199 err |= (fprintf(fp, "\n") < 0);
5202 first_cc_address = TRUE;
5203 for (list = compose->header_list; list; list = list->next) {
5204 headerentry = ((ComposeHeaderEntry *)list->data);
5205 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5207 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5208 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5209 Xstrdup_a(str, strg, return -1);
5211 if (str[0] != '\0') {
5212 compose_convert_header
5213 (compose, buf, sizeof(buf), str,
5214 strlen("Resent-Cc") + 2, TRUE);
5216 if (first_cc_address) {
5217 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5218 first_cc_address = FALSE;
5220 err |= (fprintf(fp, ",") < 0);
5222 err |= (fprintf(fp, "%s", buf) < 0);
5226 if (!first_cc_address) {
5227 err |= (fprintf(fp, "\n") < 0);
5230 return (err ? -1:0);
5233 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5235 gchar buf[BUFFSIZE];
5237 const gchar *entstr;
5238 /* struct utsname utsbuf; */
5239 gboolean err = FALSE;
5241 cm_return_val_if_fail(fp != NULL, -1);
5242 cm_return_val_if_fail(compose->account != NULL, -1);
5243 cm_return_val_if_fail(compose->account->address != NULL, -1);
5246 get_rfc822_date(buf, sizeof(buf));
5247 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5250 if (compose->account->name && *compose->account->name) {
5251 compose_convert_header
5252 (compose, buf, sizeof(buf), compose->account->name,
5253 strlen("From: "), TRUE);
5254 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5255 buf, compose->account->address) < 0);
5257 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5260 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5261 if (*entstr != '\0') {
5262 Xstrdup_a(str, entstr, return -1);
5265 compose_convert_header(compose, buf, sizeof(buf), str,
5266 strlen("Subject: "), FALSE);
5267 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5271 /* Resent-Message-ID */
5272 if (compose->account->set_domain && compose->account->domain) {
5273 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5274 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5275 g_snprintf(buf, sizeof(buf), "%s",
5276 strchr(compose->account->address, '@') ?
5277 strchr(compose->account->address, '@')+1 :
5278 compose->account->address);
5280 g_snprintf(buf, sizeof(buf), "%s", "");
5283 if (compose->account->gen_msgid) {
5285 if (compose->account->msgid_with_addr) {
5286 addr = compose->account->address;
5288 generate_msgid(buf, sizeof(buf), addr);
5289 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5290 compose->msgid = g_strdup(buf);
5292 compose->msgid = NULL;
5295 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5298 /* separator between header and body */
5299 err |= (fputs("\n", fp) == EOF);
5301 return (err ? -1:0);
5304 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5308 gchar buf[BUFFSIZE];
5310 gboolean skip = FALSE;
5311 gboolean err = FALSE;
5312 gchar *not_included[]={
5313 "Return-Path:", "Delivered-To:", "Received:",
5314 "Subject:", "X-UIDL:", "AF:",
5315 "NF:", "PS:", "SRH:",
5316 "SFN:", "DSR:", "MID:",
5317 "CFG:", "PT:", "S:",
5318 "RQ:", "SSV:", "NSV:",
5319 "SSH:", "R:", "MAID:",
5320 "NAID:", "RMID:", "FMID:",
5321 "SCF:", "RRCPT:", "NG:",
5322 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5323 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5324 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5325 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5326 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5329 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5330 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5334 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5336 for (i = 0; not_included[i] != NULL; i++) {
5337 if (g_ascii_strncasecmp(buf, not_included[i],
5338 strlen(not_included[i])) == 0) {
5345 if (fputs(buf, fdest) == -1)
5348 if (!prefs_common.redirect_keep_from) {
5349 if (g_ascii_strncasecmp(buf, "From:",
5350 strlen("From:")) == 0) {
5351 err |= (fputs(" (by way of ", fdest) == EOF);
5352 if (compose->account->name
5353 && *compose->account->name) {
5354 compose_convert_header
5355 (compose, buf, sizeof(buf),
5356 compose->account->name,
5359 err |= (fprintf(fdest, "%s <%s>",
5361 compose->account->address) < 0);
5363 err |= (fprintf(fdest, "%s",
5364 compose->account->address) < 0);
5365 err |= (fputs(")", fdest) == EOF);
5369 if (fputs("\n", fdest) == -1)
5376 if (compose_redirect_write_headers(compose, fdest))
5379 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5380 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5393 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5395 GtkTextBuffer *buffer;
5396 GtkTextIter start, end;
5399 const gchar *out_codeset;
5400 EncodingType encoding = ENC_UNKNOWN;
5401 MimeInfo *mimemsg, *mimetext;
5403 const gchar *src_codeset = CS_INTERNAL;
5404 gchar *from_addr = NULL;
5405 gchar *from_name = NULL;
5407 if (action == COMPOSE_WRITE_FOR_SEND)
5408 attach_parts = TRUE;
5410 /* create message MimeInfo */
5411 mimemsg = procmime_mimeinfo_new();
5412 mimemsg->type = MIMETYPE_MESSAGE;
5413 mimemsg->subtype = g_strdup("rfc822");
5414 mimemsg->content = MIMECONTENT_MEM;
5415 mimemsg->tmp = TRUE; /* must free content later */
5416 mimemsg->data.mem = compose_get_header(compose);
5418 /* Create text part MimeInfo */
5419 /* get all composed text */
5420 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5421 gtk_text_buffer_get_start_iter(buffer, &start);
5422 gtk_text_buffer_get_end_iter(buffer, &end);
5423 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5425 out_codeset = conv_get_charset_str(compose->out_encoding);
5427 if (!out_codeset && is_ascii_str(chars)) {
5428 out_codeset = CS_US_ASCII;
5429 } else if (prefs_common.outgoing_fallback_to_ascii &&
5430 is_ascii_str(chars)) {
5431 out_codeset = CS_US_ASCII;
5432 encoding = ENC_7BIT;
5436 gchar *test_conv_global_out = NULL;
5437 gchar *test_conv_reply = NULL;
5439 /* automatic mode. be automatic. */
5440 codeconv_set_strict(TRUE);
5442 out_codeset = conv_get_outgoing_charset_str();
5444 debug_print("trying to convert to %s\n", out_codeset);
5445 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5448 if (!test_conv_global_out && compose->orig_charset
5449 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5450 out_codeset = compose->orig_charset;
5451 debug_print("failure; trying to convert to %s\n", out_codeset);
5452 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5455 if (!test_conv_global_out && !test_conv_reply) {
5457 out_codeset = CS_INTERNAL;
5458 debug_print("failure; finally using %s\n", out_codeset);
5460 g_free(test_conv_global_out);
5461 g_free(test_conv_reply);
5462 codeconv_set_strict(FALSE);
5465 if (encoding == ENC_UNKNOWN) {
5466 if (prefs_common.encoding_method == CTE_BASE64)
5467 encoding = ENC_BASE64;
5468 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5469 encoding = ENC_QUOTED_PRINTABLE;
5470 else if (prefs_common.encoding_method == CTE_8BIT)
5471 encoding = ENC_8BIT;
5473 encoding = procmime_get_encoding_for_charset(out_codeset);
5476 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5477 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5479 if (action == COMPOSE_WRITE_FOR_SEND) {
5480 codeconv_set_strict(TRUE);
5481 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5482 codeconv_set_strict(FALSE);
5488 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5489 "to the specified %s charset.\n"
5490 "Send it as %s?"), out_codeset, src_codeset);
5491 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5492 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5495 if (aval != G_ALERTALTERNATE) {
5500 out_codeset = src_codeset;
5506 out_codeset = src_codeset;
5511 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5512 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5513 strstr(buf, "\nFrom ") != NULL) {
5514 encoding = ENC_QUOTED_PRINTABLE;
5518 mimetext = procmime_mimeinfo_new();
5519 mimetext->content = MIMECONTENT_MEM;
5520 mimetext->tmp = TRUE; /* must free content later */
5521 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5522 * and free the data, which we need later. */
5523 mimetext->data.mem = g_strdup(buf);
5524 mimetext->type = MIMETYPE_TEXT;
5525 mimetext->subtype = g_strdup("plain");
5526 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5527 g_strdup(out_codeset));
5529 /* protect trailing spaces when signing message */
5530 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5531 privacy_system_can_sign(compose->privacy_system)) {
5532 encoding = ENC_QUOTED_PRINTABLE;
5535 debug_print("main text: %zd bytes encoded as %s in %d\n",
5536 strlen(buf), out_codeset, encoding);
5538 /* check for line length limit */
5539 if (action == COMPOSE_WRITE_FOR_SEND &&
5540 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5541 check_line_length(buf, 1000, &line) < 0) {
5545 msg = g_strdup_printf
5546 (_("Line %d exceeds the line length limit (998 bytes).\n"
5547 "The contents of the message might be broken on the way to the delivery.\n"
5549 "Send it anyway?"), line + 1);
5550 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5552 if (aval != G_ALERTALTERNATE) {
5558 if (encoding != ENC_UNKNOWN)
5559 procmime_encode_content(mimetext, encoding);
5561 /* append attachment parts */
5562 if (compose_use_attach(compose) && attach_parts) {
5563 MimeInfo *mimempart;
5564 gchar *boundary = NULL;
5565 mimempart = procmime_mimeinfo_new();
5566 mimempart->content = MIMECONTENT_EMPTY;
5567 mimempart->type = MIMETYPE_MULTIPART;
5568 mimempart->subtype = g_strdup("mixed");
5572 boundary = generate_mime_boundary(NULL);
5573 } while (strstr(buf, boundary) != NULL);
5575 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5578 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5580 g_node_append(mimempart->node, mimetext->node);
5581 g_node_append(mimemsg->node, mimempart->node);
5583 if (compose_add_attachments(compose, mimempart) < 0)
5586 g_node_append(mimemsg->node, mimetext->node);
5590 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5591 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5592 /* extract name and address */
5593 if (strstr(spec, " <") && strstr(spec, ">")) {
5594 from_addr = g_strdup(strrchr(spec, '<')+1);
5595 *(strrchr(from_addr, '>')) = '\0';
5596 from_name = g_strdup(spec);
5597 *(strrchr(from_name, '<')) = '\0';
5604 /* sign message if sending */
5605 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5606 privacy_system_can_sign(compose->privacy_system))
5607 if (!privacy_sign(compose->privacy_system, mimemsg,
5608 compose->account, from_addr)) {
5615 procmime_write_mimeinfo(mimemsg, fp);
5617 procmime_mimeinfo_free_all(mimemsg);
5622 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5624 GtkTextBuffer *buffer;
5625 GtkTextIter start, end;
5630 if ((fp = g_fopen(file, "wb")) == NULL) {
5631 FILE_OP_ERROR(file, "fopen");
5635 /* chmod for security */
5636 if (change_file_mode_rw(fp, file) < 0) {
5637 FILE_OP_ERROR(file, "chmod");
5638 g_warning("can't change file mode\n");
5641 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5642 gtk_text_buffer_get_start_iter(buffer, &start);
5643 gtk_text_buffer_get_end_iter(buffer, &end);
5644 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5646 chars = conv_codeset_strdup
5647 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5650 if (!chars) return -1;
5653 len = strlen(chars);
5654 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5655 FILE_OP_ERROR(file, "fwrite");
5664 if (fclose(fp) == EOF) {
5665 FILE_OP_ERROR(file, "fclose");
5672 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5675 MsgInfo *msginfo = compose->targetinfo;
5677 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5678 if (!msginfo) return -1;
5680 if (!force && MSG_IS_LOCKED(msginfo->flags))
5683 item = msginfo->folder;
5684 cm_return_val_if_fail(item != NULL, -1);
5686 if (procmsg_msg_exist(msginfo) &&
5687 (folder_has_parent_of_type(item, F_QUEUE) ||
5688 folder_has_parent_of_type(item, F_DRAFT)
5689 || msginfo == compose->autosaved_draft)) {
5690 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5691 g_warning("can't remove the old message\n");
5694 debug_print("removed reedit target %d\n", msginfo->msgnum);
5701 static void compose_remove_draft(Compose *compose)
5704 MsgInfo *msginfo = compose->targetinfo;
5705 drafts = account_get_special_folder(compose->account, F_DRAFT);
5707 if (procmsg_msg_exist(msginfo)) {
5708 folder_item_remove_msg(drafts, msginfo->msgnum);
5713 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5714 gboolean remove_reedit_target)
5716 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5719 static gboolean compose_warn_encryption(Compose *compose)
5721 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5722 AlertValue val = G_ALERTALTERNATE;
5724 if (warning == NULL)
5727 val = alertpanel_full(_("Encryption warning"), warning,
5728 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5729 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5730 if (val & G_ALERTDISABLE) {
5731 val &= ~G_ALERTDISABLE;
5732 if (val == G_ALERTALTERNATE)
5733 privacy_inhibit_encrypt_warning(compose->privacy_system,
5737 if (val == G_ALERTALTERNATE) {
5744 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5745 gchar **msgpath, gboolean check_subject,
5746 gboolean remove_reedit_target)
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);
5759 if (compose_check_entries(compose, check_subject) == FALSE) {
5760 if (compose->batch) {
5761 gtk_widget_show_all(compose->window);
5766 if (!compose->to_list && !compose->newsgroup_list) {
5767 g_warning("can't get recipient list.");
5771 if (compose->to_list) {
5772 if (compose->account->protocol != A_NNTP)
5773 mailac = compose->account;
5774 else if (cur_account && cur_account->protocol != A_NNTP)
5775 mailac = cur_account;
5776 else if (!(mailac = compose_current_mail_account())) {
5777 alertpanel_error(_("No account for sending mails available!"));
5782 if (compose->newsgroup_list) {
5783 if (compose->account->protocol == A_NNTP)
5784 newsac = compose->account;
5786 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5791 /* write queue header */
5792 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5793 G_DIR_SEPARATOR, compose, (guint) rand());
5794 debug_print("queuing to %s\n", tmp);
5795 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5796 FILE_OP_ERROR(tmp, "fopen");
5801 if (change_file_mode_rw(fp, tmp) < 0) {
5802 FILE_OP_ERROR(tmp, "chmod");
5803 g_warning("can't change file mode\n");
5806 /* queueing variables */
5807 err |= (fprintf(fp, "AF:\n") < 0);
5808 err |= (fprintf(fp, "NF:0\n") < 0);
5809 err |= (fprintf(fp, "PS:10\n") < 0);
5810 err |= (fprintf(fp, "SRH:1\n") < 0);
5811 err |= (fprintf(fp, "SFN:\n") < 0);
5812 err |= (fprintf(fp, "DSR:\n") < 0);
5814 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5816 err |= (fprintf(fp, "MID:\n") < 0);
5817 err |= (fprintf(fp, "CFG:\n") < 0);
5818 err |= (fprintf(fp, "PT:0\n") < 0);
5819 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5820 err |= (fprintf(fp, "RQ:\n") < 0);
5822 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5824 err |= (fprintf(fp, "SSV:\n") < 0);
5826 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5828 err |= (fprintf(fp, "NSV:\n") < 0);
5829 err |= (fprintf(fp, "SSH:\n") < 0);
5830 /* write recepient list */
5831 if (compose->to_list) {
5832 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5833 for (cur = compose->to_list->next; cur != NULL;
5835 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5836 err |= (fprintf(fp, "\n") < 0);
5838 /* write newsgroup list */
5839 if (compose->newsgroup_list) {
5840 err |= (fprintf(fp, "NG:") < 0);
5841 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5842 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5843 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5844 err |= (fprintf(fp, "\n") < 0);
5846 /* Sylpheed account IDs */
5848 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5850 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5853 if (compose->privacy_system != NULL) {
5854 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5855 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5856 if (compose->use_encryption) {
5858 if (!compose_warn_encryption(compose)) {
5864 if (mailac && mailac->encrypt_to_self) {
5865 GSList *tmp_list = g_slist_copy(compose->to_list);
5866 tmp_list = g_slist_append(tmp_list, compose->account->address);
5867 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5868 g_slist_free(tmp_list);
5870 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5872 if (encdata != NULL) {
5873 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5874 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5875 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5877 } /* else we finally dont want to encrypt */
5879 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5880 /* and if encdata was null, it means there's been a problem in
5883 g_warning("failed to write queue message");
5893 /* Save copy folder */
5894 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5895 gchar *savefolderid;
5897 savefolderid = compose_get_save_to(compose);
5898 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5899 g_free(savefolderid);
5901 /* Save copy folder */
5902 if (compose->return_receipt) {
5903 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5905 /* Message-ID of message replying to */
5906 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5909 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5910 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5913 /* Message-ID of message forwarding to */
5914 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5917 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5918 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5922 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5923 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5925 /* end of headers */
5926 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5928 if (compose->redirect_filename != NULL) {
5929 if (compose_redirect_write_to_file(compose, fp) < 0) {
5937 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5941 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5945 g_warning("failed to write queue message\n");
5951 if (fclose(fp) == EOF) {
5952 FILE_OP_ERROR(tmp, "fclose");
5958 if (item && *item) {
5961 queue = account_get_special_folder(compose->account, F_QUEUE);
5964 g_warning("can't find queue folder\n");
5969 folder_item_scan(queue);
5970 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5971 g_warning("can't queue the message\n");
5977 if (msgpath == NULL) {
5983 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5984 compose_remove_reedit_target(compose, FALSE);
5987 if ((msgnum != NULL) && (item != NULL)) {
5995 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5998 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
6000 struct stat statbuf;
6001 gchar *type, *subtype;
6002 GtkTreeModel *model;
6005 model = gtk_tree_view_get_model(tree_view);
6007 if (!gtk_tree_model_get_iter_first(model, &iter))
6010 gtk_tree_model_get(model, &iter,
6014 if (!is_file_exist(ainfo->file)) {
6015 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
6016 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
6017 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
6019 if (val == G_ALERTDEFAULT) {
6024 mimepart = procmime_mimeinfo_new();
6025 mimepart->content = MIMECONTENT_FILE;
6026 mimepart->data.filename = g_strdup(ainfo->file);
6027 mimepart->tmp = FALSE; /* or we destroy our attachment */
6028 mimepart->offset = 0;
6030 g_stat(ainfo->file, &statbuf);
6031 mimepart->length = statbuf.st_size;
6033 type = g_strdup(ainfo->content_type);
6035 if (!strchr(type, '/')) {
6037 type = g_strdup("application/octet-stream");
6040 subtype = strchr(type, '/') + 1;
6041 *(subtype - 1) = '\0';
6042 mimepart->type = procmime_get_media_type(type);
6043 mimepart->subtype = g_strdup(subtype);
6046 if (mimepart->type == MIMETYPE_MESSAGE &&
6047 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
6048 mimepart->disposition = DISPOSITIONTYPE_INLINE;
6049 } else if (mimepart->type == MIMETYPE_TEXT) {
6050 if (!ainfo->name && g_ascii_strcasecmp(mimepart->subtype, "plain")) {
6051 /* Text parts with no name come from multipart/alternative
6052 * forwards. Make sure the recipient won't look at the
6053 * original HTML part by mistake. */
6054 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6055 ainfo->name = g_strdup_printf(_("Original %s part"),
6059 g_hash_table_insert(mimepart->typeparameters,
6060 g_strdup("charset"), g_strdup(ainfo->charset));
6062 if (ainfo->name && mimepart->type != MIMETYPE_MESSAGE) {
6063 if (mimepart->type == MIMETYPE_APPLICATION &&
6064 !strcmp2(mimepart->subtype, "octet-stream"))
6065 g_hash_table_insert(mimepart->typeparameters,
6066 g_strdup("name"), g_strdup(ainfo->name));
6067 g_hash_table_insert(mimepart->dispositionparameters,
6068 g_strdup("filename"), g_strdup(ainfo->name));
6069 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6072 if (mimepart->type == MIMETYPE_MESSAGE
6073 || mimepart->type == MIMETYPE_MULTIPART)
6074 ainfo->encoding = ENC_BINARY;
6075 else if (compose->use_signing) {
6076 if (ainfo->encoding == ENC_7BIT)
6077 ainfo->encoding = ENC_QUOTED_PRINTABLE;
6078 else if (ainfo->encoding == ENC_8BIT)
6079 ainfo->encoding = ENC_BASE64;
6084 procmime_encode_content(mimepart, ainfo->encoding);
6086 g_node_append(parent->node, mimepart->node);
6087 } while (gtk_tree_model_iter_next(model, &iter));
6092 #define IS_IN_CUSTOM_HEADER(header) \
6093 (compose->account->add_customhdr && \
6094 custom_header_find(compose->account->customhdr_list, header) != NULL)
6096 static void compose_add_headerfield_from_headerlist(Compose *compose,
6098 const gchar *fieldname,
6099 const gchar *seperator)
6101 gchar *str, *fieldname_w_colon;
6102 gboolean add_field = FALSE;
6104 ComposeHeaderEntry *headerentry;
6105 const gchar *headerentryname;
6106 const gchar *trans_fieldname;
6109 if (IS_IN_CUSTOM_HEADER(fieldname))
6112 debug_print("Adding %s-fields\n", fieldname);
6114 fieldstr = g_string_sized_new(64);
6116 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6117 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6119 for (list = compose->header_list; list; list = list->next) {
6120 headerentry = ((ComposeHeaderEntry *)list->data);
6121 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6123 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6124 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6126 if (str[0] != '\0') {
6128 g_string_append(fieldstr, seperator);
6129 g_string_append(fieldstr, str);
6138 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6139 compose_convert_header
6140 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6141 strlen(fieldname) + 2, TRUE);
6142 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6146 g_free(fieldname_w_colon);
6147 g_string_free(fieldstr, TRUE);
6152 static gchar *compose_get_manual_headers_info(Compose *compose)
6154 GString *sh_header = g_string_new(" ");
6156 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6158 for (list = compose->header_list; list; list = list->next) {
6159 ComposeHeaderEntry *headerentry;
6162 gchar *headername_wcolon;
6163 const gchar *headername_trans;
6165 gboolean standard_header = FALSE;
6167 headerentry = ((ComposeHeaderEntry *)list->data);
6169 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6171 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6176 if (!strstr(tmp, ":")) {
6177 headername_wcolon = g_strconcat(tmp, ":", NULL);
6178 headername = g_strdup(tmp);
6180 headername_wcolon = g_strdup(tmp);
6181 headername = g_strdup(strtok(tmp, ":"));
6185 string = std_headers;
6186 while (*string != NULL) {
6187 headername_trans = prefs_common_translated_header_name(*string);
6188 if (!strcmp(headername_trans, headername_wcolon))
6189 standard_header = TRUE;
6192 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6193 g_string_append_printf(sh_header, "%s ", headername);
6195 g_free(headername_wcolon);
6197 g_string_truncate(sh_header, strlen(sh_header->str) - 1); /* remove last space */
6198 return g_string_free(sh_header, FALSE);
6201 static gchar *compose_get_header(Compose *compose)
6203 gchar buf[BUFFSIZE];
6204 const gchar *entry_str;
6208 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6210 gchar *from_name = NULL, *from_address = NULL;
6213 cm_return_val_if_fail(compose->account != NULL, NULL);
6214 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6216 header = g_string_sized_new(64);
6219 get_rfc822_date(buf, sizeof(buf));
6220 g_string_append_printf(header, "Date: %s\n", buf);
6224 if (compose->account->name && *compose->account->name) {
6226 QUOTE_IF_REQUIRED(buf, compose->account->name);
6227 tmp = g_strdup_printf("%s <%s>",
6228 buf, compose->account->address);
6230 tmp = g_strdup_printf("%s",
6231 compose->account->address);
6233 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6234 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6236 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6237 from_address = g_strdup(compose->account->address);
6239 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6240 /* extract name and address */
6241 if (strstr(spec, " <") && strstr(spec, ">")) {
6242 from_address = g_strdup(strrchr(spec, '<')+1);
6243 *(strrchr(from_address, '>')) = '\0';
6244 from_name = g_strdup(spec);
6245 *(strrchr(from_name, '<')) = '\0';
6248 from_address = g_strdup(spec);
6255 if (from_name && *from_name) {
6256 compose_convert_header
6257 (compose, buf, sizeof(buf), from_name,
6258 strlen("From: "), TRUE);
6259 QUOTE_IF_REQUIRED(name, buf);
6261 g_string_append_printf(header, "From: %s <%s>\n",
6262 name, from_address);
6264 g_string_append_printf(header, "From: %s\n", from_address);
6267 g_free(from_address);
6270 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6273 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6276 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6280 * If this account is a NNTP account remove Bcc header from
6281 * message body since it otherwise will be publicly shown
6283 if (compose->account->protocol != A_NNTP)
6284 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6287 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6289 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6292 compose_convert_header(compose, buf, sizeof(buf), str,
6293 strlen("Subject: "), FALSE);
6294 g_string_append_printf(header, "Subject: %s\n", buf);
6300 if (compose->account->set_domain && compose->account->domain) {
6301 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6302 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6303 g_snprintf(buf, sizeof(buf), "%s",
6304 strchr(compose->account->address, '@') ?
6305 strchr(compose->account->address, '@')+1 :
6306 compose->account->address);
6308 g_snprintf(buf, sizeof(buf), "%s", "");
6311 if (compose->account->gen_msgid) {
6313 if (compose->account->msgid_with_addr) {
6314 addr = compose->account->address;
6316 generate_msgid(buf, sizeof(buf), addr);
6317 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6318 compose->msgid = g_strdup(buf);
6320 compose->msgid = NULL;
6323 if (compose->remove_references == FALSE) {
6325 if (compose->inreplyto && compose->to_list)
6326 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6329 if (compose->references)
6330 g_string_append_printf(header, "References: %s\n", compose->references);
6334 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6337 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6340 if (compose->account->organization &&
6341 strlen(compose->account->organization) &&
6342 !IS_IN_CUSTOM_HEADER("Organization")) {
6343 compose_convert_header(compose, buf, sizeof(buf),
6344 compose->account->organization,
6345 strlen("Organization: "), FALSE);
6346 g_string_append_printf(header, "Organization: %s\n", buf);
6349 /* Program version and system info */
6350 if (compose->account->gen_xmailer &&
6351 g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6352 !compose->newsgroup_list) {
6353 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6355 gtk_major_version, gtk_minor_version, gtk_micro_version,
6358 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6359 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6361 gtk_major_version, gtk_minor_version, gtk_micro_version,
6365 /* custom headers */
6366 if (compose->account->add_customhdr) {
6369 for (cur = compose->account->customhdr_list; cur != NULL;
6371 CustomHeader *chdr = (CustomHeader *)cur->data;
6373 if (custom_header_is_allowed(chdr->name)
6374 && chdr->value != NULL
6375 && *(chdr->value) != '\0') {
6376 compose_convert_header
6377 (compose, buf, sizeof(buf),
6379 strlen(chdr->name) + 2, FALSE);
6380 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6385 /* Automatic Faces and X-Faces */
6386 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6387 g_string_append_printf(header, "X-Face: %s\n", buf);
6389 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6390 g_string_append_printf(header, "X-Face: %s\n", buf);
6392 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6393 g_string_append_printf(header, "Face: %s\n", buf);
6395 else if (get_default_face (buf, sizeof(buf)) == 0) {
6396 g_string_append_printf(header, "Face: %s\n", buf);
6400 switch (compose->priority) {
6401 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6402 "X-Priority: 1 (Highest)\n");
6404 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6405 "X-Priority: 2 (High)\n");
6407 case PRIORITY_NORMAL: break;
6408 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6409 "X-Priority: 4 (Low)\n");
6411 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6412 "X-Priority: 5 (Lowest)\n");
6414 default: debug_print("compose: priority unknown : %d\n",
6418 /* Request Return Receipt */
6419 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6420 if (compose->return_receipt) {
6421 if (compose->account->name
6422 && *compose->account->name) {
6423 compose_convert_header(compose, buf, sizeof(buf),
6424 compose->account->name,
6425 strlen("Disposition-Notification-To: "),
6427 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6429 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6433 /* get special headers */
6434 for (list = compose->header_list; list; list = list->next) {
6435 ComposeHeaderEntry *headerentry;
6438 gchar *headername_wcolon;
6439 const gchar *headername_trans;
6442 gboolean standard_header = FALSE;
6444 headerentry = ((ComposeHeaderEntry *)list->data);
6446 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6448 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6453 if (!strstr(tmp, ":")) {
6454 headername_wcolon = g_strconcat(tmp, ":", NULL);
6455 headername = g_strdup(tmp);
6457 headername_wcolon = g_strdup(tmp);
6458 headername = g_strdup(strtok(tmp, ":"));
6462 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6463 Xstrdup_a(headervalue, entry_str, return NULL);
6464 subst_char(headervalue, '\r', ' ');
6465 subst_char(headervalue, '\n', ' ');
6466 string = std_headers;
6467 while (*string != NULL) {
6468 headername_trans = prefs_common_translated_header_name(*string);
6469 if (!strcmp(headername_trans, headername_wcolon))
6470 standard_header = TRUE;
6473 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6474 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6477 g_free(headername_wcolon);
6481 g_string_free(header, FALSE);
6486 #undef IS_IN_CUSTOM_HEADER
6488 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6489 gint header_len, gboolean addr_field)
6491 gchar *tmpstr = NULL;
6492 const gchar *out_codeset = NULL;
6494 cm_return_if_fail(src != NULL);
6495 cm_return_if_fail(dest != NULL);
6497 if (len < 1) return;
6499 tmpstr = g_strdup(src);
6501 subst_char(tmpstr, '\n', ' ');
6502 subst_char(tmpstr, '\r', ' ');
6505 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6506 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6507 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6512 codeconv_set_strict(TRUE);
6513 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6514 conv_get_charset_str(compose->out_encoding));
6515 codeconv_set_strict(FALSE);
6517 if (!dest || *dest == '\0') {
6518 gchar *test_conv_global_out = NULL;
6519 gchar *test_conv_reply = NULL;
6521 /* automatic mode. be automatic. */
6522 codeconv_set_strict(TRUE);
6524 out_codeset = conv_get_outgoing_charset_str();
6526 debug_print("trying to convert to %s\n", out_codeset);
6527 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6530 if (!test_conv_global_out && compose->orig_charset
6531 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6532 out_codeset = compose->orig_charset;
6533 debug_print("failure; trying to convert to %s\n", out_codeset);
6534 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6537 if (!test_conv_global_out && !test_conv_reply) {
6539 out_codeset = CS_INTERNAL;
6540 debug_print("finally using %s\n", out_codeset);
6542 g_free(test_conv_global_out);
6543 g_free(test_conv_reply);
6544 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6546 codeconv_set_strict(FALSE);
6551 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6555 cm_return_if_fail(user_data != NULL);
6557 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6558 g_strstrip(address);
6559 if (*address != '\0') {
6560 gchar *name = procheader_get_fromname(address);
6561 extract_address(address);
6562 #ifndef USE_NEW_ADDRBOOK
6563 addressbook_add_contact(name, address, NULL, NULL);
6565 debug_print("%s: %s\n", name, address);
6566 if (addressadd_selection(name, address, NULL, NULL)) {
6567 debug_print( "addressbook_add_contact - added\n" );
6574 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6576 GtkWidget *menuitem;
6579 cm_return_if_fail(menu != NULL);
6580 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6582 menuitem = gtk_separator_menu_item_new();
6583 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6584 gtk_widget_show(menuitem);
6586 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6587 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6589 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6590 g_strstrip(address);
6591 if (*address == '\0') {
6592 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6595 g_signal_connect(G_OBJECT(menuitem), "activate",
6596 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6597 gtk_widget_show(menuitem);
6600 static void compose_create_header_entry(Compose *compose)
6602 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6609 const gchar *header = NULL;
6610 ComposeHeaderEntry *headerentry;
6611 gboolean standard_header = FALSE;
6612 GtkListStore *model;
6614 #if !(GTK_CHECK_VERSION(2,12,0))
6615 GtkTooltips *tips = compose->tooltips;
6618 headerentry = g_new0(ComposeHeaderEntry, 1);
6621 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6622 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6623 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6625 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6627 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6629 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6630 COMPOSE_NEWSGROUPS);
6631 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6633 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6634 COMPOSE_FOLLOWUPTO);
6636 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6637 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo))), "grab_focus",
6638 G_CALLBACK(compose_grab_focus_cb), compose);
6639 gtk_widget_show(combo);
6642 l = g_list_prepend(l, gtk_bin_get_child(GTK_BIN(combo)));
6643 gtk_container_set_focus_chain(GTK_CONTAINER(combo), l);
6646 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6647 compose->header_nextrow, compose->header_nextrow+1,
6648 GTK_SHRINK, GTK_FILL, 0, 0);
6649 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6650 const gchar *last_header_entry = gtk_entry_get_text(
6651 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6653 while (*string != NULL) {
6654 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6655 standard_header = TRUE;
6658 if (standard_header)
6659 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6661 if (!compose->header_last || !standard_header) {
6662 switch(compose->account->protocol) {
6664 header = prefs_common_translated_header_name("Newsgroups:");
6667 header = prefs_common_translated_header_name("To:");
6672 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6674 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6675 G_CALLBACK(compose_grab_focus_cb), compose);
6677 /* Entry field with cleanup button */
6678 button = gtk_button_new();
6679 gtk_button_set_image(GTK_BUTTON(button),
6680 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6681 gtk_widget_show(button);
6682 CLAWS_SET_TIP(button,
6683 _("Delete entry contents"));
6684 entry = gtk_entry_new();
6685 gtk_widget_show(entry);
6686 CLAWS_SET_TIP(entry,
6687 _("Use <tab> to autocomplete from addressbook"));
6688 hbox = gtk_hbox_new (FALSE, 0);
6689 gtk_widget_show(hbox);
6690 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6691 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6692 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6693 compose->header_nextrow, compose->header_nextrow+1,
6694 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6696 g_signal_connect(G_OBJECT(entry), "key-press-event",
6697 G_CALLBACK(compose_headerentry_key_press_event_cb),
6699 g_signal_connect(G_OBJECT(entry), "changed",
6700 G_CALLBACK(compose_headerentry_changed_cb),
6702 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6703 G_CALLBACK(compose_grab_focus_cb), compose);
6705 g_signal_connect(G_OBJECT(button), "clicked",
6706 G_CALLBACK(compose_headerentry_button_clicked_cb),
6710 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6711 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6712 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6713 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6714 G_CALLBACK(compose_header_drag_received_cb),
6716 g_signal_connect(G_OBJECT(entry), "drag-drop",
6717 G_CALLBACK(compose_drag_drop),
6719 g_signal_connect(G_OBJECT(entry), "populate-popup",
6720 G_CALLBACK(compose_entry_popup_extend),
6723 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6725 headerentry->compose = compose;
6726 headerentry->combo = combo;
6727 headerentry->entry = entry;
6728 headerentry->button = button;
6729 headerentry->hbox = hbox;
6730 headerentry->headernum = compose->header_nextrow;
6731 headerentry->type = PREF_NONE;
6733 compose->header_nextrow++;
6734 compose->header_last = headerentry;
6735 compose->header_list =
6736 g_slist_append(compose->header_list,
6740 static void compose_add_header_entry(Compose *compose, const gchar *header,
6741 gchar *text, ComposePrefType pref_type)
6743 ComposeHeaderEntry *last_header = compose->header_last;
6744 gchar *tmp = g_strdup(text), *email;
6745 gboolean replyto_hdr;
6747 replyto_hdr = (!strcasecmp(header,
6748 prefs_common_translated_header_name("Reply-To:")) ||
6750 prefs_common_translated_header_name("Followup-To:")) ||
6752 prefs_common_translated_header_name("In-Reply-To:")));
6754 extract_address(tmp);
6755 email = g_utf8_strdown(tmp, -1);
6757 if (replyto_hdr == FALSE &&
6758 g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6760 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6761 header, text, (gint) pref_type);
6767 if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6768 gtk_entry_set_text(GTK_ENTRY(
6769 gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6771 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6772 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6773 last_header->type = pref_type;
6775 if (replyto_hdr == FALSE)
6776 g_hash_table_insert(compose->email_hashtable, email,
6777 GUINT_TO_POINTER(1));
6784 static void compose_destroy_headerentry(Compose *compose,
6785 ComposeHeaderEntry *headerentry)
6787 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6790 extract_address(text);
6791 email = g_utf8_strdown(text, -1);
6792 g_hash_table_remove(compose->email_hashtable, email);
6796 gtk_widget_destroy(headerentry->combo);
6797 gtk_widget_destroy(headerentry->entry);
6798 gtk_widget_destroy(headerentry->button);
6799 gtk_widget_destroy(headerentry->hbox);
6800 g_free(headerentry);
6803 static void compose_remove_header_entries(Compose *compose)
6806 for (list = compose->header_list; list; list = list->next)
6807 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6809 compose->header_last = NULL;
6810 g_slist_free(compose->header_list);
6811 compose->header_list = NULL;
6812 compose->header_nextrow = 1;
6813 compose_create_header_entry(compose);
6816 static GtkWidget *compose_create_header(Compose *compose)
6818 GtkWidget *from_optmenu_hbox;
6819 GtkWidget *header_scrolledwin_main;
6820 GtkWidget *header_table_main;
6821 GtkWidget *header_scrolledwin;
6822 GtkWidget *header_table;
6824 /* parent with account selection and from header */
6825 header_scrolledwin_main = gtk_scrolled_window_new(NULL, NULL);
6826 gtk_widget_show(header_scrolledwin_main);
6827 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin_main), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6829 header_table_main = gtk_table_new(2, 2, FALSE);
6830 gtk_widget_show(header_table_main);
6831 gtk_container_set_border_width(GTK_CONTAINER(header_table_main), BORDER_WIDTH);
6832 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin_main), header_table_main);
6833 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin_main)))), GTK_SHADOW_NONE);
6835 from_optmenu_hbox = compose_account_option_menu_create(compose);
6836 gtk_table_attach(GTK_TABLE(header_table_main), from_optmenu_hbox,
6837 0, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6839 /* child with header labels and entries */
6840 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6841 gtk_widget_show(header_scrolledwin);
6842 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6844 header_table = gtk_table_new(2, 2, FALSE);
6845 gtk_widget_show(header_table);
6846 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6847 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6848 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6850 gtk_table_attach(GTK_TABLE(header_table_main), header_scrolledwin,
6851 0, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 2);
6853 compose->header_table = header_table;
6854 compose->header_list = NULL;
6855 compose->header_nextrow = 0;
6857 compose_create_header_entry(compose);
6859 compose->table = NULL;
6861 return header_scrolledwin_main;
6864 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6866 Compose *compose = (Compose *)data;
6867 GdkEventButton event;
6870 event.time = gtk_get_current_event_time();
6872 return attach_button_pressed(compose->attach_clist, &event, compose);
6875 static GtkWidget *compose_create_attach(Compose *compose)
6877 GtkWidget *attach_scrwin;
6878 GtkWidget *attach_clist;
6880 GtkListStore *store;
6881 GtkCellRenderer *renderer;
6882 GtkTreeViewColumn *column;
6883 GtkTreeSelection *selection;
6885 /* attachment list */
6886 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6887 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6888 GTK_POLICY_AUTOMATIC,
6889 GTK_POLICY_AUTOMATIC);
6890 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6892 store = gtk_list_store_new(N_ATTACH_COLS,
6898 G_TYPE_AUTO_POINTER,
6900 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6901 (GTK_TREE_MODEL(store)));
6902 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6903 g_object_unref(store);
6905 renderer = gtk_cell_renderer_text_new();
6906 column = gtk_tree_view_column_new_with_attributes
6907 (_("Mime type"), renderer, "text",
6908 COL_MIMETYPE, NULL);
6909 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6911 renderer = gtk_cell_renderer_text_new();
6912 column = gtk_tree_view_column_new_with_attributes
6913 (_("Size"), renderer, "text",
6915 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6917 renderer = gtk_cell_renderer_text_new();
6918 column = gtk_tree_view_column_new_with_attributes
6919 (_("Name"), renderer, "text",
6921 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6923 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6924 prefs_common.use_stripes_everywhere);
6925 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6926 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6928 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6929 G_CALLBACK(attach_selected), compose);
6930 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6931 G_CALLBACK(attach_button_pressed), compose);
6933 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6934 G_CALLBACK(popup_attach_button_pressed), compose);
6936 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6937 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6938 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6939 G_CALLBACK(popup_attach_button_pressed), compose);
6941 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6942 G_CALLBACK(attach_key_pressed), compose);
6945 gtk_drag_dest_set(attach_clist,
6946 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6947 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6948 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6949 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6950 G_CALLBACK(compose_attach_drag_received_cb),
6952 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6953 G_CALLBACK(compose_drag_drop),
6956 compose->attach_scrwin = attach_scrwin;
6957 compose->attach_clist = attach_clist;
6959 return attach_scrwin;
6962 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6963 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6965 static GtkWidget *compose_create_others(Compose *compose)
6968 GtkWidget *savemsg_checkbtn;
6969 GtkWidget *savemsg_combo;
6970 GtkWidget *savemsg_select;
6973 gchar *folderidentifier;
6975 /* Table for settings */
6976 table = gtk_table_new(3, 1, FALSE);
6977 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6978 gtk_widget_show(table);
6979 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6982 /* Save Message to folder */
6983 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6984 gtk_widget_show(savemsg_checkbtn);
6985 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6986 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6987 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6989 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6990 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6992 savemsg_combo = gtk_combo_box_entry_new_text();
6993 compose->savemsg_checkbtn = savemsg_checkbtn;
6994 compose->savemsg_combo = savemsg_combo;
6995 gtk_widget_show(savemsg_combo);
6997 if (prefs_common.compose_save_to_history)
6998 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6999 prefs_common.compose_save_to_history);
7001 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
7002 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
7003 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
7004 G_CALLBACK(compose_grab_focus_cb), compose);
7005 if (account_get_special_folder(compose->account, F_OUTBOX)) {
7006 folderidentifier = folder_item_get_identifier(account_get_special_folder
7007 (compose->account, F_OUTBOX));
7008 compose_set_save_to(compose, folderidentifier);
7009 g_free(folderidentifier);
7012 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
7013 gtk_widget_show(savemsg_select);
7014 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7015 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
7016 G_CALLBACK(compose_savemsg_select_cb),
7022 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
7024 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
7025 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
7028 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
7033 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
7036 path = folder_item_get_identifier(dest);
7038 compose_set_save_to(compose, path);
7042 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
7043 GdkAtom clip, GtkTextIter *insert_place);
7046 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
7050 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7052 if (event->button == 3) {
7054 GtkTextIter sel_start, sel_end;
7055 gboolean stuff_selected;
7057 /* move the cursor to allow GtkAspell to check the word
7058 * under the mouse */
7059 if (event->x && event->y) {
7060 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7061 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7063 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7066 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
7067 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7070 stuff_selected = gtk_text_buffer_get_selection_bounds(
7072 &sel_start, &sel_end);
7074 gtk_text_buffer_place_cursor (buffer, &iter);
7075 /* reselect stuff */
7077 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
7078 gtk_text_buffer_select_range(buffer,
7079 &sel_start, &sel_end);
7081 return FALSE; /* pass the event so that the right-click goes through */
7084 if (event->button == 2) {
7089 /* get the middle-click position to paste at the correct place */
7090 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7091 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7093 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7096 entry_paste_clipboard(compose, text,
7097 prefs_common.linewrap_pastes,
7098 GDK_SELECTION_PRIMARY, &iter);
7106 static void compose_spell_menu_changed(void *data)
7108 Compose *compose = (Compose *)data;
7110 GtkWidget *menuitem;
7111 GtkWidget *parent_item;
7112 GtkMenu *menu = GTK_MENU(gtk_menu_new());
7115 if (compose->gtkaspell == NULL)
7118 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
7119 "/Menu/Spelling/Options");
7121 /* setting the submenu removes /Spelling/Options from the factory
7122 * so we need to save it */
7124 if (parent_item == NULL) {
7125 parent_item = compose->aspell_options_menu;
7126 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
7128 compose->aspell_options_menu = parent_item;
7130 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
7132 spell_menu = g_slist_reverse(spell_menu);
7133 for (items = spell_menu;
7134 items; items = items->next) {
7135 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
7136 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
7137 gtk_widget_show(GTK_WIDGET(menuitem));
7139 g_slist_free(spell_menu);
7141 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7142 gtk_widget_show(parent_item);
7145 static void compose_dict_changed(void *data)
7147 Compose *compose = (Compose *) data;
7149 if(compose->gtkaspell &&
7150 compose->gtkaspell->recheck_when_changing_dict == FALSE)
7153 gtkaspell_highlight_all(compose->gtkaspell);
7154 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7158 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7160 Compose *compose = (Compose *)data;
7161 GdkEventButton event;
7164 event.time = gtk_get_current_event_time();
7168 return text_clicked(compose->text, &event, compose);
7171 static gboolean compose_force_window_origin = TRUE;
7172 static Compose *compose_create(PrefsAccount *account,
7181 GtkWidget *handlebox;
7183 GtkWidget *notebook;
7185 GtkWidget *attach_hbox;
7186 GtkWidget *attach_lab1;
7187 GtkWidget *attach_lab2;
7192 GtkWidget *subject_hbox;
7193 GtkWidget *subject_frame;
7194 GtkWidget *subject_entry;
7198 GtkWidget *edit_vbox;
7199 GtkWidget *ruler_hbox;
7201 GtkWidget *scrolledwin;
7203 GtkTextBuffer *buffer;
7204 GtkClipboard *clipboard;
7206 UndoMain *undostruct;
7208 GtkWidget *popupmenu;
7209 GtkWidget *tmpl_menu;
7210 GtkActionGroup *action_group = NULL;
7213 GtkAspell * gtkaspell = NULL;
7216 static GdkGeometry geometry;
7218 cm_return_val_if_fail(account != NULL, NULL);
7220 debug_print("Creating compose window...\n");
7221 compose = g_new0(Compose, 1);
7223 compose->batch = batch;
7224 compose->account = account;
7225 compose->folder = folder;
7227 compose->mutex = g_mutex_new();
7228 compose->set_cursor_pos = -1;
7230 #if !(GTK_CHECK_VERSION(2,12,0))
7231 compose->tooltips = tips;
7234 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7236 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7237 gtk_widget_set_size_request(window, prefs_common.compose_width,
7238 prefs_common.compose_height);
7240 if (!geometry.max_width) {
7241 geometry.max_width = gdk_screen_width();
7242 geometry.max_height = gdk_screen_height();
7245 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7246 &geometry, GDK_HINT_MAX_SIZE);
7247 if (!geometry.min_width) {
7248 geometry.min_width = 600;
7249 geometry.min_height = 440;
7251 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7252 &geometry, GDK_HINT_MIN_SIZE);
7254 #ifndef GENERIC_UMPC
7255 if (compose_force_window_origin)
7256 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7257 prefs_common.compose_y);
7259 g_signal_connect(G_OBJECT(window), "delete_event",
7260 G_CALLBACK(compose_delete_cb), compose);
7261 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7262 gtk_widget_realize(window);
7264 gtkut_widget_set_composer_icon(window);
7266 vbox = gtk_vbox_new(FALSE, 0);
7267 gtk_container_add(GTK_CONTAINER(window), vbox);
7269 compose->ui_manager = gtk_ui_manager_new();
7270 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7271 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7272 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7273 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7274 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7275 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7276 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7277 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7278 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7279 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7282 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7284 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7287 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7288 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7290 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7292 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7293 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7294 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7297 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7298 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7299 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7300 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7301 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7302 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7303 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7304 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7305 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7306 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM)
7307 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7308 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7311 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7312 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7313 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7315 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7316 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7317 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7319 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7320 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7321 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7322 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7324 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7326 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7327 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7328 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7329 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7330 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7331 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7332 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7333 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7334 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7335 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7336 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7337 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7338 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7339 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7340 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7342 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7344 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7345 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7346 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7347 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7348 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7350 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7352 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7356 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7357 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7358 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7359 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7360 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7361 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7365 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7366 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7367 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7368 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7369 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7371 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7372 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7373 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7374 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7375 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7378 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7379 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7380 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7381 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7382 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7383 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7384 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7386 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7387 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7388 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7389 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7390 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7392 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7394 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7395 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7396 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7397 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7398 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7400 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7401 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)
7402 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)
7403 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7405 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7407 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7408 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)
7409 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)
7411 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7413 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7414 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)
7415 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7417 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7418 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)
7419 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7421 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7423 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7424 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)
7425 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7426 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7427 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7429 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7430 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)
7431 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)
7432 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7433 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7435 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7436 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7437 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7438 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7439 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7440 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7442 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7443 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7444 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)
7446 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7447 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7448 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7452 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7453 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7454 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7455 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7456 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7457 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7460 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7462 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7463 gtk_widget_show_all(menubar);
7465 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7467 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7469 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7472 if (prefs_common.toolbar_detachable) {
7473 handlebox = gtk_handle_box_new();
7475 handlebox = gtk_hbox_new(FALSE, 0);
7477 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7479 gtk_widget_realize(handlebox);
7481 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7484 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7488 vbox2 = gtk_vbox_new(FALSE, 2);
7489 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7490 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7493 notebook = gtk_notebook_new();
7494 gtk_widget_set_size_request(notebook, -1, prefs_common.compose_notebook_height);
7495 gtk_widget_show(notebook);
7497 /* header labels and entries */
7498 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7499 compose_create_header(compose),
7500 gtk_label_new_with_mnemonic(_("Hea_der")));
7501 /* attachment list */
7502 attach_hbox = gtk_hbox_new(FALSE, 0);
7503 gtk_widget_show(attach_hbox);
7505 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7506 gtk_widget_show(attach_lab1);
7507 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7509 attach_lab2 = gtk_label_new("");
7510 gtk_widget_show(attach_lab2);
7511 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7513 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7514 compose_create_attach(compose),
7517 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7518 compose_create_others(compose),
7519 gtk_label_new_with_mnemonic(_("Othe_rs")));
7522 subject_hbox = gtk_hbox_new(FALSE, 0);
7523 gtk_widget_show(subject_hbox);
7525 subject_frame = gtk_frame_new(NULL);
7526 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7527 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7528 gtk_widget_show(subject_frame);
7530 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7531 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7532 gtk_widget_show(subject);
7534 label = gtk_label_new(_("Subject:"));
7535 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7536 gtk_widget_show(label);
7539 subject_entry = claws_spell_entry_new();
7541 subject_entry = gtk_entry_new();
7543 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7544 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7545 G_CALLBACK(compose_grab_focus_cb), compose);
7546 gtk_widget_show(subject_entry);
7547 compose->subject_entry = subject_entry;
7548 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7550 edit_vbox = gtk_vbox_new(FALSE, 0);
7552 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7555 ruler_hbox = gtk_hbox_new(FALSE, 0);
7556 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7558 ruler = gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL);
7559 gtk_shruler_set_range(GTK_SHRULER(ruler), 0.0, 100.0, 1.0);
7560 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7564 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7565 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7566 GTK_POLICY_AUTOMATIC,
7567 GTK_POLICY_AUTOMATIC);
7568 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7570 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7572 text = gtk_text_view_new();
7573 if (prefs_common.show_compose_margin) {
7574 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7575 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7577 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7578 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7579 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7580 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7581 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7583 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7584 g_signal_connect(G_OBJECT(notebook), "size_allocate",
7585 G_CALLBACK(compose_notebook_size_alloc), compose);
7586 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7587 G_CALLBACK(compose_edit_size_alloc),
7589 g_signal_connect(G_OBJECT(buffer), "changed",
7590 G_CALLBACK(compose_changed_cb), compose);
7591 g_signal_connect(G_OBJECT(text), "grab_focus",
7592 G_CALLBACK(compose_grab_focus_cb), compose);
7593 g_signal_connect(G_OBJECT(buffer), "insert_text",
7594 G_CALLBACK(text_inserted), compose);
7595 g_signal_connect(G_OBJECT(text), "button_press_event",
7596 G_CALLBACK(text_clicked), compose);
7598 g_signal_connect(G_OBJECT(text), "popup-menu",
7599 G_CALLBACK(compose_popup_menu), compose);
7601 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7602 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7603 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7604 G_CALLBACK(compose_popup_menu), compose);
7606 g_signal_connect(G_OBJECT(subject_entry), "changed",
7607 G_CALLBACK(compose_changed_cb), compose);
7610 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7611 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7612 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7613 g_signal_connect(G_OBJECT(text), "drag_data_received",
7614 G_CALLBACK(compose_insert_drag_received_cb),
7616 g_signal_connect(G_OBJECT(text), "drag-drop",
7617 G_CALLBACK(compose_drag_drop),
7619 g_signal_connect(G_OBJECT(text), "key-press-event",
7620 G_CALLBACK(completion_set_focus_to_subject),
7622 gtk_widget_show_all(vbox);
7624 /* pane between attach clist and text */
7625 paned = gtk_vpaned_new();
7626 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7628 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7629 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7631 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7633 gtk_paned_add1(GTK_PANED(paned), notebook);
7634 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7635 gtk_widget_show_all(paned);
7638 if (prefs_common.textfont) {
7639 PangoFontDescription *font_desc;
7641 font_desc = pango_font_description_from_string
7642 (prefs_common.textfont);
7644 gtk_widget_modify_font(text, font_desc);
7645 pango_font_description_free(font_desc);
7649 gtk_action_group_add_actions(action_group, compose_popup_entries,
7650 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7651 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7652 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7653 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7654 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7655 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7656 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7658 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7660 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7661 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7662 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7664 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7666 undostruct = undo_init(text);
7667 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7670 address_completion_start(window);
7672 compose->window = window;
7673 compose->vbox = vbox;
7674 compose->menubar = menubar;
7675 compose->handlebox = handlebox;
7677 compose->vbox2 = vbox2;
7679 compose->paned = paned;
7681 compose->attach_label = attach_lab2;
7683 compose->notebook = notebook;
7684 compose->edit_vbox = edit_vbox;
7685 compose->ruler_hbox = ruler_hbox;
7686 compose->ruler = ruler;
7687 compose->scrolledwin = scrolledwin;
7688 compose->text = text;
7690 compose->focused_editable = NULL;
7692 compose->popupmenu = popupmenu;
7694 compose->tmpl_menu = tmpl_menu;
7696 compose->mode = mode;
7697 compose->rmode = mode;
7699 compose->targetinfo = NULL;
7700 compose->replyinfo = NULL;
7701 compose->fwdinfo = NULL;
7703 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7704 g_str_equal, (GDestroyNotify) g_free, NULL);
7706 compose->replyto = NULL;
7708 compose->bcc = NULL;
7709 compose->followup_to = NULL;
7711 compose->ml_post = NULL;
7713 compose->inreplyto = NULL;
7714 compose->references = NULL;
7715 compose->msgid = NULL;
7716 compose->boundary = NULL;
7718 compose->autowrap = prefs_common.autowrap;
7719 compose->autoindent = prefs_common.auto_indent;
7720 compose->use_signing = FALSE;
7721 compose->use_encryption = FALSE;
7722 compose->privacy_system = NULL;
7724 compose->modified = FALSE;
7726 compose->return_receipt = FALSE;
7728 compose->to_list = NULL;
7729 compose->newsgroup_list = NULL;
7731 compose->undostruct = undostruct;
7733 compose->sig_str = NULL;
7735 compose->exteditor_file = NULL;
7736 compose->exteditor_pid = -1;
7737 compose->exteditor_tag = -1;
7738 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7741 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7742 if (mode != COMPOSE_REDIRECT) {
7743 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7744 strcmp(prefs_common.dictionary, "")) {
7745 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7746 prefs_common.alt_dictionary,
7747 conv_get_locale_charset_str(),
7748 prefs_common.misspelled_col,
7749 prefs_common.check_while_typing,
7750 prefs_common.recheck_when_changing_dict,
7751 prefs_common.use_alternate,
7752 prefs_common.use_both_dicts,
7753 GTK_TEXT_VIEW(text),
7754 GTK_WINDOW(compose->window),
7755 compose_dict_changed,
7756 compose_spell_menu_changed,
7759 alertpanel_error(_("Spell checker could not "
7761 gtkaspell_checkers_strerror());
7762 gtkaspell_checkers_reset_error();
7764 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7768 compose->gtkaspell = gtkaspell;
7769 compose_spell_menu_changed(compose);
7770 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7773 compose_select_account(compose, account, TRUE);
7775 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7776 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7778 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7779 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7781 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7782 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7784 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7785 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7787 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7788 if (account->protocol != A_NNTP)
7789 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7790 prefs_common_translated_header_name("To:"));
7792 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7793 prefs_common_translated_header_name("Newsgroups:"));
7795 #ifndef USE_NEW_ADDRBOOK
7796 addressbook_set_target_compose(compose);
7798 if (mode != COMPOSE_REDIRECT)
7799 compose_set_template_menu(compose);
7801 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7804 compose_list = g_list_append(compose_list, compose);
7806 if (!prefs_common.show_ruler)
7807 gtk_widget_hide(ruler_hbox);
7809 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7812 compose->priority = PRIORITY_NORMAL;
7813 compose_update_priority_menu_item(compose);
7815 compose_set_out_encoding(compose);
7818 compose_update_actions_menu(compose);
7820 /* Privacy Systems menu */
7821 compose_update_privacy_systems_menu(compose);
7823 activate_privacy_system(compose, account, TRUE);
7824 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7826 gtk_widget_realize(window);
7828 gtk_widget_show(window);
7830 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7831 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7838 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7843 GtkWidget *optmenubox;
7846 GtkWidget *from_name = NULL;
7847 #if !(GTK_CHECK_VERSION(2,12,0))
7848 GtkTooltips *tips = compose->tooltips;
7851 gint num = 0, def_menu = 0;
7853 accounts = account_get_list();
7854 cm_return_val_if_fail(accounts != NULL, NULL);
7856 optmenubox = gtk_event_box_new();
7857 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7858 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7860 hbox = gtk_hbox_new(FALSE, 6);
7861 from_name = gtk_entry_new();
7863 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7864 G_CALLBACK(compose_grab_focus_cb), compose);
7866 for (; accounts != NULL; accounts = accounts->next, num++) {
7867 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7868 gchar *name, *from = NULL;
7870 if (ac == compose->account) def_menu = num;
7872 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7875 if (ac == compose->account) {
7876 if (ac->name && *ac->name) {
7878 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7879 from = g_strdup_printf("%s <%s>",
7881 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7883 from = g_strdup_printf("%s",
7885 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7888 COMBOBOX_ADD(menu, name, ac->account_id);
7893 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7895 g_signal_connect(G_OBJECT(optmenu), "changed",
7896 G_CALLBACK(account_activated),
7898 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7899 G_CALLBACK(compose_entry_popup_extend),
7902 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7903 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7905 CLAWS_SET_TIP(optmenubox,
7906 _("Account to use for this email"));
7907 CLAWS_SET_TIP(from_name,
7908 _("Sender address to be used"));
7910 compose->account_combo = optmenu;
7911 compose->from_name = from_name;
7916 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7918 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7919 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7920 Compose *compose = (Compose *) data;
7922 compose->priority = value;
7926 static void compose_reply_change_mode(Compose *compose,
7929 gboolean was_modified = compose->modified;
7931 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7933 cm_return_if_fail(compose->replyinfo != NULL);
7935 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7937 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7939 if (action == COMPOSE_REPLY_TO_ALL)
7941 if (action == COMPOSE_REPLY_TO_SENDER)
7943 if (action == COMPOSE_REPLY_TO_LIST)
7946 compose_remove_header_entries(compose);
7947 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7948 if (compose->account->set_autocc && compose->account->auto_cc)
7949 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7951 if (compose->account->set_autobcc && compose->account->auto_bcc)
7952 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7954 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7955 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7956 compose_show_first_last_header(compose, TRUE);
7957 compose->modified = was_modified;
7958 compose_set_title(compose);
7961 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7963 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7964 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7965 Compose *compose = (Compose *) data;
7968 compose_reply_change_mode(compose, value);
7971 static void compose_update_priority_menu_item(Compose * compose)
7973 GtkWidget *menuitem = NULL;
7974 switch (compose->priority) {
7975 case PRIORITY_HIGHEST:
7976 menuitem = gtk_ui_manager_get_widget
7977 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7980 menuitem = gtk_ui_manager_get_widget
7981 (compose->ui_manager, "/Menu/Options/Priority/High");
7983 case PRIORITY_NORMAL:
7984 menuitem = gtk_ui_manager_get_widget
7985 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7988 menuitem = gtk_ui_manager_get_widget
7989 (compose->ui_manager, "/Menu/Options/Priority/Low");
7991 case PRIORITY_LOWEST:
7992 menuitem = gtk_ui_manager_get_widget
7993 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7996 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7999 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
8001 Compose *compose = (Compose *) data;
8003 gboolean can_sign = FALSE, can_encrypt = FALSE;
8005 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
8007 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
8010 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
8011 g_free(compose->privacy_system);
8012 compose->privacy_system = NULL;
8013 if (systemid != NULL) {
8014 compose->privacy_system = g_strdup(systemid);
8016 can_sign = privacy_system_can_sign(systemid);
8017 can_encrypt = privacy_system_can_encrypt(systemid);
8020 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
8022 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8023 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8026 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
8028 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8029 GtkWidget *menuitem = NULL;
8030 GList *children, *amenu;
8031 gboolean can_sign = FALSE, can_encrypt = FALSE;
8032 gboolean found = FALSE;
8034 if (compose->privacy_system != NULL) {
8036 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8037 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
8038 cm_return_if_fail(menuitem != NULL);
8040 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
8043 while (amenu != NULL) {
8044 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
8045 if (systemid != NULL) {
8046 if (strcmp(systemid, compose->privacy_system) == 0 &&
8047 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8048 menuitem = GTK_WIDGET(amenu->data);
8050 can_sign = privacy_system_can_sign(systemid);
8051 can_encrypt = privacy_system_can_encrypt(systemid);
8055 } else if (strlen(compose->privacy_system) == 0 &&
8056 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8057 menuitem = GTK_WIDGET(amenu->data);
8060 can_encrypt = FALSE;
8065 amenu = amenu->next;
8067 g_list_free(children);
8068 if (menuitem != NULL)
8069 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8071 if (warn && !found && strlen(compose->privacy_system)) {
8072 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8073 "will not be able to sign or encrypt this message."),
8074 compose->privacy_system);
8078 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8079 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8082 static void compose_set_out_encoding(Compose *compose)
8084 CharSet out_encoding;
8085 const gchar *branch = NULL;
8086 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
8088 switch(out_encoding) {
8089 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8090 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
8091 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
8092 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
8093 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
8094 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
8095 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
8096 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
8097 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
8098 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
8099 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
8100 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
8101 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
8102 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
8103 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
8104 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
8105 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
8106 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
8107 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
8108 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
8109 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
8110 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
8111 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
8112 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
8113 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
8114 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
8115 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
8116 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
8117 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
8118 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
8119 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
8120 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
8121 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8123 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8126 static void compose_set_template_menu(Compose *compose)
8128 GSList *tmpl_list, *cur;
8132 tmpl_list = template_get_config();
8134 menu = gtk_menu_new();
8136 gtk_menu_set_accel_group (GTK_MENU (menu),
8137 gtk_ui_manager_get_accel_group(compose->ui_manager));
8138 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8139 Template *tmpl = (Template *)cur->data;
8140 gchar *accel_path = NULL;
8141 item = gtk_menu_item_new_with_label(tmpl->name);
8142 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8143 g_signal_connect(G_OBJECT(item), "activate",
8144 G_CALLBACK(compose_template_activate_cb),
8146 g_object_set_data(G_OBJECT(item), "template", tmpl);
8147 gtk_widget_show(item);
8148 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8149 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8153 gtk_widget_show(menu);
8154 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8157 void compose_update_actions_menu(Compose *compose)
8159 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8162 static void compose_update_privacy_systems_menu(Compose *compose)
8164 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8165 GSList *systems, *cur;
8167 GtkWidget *system_none;
8169 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8170 GtkWidget *privacy_menu = gtk_menu_new();
8172 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8173 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8175 g_signal_connect(G_OBJECT(system_none), "activate",
8176 G_CALLBACK(compose_set_privacy_system_cb), compose);
8178 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8179 gtk_widget_show(system_none);
8181 systems = privacy_get_system_ids();
8182 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8183 gchar *systemid = cur->data;
8185 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8186 widget = gtk_radio_menu_item_new_with_label(group,
8187 privacy_system_get_name(systemid));
8188 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8189 g_strdup(systemid), g_free);
8190 g_signal_connect(G_OBJECT(widget), "activate",
8191 G_CALLBACK(compose_set_privacy_system_cb), compose);
8193 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8194 gtk_widget_show(widget);
8197 g_slist_free(systems);
8198 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8199 gtk_widget_show_all(privacy_menu);
8200 gtk_widget_show_all(privacy_menuitem);
8203 void compose_reflect_prefs_all(void)
8208 for (cur = compose_list; cur != NULL; cur = cur->next) {
8209 compose = (Compose *)cur->data;
8210 compose_set_template_menu(compose);
8214 void compose_reflect_prefs_pixmap_theme(void)
8219 for (cur = compose_list; cur != NULL; cur = cur->next) {
8220 compose = (Compose *)cur->data;
8221 toolbar_update(TOOLBAR_COMPOSE, compose);
8225 static const gchar *compose_quote_char_from_context(Compose *compose)
8227 const gchar *qmark = NULL;
8229 cm_return_val_if_fail(compose != NULL, NULL);
8231 switch (compose->mode) {
8232 /* use forward-specific quote char */
8233 case COMPOSE_FORWARD:
8234 case COMPOSE_FORWARD_AS_ATTACH:
8235 case COMPOSE_FORWARD_INLINE:
8236 if (compose->folder && compose->folder->prefs &&
8237 compose->folder->prefs->forward_with_format)
8238 qmark = compose->folder->prefs->forward_quotemark;
8239 else if (compose->account->forward_with_format)
8240 qmark = compose->account->forward_quotemark;
8242 qmark = prefs_common.fw_quotemark;
8245 /* use reply-specific quote char in all other modes */
8247 if (compose->folder && compose->folder->prefs &&
8248 compose->folder->prefs->reply_with_format)
8249 qmark = compose->folder->prefs->reply_quotemark;
8250 else if (compose->account->reply_with_format)
8251 qmark = compose->account->reply_quotemark;
8253 qmark = prefs_common.quotemark;
8257 if (qmark == NULL || *qmark == '\0')
8263 static void compose_template_apply(Compose *compose, Template *tmpl,
8267 GtkTextBuffer *buffer;
8271 gchar *parsed_str = NULL;
8272 gint cursor_pos = 0;
8273 const gchar *err_msg = _("The body of the template has an error at line %d.");
8276 /* process the body */
8278 text = GTK_TEXT_VIEW(compose->text);
8279 buffer = gtk_text_view_get_buffer(text);
8282 qmark = compose_quote_char_from_context(compose);
8284 if (compose->replyinfo != NULL) {
8287 gtk_text_buffer_set_text(buffer, "", -1);
8288 mark = gtk_text_buffer_get_insert(buffer);
8289 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8291 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8292 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8294 } else if (compose->fwdinfo != NULL) {
8297 gtk_text_buffer_set_text(buffer, "", -1);
8298 mark = gtk_text_buffer_get_insert(buffer);
8299 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8301 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8302 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8305 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8307 GtkTextIter start, end;
8310 gtk_text_buffer_get_start_iter(buffer, &start);
8311 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8312 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8314 /* clear the buffer now */
8316 gtk_text_buffer_set_text(buffer, "", -1);
8318 parsed_str = compose_quote_fmt(compose, dummyinfo,
8319 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8320 procmsg_msginfo_free( dummyinfo );
8326 gtk_text_buffer_set_text(buffer, "", -1);
8327 mark = gtk_text_buffer_get_insert(buffer);
8328 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8331 if (replace && parsed_str && compose->account->auto_sig)
8332 compose_insert_sig(compose, FALSE);
8334 if (replace && parsed_str) {
8335 gtk_text_buffer_get_start_iter(buffer, &iter);
8336 gtk_text_buffer_place_cursor(buffer, &iter);
8340 cursor_pos = quote_fmt_get_cursor_pos();
8341 compose->set_cursor_pos = cursor_pos;
8342 if (cursor_pos == -1)
8344 gtk_text_buffer_get_start_iter(buffer, &iter);
8345 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8346 gtk_text_buffer_place_cursor(buffer, &iter);
8349 /* process the other fields */
8351 compose_template_apply_fields(compose, tmpl);
8352 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8353 quote_fmt_reset_vartable();
8354 compose_changed_cb(NULL, compose);
8357 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8358 gtkaspell_highlight_all(compose->gtkaspell);
8362 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8364 MsgInfo* dummyinfo = NULL;
8365 MsgInfo *msginfo = NULL;
8368 if (compose->replyinfo != NULL)
8369 msginfo = compose->replyinfo;
8370 else if (compose->fwdinfo != NULL)
8371 msginfo = compose->fwdinfo;
8373 dummyinfo = compose_msginfo_new_from_compose(compose);
8374 msginfo = dummyinfo;
8377 if (tmpl->from && *tmpl->from != '\0') {
8379 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8380 compose->gtkaspell);
8382 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8384 quote_fmt_scan_string(tmpl->from);
8387 buf = quote_fmt_get_buffer();
8389 alertpanel_error(_("Template From format error."));
8391 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8395 if (tmpl->to && *tmpl->to != '\0') {
8397 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8398 compose->gtkaspell);
8400 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8402 quote_fmt_scan_string(tmpl->to);
8405 buf = quote_fmt_get_buffer();
8407 alertpanel_error(_("Template To format error."));
8409 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8413 if (tmpl->cc && *tmpl->cc != '\0') {
8415 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8416 compose->gtkaspell);
8418 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8420 quote_fmt_scan_string(tmpl->cc);
8423 buf = quote_fmt_get_buffer();
8425 alertpanel_error(_("Template Cc format error."));
8427 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8431 if (tmpl->bcc && *tmpl->bcc != '\0') {
8433 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8434 compose->gtkaspell);
8436 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8438 quote_fmt_scan_string(tmpl->bcc);
8441 buf = quote_fmt_get_buffer();
8443 alertpanel_error(_("Template Bcc format error."));
8445 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8449 /* process the subject */
8450 if (tmpl->subject && *tmpl->subject != '\0') {
8452 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8453 compose->gtkaspell);
8455 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8457 quote_fmt_scan_string(tmpl->subject);
8460 buf = quote_fmt_get_buffer();
8462 alertpanel_error(_("Template subject format error."));
8464 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8468 procmsg_msginfo_free( dummyinfo );
8471 static void compose_destroy(Compose *compose)
8473 GtkAllocation allocation;
8474 GtkTextBuffer *buffer;
8475 GtkClipboard *clipboard;
8477 compose_list = g_list_remove(compose_list, compose);
8479 if (compose->updating) {
8480 debug_print("danger, not destroying anything now\n");
8481 compose->deferred_destroy = TRUE;
8484 /* NOTE: address_completion_end() does nothing with the window
8485 * however this may change. */
8486 address_completion_end(compose->window);
8488 slist_free_strings(compose->to_list);
8489 g_slist_free(compose->to_list);
8490 slist_free_strings(compose->newsgroup_list);
8491 g_slist_free(compose->newsgroup_list);
8492 slist_free_strings(compose->header_list);
8493 g_slist_free(compose->header_list);
8495 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8497 g_hash_table_destroy(compose->email_hashtable);
8499 procmsg_msginfo_free(compose->targetinfo);
8500 procmsg_msginfo_free(compose->replyinfo);
8501 procmsg_msginfo_free(compose->fwdinfo);
8503 g_free(compose->replyto);
8504 g_free(compose->cc);
8505 g_free(compose->bcc);
8506 g_free(compose->newsgroups);
8507 g_free(compose->followup_to);
8509 g_free(compose->ml_post);
8511 g_free(compose->inreplyto);
8512 g_free(compose->references);
8513 g_free(compose->msgid);
8514 g_free(compose->boundary);
8516 g_free(compose->redirect_filename);
8517 if (compose->undostruct)
8518 undo_destroy(compose->undostruct);
8520 g_free(compose->sig_str);
8522 g_free(compose->exteditor_file);
8524 g_free(compose->orig_charset);
8526 g_free(compose->privacy_system);
8528 #ifndef USE_NEW_ADDRBOOK
8529 if (addressbook_get_target_compose() == compose)
8530 addressbook_set_target_compose(NULL);
8533 if (compose->gtkaspell) {
8534 gtkaspell_delete(compose->gtkaspell);
8535 compose->gtkaspell = NULL;
8539 if (!compose->batch) {
8540 gtk_widget_get_allocation(compose->window, &allocation);
8541 prefs_common.compose_width = allocation.width;
8542 prefs_common.compose_height = allocation.height;
8545 if (!gtk_widget_get_parent(compose->paned))
8546 gtk_widget_destroy(compose->paned);
8547 gtk_widget_destroy(compose->popupmenu);
8549 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8550 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8551 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8553 gtk_widget_destroy(compose->window);
8554 toolbar_destroy(compose->toolbar);
8555 g_free(compose->toolbar);
8556 g_mutex_free(compose->mutex);
8560 static void compose_attach_info_free(AttachInfo *ainfo)
8562 g_free(ainfo->file);
8563 g_free(ainfo->content_type);
8564 g_free(ainfo->name);
8565 g_free(ainfo->charset);
8569 static void compose_attach_update_label(Compose *compose)
8574 GtkTreeModel *model;
8579 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8580 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8581 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8585 while(gtk_tree_model_iter_next(model, &iter))
8588 text = g_strdup_printf("(%d)", i);
8589 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8593 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8595 Compose *compose = (Compose *)data;
8596 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8597 GtkTreeSelection *selection;
8599 GtkTreeModel *model;
8601 selection = gtk_tree_view_get_selection(tree_view);
8602 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8607 for (cur = sel; cur != NULL; cur = cur->next) {
8608 GtkTreePath *path = cur->data;
8609 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8612 gtk_tree_path_free(path);
8615 for (cur = sel; cur != NULL; cur = cur->next) {
8616 GtkTreeRowReference *ref = cur->data;
8617 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8620 if (gtk_tree_model_get_iter(model, &iter, path))
8621 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8623 gtk_tree_path_free(path);
8624 gtk_tree_row_reference_free(ref);
8628 compose_attach_update_label(compose);
8631 static struct _AttachProperty
8634 GtkWidget *mimetype_entry;
8635 GtkWidget *encoding_optmenu;
8636 GtkWidget *path_entry;
8637 GtkWidget *filename_entry;
8639 GtkWidget *cancel_btn;
8642 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8644 gtk_tree_path_free((GtkTreePath *)ptr);
8647 static void compose_attach_property(GtkAction *action, gpointer data)
8649 Compose *compose = (Compose *)data;
8650 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8652 GtkComboBox *optmenu;
8653 GtkTreeSelection *selection;
8655 GtkTreeModel *model;
8658 static gboolean cancelled;
8660 /* only if one selected */
8661 selection = gtk_tree_view_get_selection(tree_view);
8662 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8665 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8669 path = (GtkTreePath *) sel->data;
8670 gtk_tree_model_get_iter(model, &iter, path);
8671 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8674 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8680 if (!attach_prop.window)
8681 compose_attach_property_create(&cancelled);
8682 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8683 gtk_widget_grab_focus(attach_prop.ok_btn);
8684 gtk_widget_show(attach_prop.window);
8685 gtk_window_set_transient_for(GTK_WINDOW(attach_prop.window),
8686 GTK_WINDOW(compose->window));
8688 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8689 if (ainfo->encoding == ENC_UNKNOWN)
8690 combobox_select_by_data(optmenu, ENC_BASE64);
8692 combobox_select_by_data(optmenu, ainfo->encoding);
8694 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8695 ainfo->content_type ? ainfo->content_type : "");
8696 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8697 ainfo->file ? ainfo->file : "");
8698 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8699 ainfo->name ? ainfo->name : "");
8702 const gchar *entry_text;
8704 gchar *cnttype = NULL;
8711 gtk_widget_hide(attach_prop.window);
8712 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8717 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8718 if (*entry_text != '\0') {
8721 text = g_strstrip(g_strdup(entry_text));
8722 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8723 cnttype = g_strdup(text);
8726 alertpanel_error(_("Invalid MIME type."));
8732 ainfo->encoding = combobox_get_active_data(optmenu);
8734 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8735 if (*entry_text != '\0') {
8736 if (is_file_exist(entry_text) &&
8737 (size = get_file_size(entry_text)) > 0)
8738 file = g_strdup(entry_text);
8741 (_("File doesn't exist or is empty."));
8747 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8748 if (*entry_text != '\0') {
8749 g_free(ainfo->name);
8750 ainfo->name = g_strdup(entry_text);
8754 g_free(ainfo->content_type);
8755 ainfo->content_type = cnttype;
8758 g_free(ainfo->file);
8762 ainfo->size = (goffset)size;
8764 /* update tree store */
8765 text = to_human_readable(ainfo->size);
8766 gtk_tree_model_get_iter(model, &iter, path);
8767 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8768 COL_MIMETYPE, ainfo->content_type,
8770 COL_NAME, ainfo->name,
8771 COL_CHARSET, ainfo->charset,
8777 gtk_tree_path_free(path);
8780 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8782 label = gtk_label_new(str); \
8783 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8784 GTK_FILL, 0, 0, 0); \
8785 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8787 entry = gtk_entry_new(); \
8788 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8789 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8792 static void compose_attach_property_create(gboolean *cancelled)
8798 GtkWidget *mimetype_entry;
8801 GtkListStore *optmenu_menu;
8802 GtkWidget *path_entry;
8803 GtkWidget *filename_entry;
8806 GtkWidget *cancel_btn;
8807 GList *mime_type_list, *strlist;
8810 debug_print("Creating attach_property window...\n");
8812 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8813 gtk_widget_set_size_request(window, 480, -1);
8814 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8815 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8816 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8817 g_signal_connect(G_OBJECT(window), "delete_event",
8818 G_CALLBACK(attach_property_delete_event),
8820 g_signal_connect(G_OBJECT(window), "key_press_event",
8821 G_CALLBACK(attach_property_key_pressed),
8824 vbox = gtk_vbox_new(FALSE, 8);
8825 gtk_container_add(GTK_CONTAINER(window), vbox);
8827 table = gtk_table_new(4, 2, FALSE);
8828 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8829 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8830 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8832 label = gtk_label_new(_("MIME type"));
8833 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8835 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8836 mimetype_entry = gtk_combo_box_entry_new_text();
8837 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8838 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8840 /* stuff with list */
8841 mime_type_list = procmime_get_mime_type_list();
8843 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8844 MimeType *type = (MimeType *) mime_type_list->data;
8847 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8849 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8852 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8853 (GCompareFunc)strcmp2);
8856 for (mime_type_list = strlist; mime_type_list != NULL;
8857 mime_type_list = mime_type_list->next) {
8858 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8859 g_free(mime_type_list->data);
8861 g_list_free(strlist);
8862 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8863 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8865 label = gtk_label_new(_("Encoding"));
8866 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8868 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8870 hbox = gtk_hbox_new(FALSE, 0);
8871 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8872 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8874 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8875 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8877 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8878 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8879 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8880 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8881 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8883 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8885 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8886 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8888 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8889 &ok_btn, GTK_STOCK_OK,
8891 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8892 gtk_widget_grab_default(ok_btn);
8894 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8895 G_CALLBACK(attach_property_ok),
8897 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8898 G_CALLBACK(attach_property_cancel),
8901 gtk_widget_show_all(vbox);
8903 attach_prop.window = window;
8904 attach_prop.mimetype_entry = mimetype_entry;
8905 attach_prop.encoding_optmenu = optmenu;
8906 attach_prop.path_entry = path_entry;
8907 attach_prop.filename_entry = filename_entry;
8908 attach_prop.ok_btn = ok_btn;
8909 attach_prop.cancel_btn = cancel_btn;
8912 #undef SET_LABEL_AND_ENTRY
8914 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8920 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8926 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8927 gboolean *cancelled)
8935 static gboolean attach_property_key_pressed(GtkWidget *widget,
8937 gboolean *cancelled)
8939 if (event && event->keyval == GDK_KEY_Escape) {
8943 if (event && event->keyval == GDK_KEY_Return) {
8951 static void compose_exec_ext_editor(Compose *compose)
8958 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8959 G_DIR_SEPARATOR, compose);
8961 if (pipe(pipe_fds) < 0) {
8967 if ((pid = fork()) < 0) {
8974 /* close the write side of the pipe */
8977 compose->exteditor_file = g_strdup(tmp);
8978 compose->exteditor_pid = pid;
8980 compose_set_ext_editor_sensitive(compose, FALSE);
8983 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8985 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8987 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8991 } else { /* process-monitoring process */
8997 /* close the read side of the pipe */
9000 if (compose_write_body_to_file(compose, tmp) < 0) {
9001 fd_write_all(pipe_fds[1], "2\n", 2);
9005 pid_ed = compose_exec_ext_editor_real(tmp);
9007 fd_write_all(pipe_fds[1], "1\n", 2);
9011 /* wait until editor is terminated */
9012 waitpid(pid_ed, NULL, 0);
9014 fd_write_all(pipe_fds[1], "0\n", 2);
9021 #endif /* G_OS_UNIX */
9025 static gint compose_exec_ext_editor_real(const gchar *file)
9032 cm_return_val_if_fail(file != NULL, -1);
9034 if ((pid = fork()) < 0) {
9039 if (pid != 0) return pid;
9041 /* grandchild process */
9043 if (setpgid(0, getppid()))
9046 if (prefs_common_get_ext_editor_cmd() &&
9047 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
9048 *(p + 1) == 's' && !strchr(p + 2, '%')) {
9049 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
9051 if (prefs_common_get_ext_editor_cmd())
9052 g_warning("External editor command-line is invalid: '%s'\n",
9053 prefs_common_get_ext_editor_cmd());
9054 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
9057 cmdline = strsplit_with_quote(buf, " ", 1024);
9058 execvp(cmdline[0], cmdline);
9061 g_strfreev(cmdline);
9066 static gboolean compose_ext_editor_kill(Compose *compose)
9068 pid_t pgid = compose->exteditor_pid * -1;
9071 ret = kill(pgid, 0);
9073 if (ret == 0 || (ret == -1 && EPERM == errno)) {
9077 msg = g_strdup_printf
9078 (_("The external editor is still working.\n"
9079 "Force terminating the process?\n"
9080 "process group id: %d"), -pgid);
9081 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
9082 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
9086 if (val == G_ALERTALTERNATE) {
9087 g_source_remove(compose->exteditor_tag);
9088 g_io_channel_shutdown(compose->exteditor_ch,
9090 g_io_channel_unref(compose->exteditor_ch);
9092 if (kill(pgid, SIGTERM) < 0) perror("kill");
9093 waitpid(compose->exteditor_pid, NULL, 0);
9095 g_warning("Terminated process group id: %d", -pgid);
9096 g_warning("Temporary file: %s",
9097 compose->exteditor_file);
9099 compose_set_ext_editor_sensitive(compose, TRUE);
9101 g_free(compose->exteditor_file);
9102 compose->exteditor_file = NULL;
9103 compose->exteditor_pid = -1;
9104 compose->exteditor_ch = NULL;
9105 compose->exteditor_tag = -1;
9113 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
9117 Compose *compose = (Compose *)data;
9120 debug_print("Compose: input from monitoring process\n");
9122 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
9124 g_io_channel_shutdown(source, FALSE, NULL);
9125 g_io_channel_unref(source);
9127 waitpid(compose->exteditor_pid, NULL, 0);
9129 if (buf[0] == '0') { /* success */
9130 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9131 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9133 gtk_text_buffer_set_text(buffer, "", -1);
9134 compose_insert_file(compose, compose->exteditor_file);
9135 compose_changed_cb(NULL, compose);
9136 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9138 if (claws_unlink(compose->exteditor_file) < 0)
9139 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9140 } else if (buf[0] == '1') { /* failed */
9141 g_warning("Couldn't exec external editor\n");
9142 if (claws_unlink(compose->exteditor_file) < 0)
9143 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9144 } else if (buf[0] == '2') {
9145 g_warning("Couldn't write to file\n");
9146 } else if (buf[0] == '3') {
9147 g_warning("Pipe read failed\n");
9150 compose_set_ext_editor_sensitive(compose, TRUE);
9152 g_free(compose->exteditor_file);
9153 compose->exteditor_file = NULL;
9154 compose->exteditor_pid = -1;
9155 compose->exteditor_ch = NULL;
9156 compose->exteditor_tag = -1;
9161 static void compose_set_ext_editor_sensitive(Compose *compose,
9164 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9165 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9166 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9167 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9168 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9169 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9170 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9172 gtk_widget_set_sensitive(compose->text, sensitive);
9173 if (compose->toolbar->send_btn)
9174 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
9175 if (compose->toolbar->sendl_btn)
9176 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
9177 if (compose->toolbar->draft_btn)
9178 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
9179 if (compose->toolbar->insert_btn)
9180 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
9181 if (compose->toolbar->sig_btn)
9182 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
9183 if (compose->toolbar->exteditor_btn)
9184 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9185 if (compose->toolbar->linewrap_current_btn)
9186 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9187 if (compose->toolbar->linewrap_all_btn)
9188 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9190 #endif /* G_OS_UNIX */
9193 * compose_undo_state_changed:
9195 * Change the sensivity of the menuentries undo and redo
9197 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9198 gint redo_state, gpointer data)
9200 Compose *compose = (Compose *)data;
9202 switch (undo_state) {
9203 case UNDO_STATE_TRUE:
9204 if (!undostruct->undo_state) {
9205 undostruct->undo_state = TRUE;
9206 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9209 case UNDO_STATE_FALSE:
9210 if (undostruct->undo_state) {
9211 undostruct->undo_state = FALSE;
9212 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9215 case UNDO_STATE_UNCHANGED:
9217 case UNDO_STATE_REFRESH:
9218 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9221 g_warning("Undo state not recognized");
9225 switch (redo_state) {
9226 case UNDO_STATE_TRUE:
9227 if (!undostruct->redo_state) {
9228 undostruct->redo_state = TRUE;
9229 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9232 case UNDO_STATE_FALSE:
9233 if (undostruct->redo_state) {
9234 undostruct->redo_state = FALSE;
9235 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9238 case UNDO_STATE_UNCHANGED:
9240 case UNDO_STATE_REFRESH:
9241 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9244 g_warning("Redo state not recognized");
9249 /* callback functions */
9251 static void compose_notebook_size_alloc(GtkNotebook *notebook,
9252 GtkAllocation *allocation,
9255 prefs_common.compose_notebook_height = allocation->height;
9258 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9259 * includes "non-client" (windows-izm) in calculation, so this calculation
9260 * may not be accurate.
9262 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9263 GtkAllocation *allocation,
9264 GtkSHRuler *shruler)
9266 if (prefs_common.show_ruler) {
9267 gint char_width = 0, char_height = 0;
9268 gint line_width_in_chars;
9270 gtkut_get_font_size(GTK_WIDGET(widget),
9271 &char_width, &char_height);
9272 line_width_in_chars =
9273 (allocation->width - allocation->x) / char_width;
9275 /* got the maximum */
9276 gtk_shruler_set_range(GTK_SHRULER(shruler),
9277 0.0, line_width_in_chars, 0);
9286 ComposePrefType type;
9287 gboolean entry_marked;
9290 static void account_activated(GtkComboBox *optmenu, gpointer data)
9292 Compose *compose = (Compose *)data;
9295 gchar *folderidentifier;
9296 gint account_id = 0;
9299 GSList *list, *saved_list = NULL;
9300 HeaderEntryState *state;
9301 GtkRcStyle *style = NULL;
9302 static GdkColor yellow;
9303 static gboolean color_set = FALSE;
9305 /* Get ID of active account in the combo box */
9306 menu = gtk_combo_box_get_model(optmenu);
9307 gtk_combo_box_get_active_iter(optmenu, &iter);
9308 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9310 ac = account_find_from_id(account_id);
9311 cm_return_if_fail(ac != NULL);
9313 if (ac != compose->account) {
9314 compose_select_account(compose, ac, FALSE);
9316 for (list = compose->header_list; list; list = list->next) {
9317 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9319 if (hentry->type == PREF_ACCOUNT || !list->next) {
9320 compose_destroy_headerentry(compose, hentry);
9324 state = g_malloc0(sizeof(HeaderEntryState));
9325 state->header = gtk_editable_get_chars(GTK_EDITABLE(
9326 gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9327 state->entry = gtk_editable_get_chars(
9328 GTK_EDITABLE(hentry->entry), 0, -1);
9329 state->type = hentry->type;
9332 gdk_color_parse("#f5f6be", &yellow);
9333 color_set = gdk_colormap_alloc_color(
9334 gdk_colormap_get_system(),
9335 &yellow, FALSE, TRUE);
9338 style = gtk_widget_get_modifier_style(hentry->entry);
9339 state->entry_marked = gdk_color_equal(&yellow,
9340 &style->base[GTK_STATE_NORMAL]);
9342 saved_list = g_slist_append(saved_list, state);
9343 compose_destroy_headerentry(compose, hentry);
9346 compose->header_last = NULL;
9347 g_slist_free(compose->header_list);
9348 compose->header_list = NULL;
9349 compose->header_nextrow = 1;
9350 compose_create_header_entry(compose);
9352 if (ac->set_autocc && ac->auto_cc)
9353 compose_entry_append(compose, ac->auto_cc,
9354 COMPOSE_CC, PREF_ACCOUNT);
9356 if (ac->set_autobcc && ac->auto_bcc)
9357 compose_entry_append(compose, ac->auto_bcc,
9358 COMPOSE_BCC, PREF_ACCOUNT);
9360 if (ac->set_autoreplyto && ac->auto_replyto)
9361 compose_entry_append(compose, ac->auto_replyto,
9362 COMPOSE_REPLYTO, PREF_ACCOUNT);
9364 for (list = saved_list; list; list = list->next) {
9365 state = (HeaderEntryState *) list->data;
9367 compose_add_header_entry(compose, state->header,
9368 state->entry, state->type);
9369 if (state->entry_marked)
9370 compose_entry_mark_default_to(compose, state->entry);
9372 g_free(state->header);
9373 g_free(state->entry);
9376 g_slist_free(saved_list);
9378 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9379 (ac->protocol == A_NNTP) ?
9380 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9383 /* Set message save folder */
9384 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9385 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9387 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9388 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9390 compose_set_save_to(compose, NULL);
9391 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9392 folderidentifier = folder_item_get_identifier(account_get_special_folder
9393 (compose->account, F_OUTBOX));
9394 compose_set_save_to(compose, folderidentifier);
9395 g_free(folderidentifier);
9399 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9400 GtkTreeViewColumn *column, Compose *compose)
9402 compose_attach_property(NULL, compose);
9405 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9408 Compose *compose = (Compose *)data;
9409 GtkTreeSelection *attach_selection;
9410 gint attach_nr_selected;
9412 if (!event) return FALSE;
9414 if (event->button == 3) {
9415 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9416 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9418 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", (attach_nr_selected > 0));
9419 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", (attach_nr_selected > 0));
9421 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9422 NULL, NULL, event->button, event->time);
9429 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9432 Compose *compose = (Compose *)data;
9434 if (!event) return FALSE;
9436 switch (event->keyval) {
9437 case GDK_KEY_Delete:
9438 compose_attach_remove_selected(NULL, compose);
9444 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9446 toolbar_comp_set_sensitive(compose, allow);
9447 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9448 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9450 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9452 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9453 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9454 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9456 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9460 static void compose_send_cb(GtkAction *action, gpointer data)
9462 Compose *compose = (Compose *)data;
9464 if (prefs_common.work_offline &&
9465 !inc_offline_should_override(TRUE,
9466 _("Claws Mail needs network access in order "
9467 "to send this email.")))
9470 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9471 g_source_remove(compose->draft_timeout_tag);
9472 compose->draft_timeout_tag = -1;
9475 compose_send(compose);
9478 static void compose_send_later_cb(GtkAction *action, gpointer data)
9480 Compose *compose = (Compose *)data;
9484 compose_allow_user_actions(compose, FALSE);
9485 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9486 compose_allow_user_actions(compose, TRUE);
9490 compose_close(compose);
9491 } else if (val == -1) {
9492 alertpanel_error(_("Could not queue message."));
9493 } else if (val == -2) {
9494 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9495 } else if (val == -3) {
9496 if (privacy_peek_error())
9497 alertpanel_error(_("Could not queue message for sending:\n\n"
9498 "Signature failed: %s"), privacy_get_error());
9499 } else if (val == -4) {
9500 alertpanel_error(_("Could not queue message for sending:\n\n"
9501 "Charset conversion failed."));
9502 } else if (val == -5) {
9503 alertpanel_error(_("Could not queue message for sending:\n\n"
9504 "Couldn't get recipient encryption key."));
9505 } else if (val == -6) {
9508 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9511 #define DRAFTED_AT_EXIT "drafted_at_exit"
9512 static void compose_register_draft(MsgInfo *info)
9514 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9515 DRAFTED_AT_EXIT, NULL);
9516 FILE *fp = g_fopen(filepath, "ab");
9519 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9527 gboolean compose_draft (gpointer data, guint action)
9529 Compose *compose = (Compose *)data;
9534 MsgFlags flag = {0, 0};
9535 static gboolean lock = FALSE;
9536 MsgInfo *newmsginfo;
9538 gboolean target_locked = FALSE;
9539 gboolean err = FALSE;
9541 if (lock) return FALSE;
9543 if (compose->sending)
9546 draft = account_get_special_folder(compose->account, F_DRAFT);
9547 cm_return_val_if_fail(draft != NULL, FALSE);
9549 if (!g_mutex_trylock(compose->mutex)) {
9550 /* we don't want to lock the mutex once it's available,
9551 * because as the only other part of compose.c locking
9552 * it is compose_close - which means once unlocked,
9553 * the compose struct will be freed */
9554 debug_print("couldn't lock mutex, probably sending\n");
9560 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9561 G_DIR_SEPARATOR, compose);
9562 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9563 FILE_OP_ERROR(tmp, "fopen");
9567 /* chmod for security */
9568 if (change_file_mode_rw(fp, tmp) < 0) {
9569 FILE_OP_ERROR(tmp, "chmod");
9570 g_warning("can't change file mode\n");
9573 /* Save draft infos */
9574 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9575 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9577 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9578 gchar *savefolderid;
9580 savefolderid = compose_get_save_to(compose);
9581 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9582 g_free(savefolderid);
9584 if (compose->return_receipt) {
9585 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9587 if (compose->privacy_system) {
9588 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9589 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9590 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9593 /* Message-ID of message replying to */
9594 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9597 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9598 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9601 /* Message-ID of message forwarding to */
9602 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9605 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9606 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9610 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9611 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9613 sheaders = compose_get_manual_headers_info(compose);
9614 err |= (fprintf(fp, "X-Claws-Manual-Headers:%s\n", sheaders) < 0);
9617 /* end of headers */
9618 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9625 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9629 if (fclose(fp) == EOF) {
9633 if (compose->targetinfo) {
9634 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9635 flag.perm_flags = target_locked?MSG_LOCKED:0;
9637 flag.tmp_flags = MSG_DRAFT;
9639 folder_item_scan(draft);
9640 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9641 MsgInfo *tmpinfo = NULL;
9642 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9643 if (compose->msgid) {
9644 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9647 msgnum = tmpinfo->msgnum;
9648 procmsg_msginfo_free(tmpinfo);
9649 debug_print("got draft msgnum %d from scanning\n", msgnum);
9651 debug_print("didn't get draft msgnum after scanning\n");
9654 debug_print("got draft msgnum %d from adding\n", msgnum);
9660 if (action != COMPOSE_AUTO_SAVE) {
9661 if (action != COMPOSE_DRAFT_FOR_EXIT)
9662 alertpanel_error(_("Could not save draft."));
9665 gtkut_window_popup(compose->window);
9666 val = alertpanel_full(_("Could not save draft"),
9667 _("Could not save draft.\n"
9668 "Do you want to cancel exit or discard this email?"),
9669 _("_Cancel exit"), _("_Discard email"), NULL,
9670 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9671 if (val == G_ALERTALTERNATE) {
9673 g_mutex_unlock(compose->mutex); /* must be done before closing */
9674 compose_close(compose);
9678 g_mutex_unlock(compose->mutex); /* must be done before closing */
9687 if (compose->mode == COMPOSE_REEDIT) {
9688 compose_remove_reedit_target(compose, TRUE);
9691 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9694 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9696 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9698 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9699 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9700 procmsg_msginfo_set_flags(newmsginfo, 0,
9701 MSG_HAS_ATTACHMENT);
9703 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9704 compose_register_draft(newmsginfo);
9706 procmsg_msginfo_free(newmsginfo);
9709 folder_item_scan(draft);
9711 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9713 g_mutex_unlock(compose->mutex); /* must be done before closing */
9714 compose_close(compose);
9720 path = folder_item_fetch_msg(draft, msgnum);
9722 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9725 if (g_stat(path, &s) < 0) {
9726 FILE_OP_ERROR(path, "stat");
9732 procmsg_msginfo_free(compose->targetinfo);
9733 compose->targetinfo = procmsg_msginfo_new();
9734 compose->targetinfo->msgnum = msgnum;
9735 compose->targetinfo->size = (goffset)s.st_size;
9736 compose->targetinfo->mtime = s.st_mtime;
9737 compose->targetinfo->folder = draft;
9739 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9740 compose->mode = COMPOSE_REEDIT;
9742 if (action == COMPOSE_AUTO_SAVE) {
9743 compose->autosaved_draft = compose->targetinfo;
9745 compose->modified = FALSE;
9746 compose_set_title(compose);
9750 g_mutex_unlock(compose->mutex);
9754 void compose_clear_exit_drafts(void)
9756 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9757 DRAFTED_AT_EXIT, NULL);
9758 if (is_file_exist(filepath))
9759 claws_unlink(filepath);
9764 void compose_reopen_exit_drafts(void)
9766 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9767 DRAFTED_AT_EXIT, NULL);
9768 FILE *fp = g_fopen(filepath, "rb");
9772 while (fgets(buf, sizeof(buf), fp)) {
9773 gchar **parts = g_strsplit(buf, "\t", 2);
9774 const gchar *folder = parts[0];
9775 int msgnum = parts[1] ? atoi(parts[1]):-1;
9777 if (folder && *folder && msgnum > -1) {
9778 FolderItem *item = folder_find_item_from_identifier(folder);
9779 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9781 compose_reedit(info, FALSE);
9788 compose_clear_exit_drafts();
9791 static void compose_save_cb(GtkAction *action, gpointer data)
9793 Compose *compose = (Compose *)data;
9794 compose_draft(compose, COMPOSE_KEEP_EDITING);
9795 compose->rmode = COMPOSE_REEDIT;
9798 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9800 if (compose && file_list) {
9803 for ( tmp = file_list; tmp; tmp = tmp->next) {
9804 gchar *file = (gchar *) tmp->data;
9805 gchar *utf8_filename = conv_filename_to_utf8(file);
9806 compose_attach_append(compose, file, utf8_filename, NULL, NULL);
9807 compose_changed_cb(NULL, compose);
9812 g_free(utf8_filename);
9817 static void compose_attach_cb(GtkAction *action, gpointer data)
9819 Compose *compose = (Compose *)data;
9822 if (compose->redirect_filename != NULL)
9825 /* Set focus_window properly, in case we were called via popup menu,
9826 * which unsets it (via focus_out_event callback on compose window). */
9827 manage_window_focus_in(compose->window, NULL, NULL);
9829 file_list = filesel_select_multiple_files_open(_("Select file"));
9832 compose_attach_from_list(compose, file_list, TRUE);
9833 g_list_free(file_list);
9837 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9839 Compose *compose = (Compose *)data;
9841 gint files_inserted = 0;
9843 file_list = filesel_select_multiple_files_open(_("Select file"));
9848 for ( tmp = file_list; tmp; tmp = tmp->next) {
9849 gchar *file = (gchar *) tmp->data;
9850 gchar *filedup = g_strdup(file);
9851 gchar *shortfile = g_path_get_basename(filedup);
9852 ComposeInsertResult res;
9853 /* insert the file if the file is short or if the user confirmed that
9854 he/she wants to insert the large file */
9855 res = compose_insert_file(compose, file);
9856 if (res == COMPOSE_INSERT_READ_ERROR) {
9857 alertpanel_error(_("File '%s' could not be read."), shortfile);
9858 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9859 alertpanel_error(_("File '%s' contained invalid characters\n"
9860 "for the current encoding, insertion may be incorrect."),
9862 } else if (res == COMPOSE_INSERT_SUCCESS)
9869 g_list_free(file_list);
9873 if (files_inserted > 0 && compose->gtkaspell &&
9874 compose->gtkaspell->check_while_typing)
9875 gtkaspell_highlight_all(compose->gtkaspell);
9879 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9881 Compose *compose = (Compose *)data;
9883 compose_insert_sig(compose, FALSE);
9886 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9890 Compose *compose = (Compose *)data;
9892 gtkut_widget_get_uposition(widget, &x, &y);
9893 if (!compose->batch) {
9894 prefs_common.compose_x = x;
9895 prefs_common.compose_y = y;
9897 if (compose->sending || compose->updating)
9899 compose_close_cb(NULL, compose);
9903 void compose_close_toolbar(Compose *compose)
9905 compose_close_cb(NULL, compose);
9908 static void compose_close_cb(GtkAction *action, gpointer data)
9910 Compose *compose = (Compose *)data;
9914 if (compose->exteditor_tag != -1) {
9915 if (!compose_ext_editor_kill(compose))
9920 if (compose->modified) {
9921 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9922 if (!g_mutex_trylock(compose->mutex)) {
9923 /* we don't want to lock the mutex once it's available,
9924 * because as the only other part of compose.c locking
9925 * it is compose_close - which means once unlocked,
9926 * the compose struct will be freed */
9927 debug_print("couldn't lock mutex, probably sending\n");
9931 val = alertpanel(_("Discard message"),
9932 _("This message has been modified. Discard it?"),
9933 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9935 val = alertpanel(_("Save changes"),
9936 _("This message has been modified. Save the latest changes?"),
9937 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9939 g_mutex_unlock(compose->mutex);
9941 case G_ALERTDEFAULT:
9942 if (prefs_common.autosave && !reedit)
9943 compose_remove_draft(compose);
9945 case G_ALERTALTERNATE:
9946 compose_draft(data, COMPOSE_QUIT_EDITING);
9953 compose_close(compose);
9956 static void compose_print_cb(GtkAction *action, gpointer data)
9958 Compose *compose = (Compose *) data;
9960 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9961 if (compose->targetinfo)
9962 messageview_print(compose->targetinfo, FALSE, -1, -1, 0);
9965 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9967 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9968 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9969 Compose *compose = (Compose *) data;
9972 compose->out_encoding = (CharSet)value;
9975 static void compose_address_cb(GtkAction *action, gpointer data)
9977 Compose *compose = (Compose *)data;
9979 #ifndef USE_NEW_ADDRBOOK
9980 addressbook_open(compose);
9982 GError* error = NULL;
9983 addressbook_connect_signals(compose);
9984 addressbook_dbus_open(TRUE, &error);
9986 g_warning("%s", error->message);
9987 g_error_free(error);
9992 static void about_show_cb(GtkAction *action, gpointer data)
9997 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9999 Compose *compose = (Compose *)data;
10004 tmpl = g_object_get_data(G_OBJECT(widget), "template");
10005 cm_return_if_fail(tmpl != NULL);
10007 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
10009 val = alertpanel(_("Apply template"), msg,
10010 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
10013 if (val == G_ALERTDEFAULT)
10014 compose_template_apply(compose, tmpl, TRUE);
10015 else if (val == G_ALERTALTERNATE)
10016 compose_template_apply(compose, tmpl, FALSE);
10019 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
10021 Compose *compose = (Compose *)data;
10023 compose_exec_ext_editor(compose);
10026 static void compose_undo_cb(GtkAction *action, gpointer data)
10028 Compose *compose = (Compose *)data;
10029 gboolean prev_autowrap = compose->autowrap;
10031 compose->autowrap = FALSE;
10032 undo_undo(compose->undostruct);
10033 compose->autowrap = prev_autowrap;
10036 static void compose_redo_cb(GtkAction *action, gpointer data)
10038 Compose *compose = (Compose *)data;
10039 gboolean prev_autowrap = compose->autowrap;
10041 compose->autowrap = FALSE;
10042 undo_redo(compose->undostruct);
10043 compose->autowrap = prev_autowrap;
10046 static void entry_cut_clipboard(GtkWidget *entry)
10048 if (GTK_IS_EDITABLE(entry))
10049 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
10050 else if (GTK_IS_TEXT_VIEW(entry))
10051 gtk_text_buffer_cut_clipboard(
10052 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10053 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
10057 static void entry_copy_clipboard(GtkWidget *entry)
10059 if (GTK_IS_EDITABLE(entry))
10060 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
10061 else if (GTK_IS_TEXT_VIEW(entry))
10062 gtk_text_buffer_copy_clipboard(
10063 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10064 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
10067 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
10068 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
10070 if (GTK_IS_TEXT_VIEW(entry)) {
10071 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10072 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
10073 GtkTextIter start_iter, end_iter;
10075 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
10077 if (contents == NULL)
10080 /* we shouldn't delete the selection when middle-click-pasting, or we
10081 * can't mid-click-paste our own selection */
10082 if (clip != GDK_SELECTION_PRIMARY) {
10083 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
10084 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
10087 if (insert_place == NULL) {
10088 /* if insert_place isn't specified, insert at the cursor.
10089 * used for Ctrl-V pasting */
10090 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10091 start = gtk_text_iter_get_offset(&start_iter);
10092 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
10094 /* if insert_place is specified, paste here.
10095 * used for mid-click-pasting */
10096 start = gtk_text_iter_get_offset(insert_place);
10097 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
10098 if (prefs_common.primary_paste_unselects)
10099 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
10103 /* paste unwrapped: mark the paste so it's not wrapped later */
10104 end = start + strlen(contents);
10105 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
10106 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
10107 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
10108 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
10109 /* rewrap paragraph now (after a mid-click-paste) */
10110 mark_start = gtk_text_buffer_get_insert(buffer);
10111 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10112 gtk_text_iter_backward_char(&start_iter);
10113 compose_beautify_paragraph(compose, &start_iter, TRUE);
10115 } else if (GTK_IS_EDITABLE(entry))
10116 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
10118 compose->modified = TRUE;
10121 static void entry_allsel(GtkWidget *entry)
10123 if (GTK_IS_EDITABLE(entry))
10124 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
10125 else if (GTK_IS_TEXT_VIEW(entry)) {
10126 GtkTextIter startiter, enditer;
10127 GtkTextBuffer *textbuf;
10129 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10130 gtk_text_buffer_get_start_iter(textbuf, &startiter);
10131 gtk_text_buffer_get_end_iter(textbuf, &enditer);
10133 gtk_text_buffer_move_mark_by_name(textbuf,
10134 "selection_bound", &startiter);
10135 gtk_text_buffer_move_mark_by_name(textbuf,
10136 "insert", &enditer);
10140 static void compose_cut_cb(GtkAction *action, gpointer data)
10142 Compose *compose = (Compose *)data;
10143 if (compose->focused_editable
10144 #ifndef GENERIC_UMPC
10145 && gtk_widget_has_focus(compose->focused_editable)
10148 entry_cut_clipboard(compose->focused_editable);
10151 static void compose_copy_cb(GtkAction *action, gpointer data)
10153 Compose *compose = (Compose *)data;
10154 if (compose->focused_editable
10155 #ifndef GENERIC_UMPC
10156 && gtk_widget_has_focus(compose->focused_editable)
10159 entry_copy_clipboard(compose->focused_editable);
10162 static void compose_paste_cb(GtkAction *action, gpointer data)
10164 Compose *compose = (Compose *)data;
10165 gint prev_autowrap;
10166 GtkTextBuffer *buffer;
10168 if (compose->focused_editable &&
10169 #ifndef GENERIC_UMPC
10170 gtk_widget_has_focus(compose->focused_editable)
10173 entry_paste_clipboard(compose, compose->focused_editable,
10174 prefs_common.linewrap_pastes,
10175 GDK_SELECTION_CLIPBOARD, NULL);
10180 #ifndef GENERIC_UMPC
10181 gtk_widget_has_focus(compose->text) &&
10183 compose->gtkaspell &&
10184 compose->gtkaspell->check_while_typing)
10185 gtkaspell_highlight_all(compose->gtkaspell);
10189 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10191 Compose *compose = (Compose *)data;
10192 gint wrap_quote = prefs_common.linewrap_quote;
10193 if (compose->focused_editable
10194 #ifndef GENERIC_UMPC
10195 && gtk_widget_has_focus(compose->focused_editable)
10198 /* let text_insert() (called directly or at a later time
10199 * after the gtk_editable_paste_clipboard) know that
10200 * text is to be inserted as a quotation. implemented
10201 * by using a simple refcount... */
10202 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10203 G_OBJECT(compose->focused_editable),
10204 "paste_as_quotation"));
10205 g_object_set_data(G_OBJECT(compose->focused_editable),
10206 "paste_as_quotation",
10207 GINT_TO_POINTER(paste_as_quotation + 1));
10208 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10209 entry_paste_clipboard(compose, compose->focused_editable,
10210 prefs_common.linewrap_pastes,
10211 GDK_SELECTION_CLIPBOARD, NULL);
10212 prefs_common.linewrap_quote = wrap_quote;
10216 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10218 Compose *compose = (Compose *)data;
10219 gint prev_autowrap;
10220 GtkTextBuffer *buffer;
10222 if (compose->focused_editable
10223 #ifndef GENERIC_UMPC
10224 && gtk_widget_has_focus(compose->focused_editable)
10227 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10228 GDK_SELECTION_CLIPBOARD, NULL);
10233 #ifndef GENERIC_UMPC
10234 gtk_widget_has_focus(compose->text) &&
10236 compose->gtkaspell &&
10237 compose->gtkaspell->check_while_typing)
10238 gtkaspell_highlight_all(compose->gtkaspell);
10242 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10244 Compose *compose = (Compose *)data;
10245 gint prev_autowrap;
10246 GtkTextBuffer *buffer;
10248 if (compose->focused_editable
10249 #ifndef GENERIC_UMPC
10250 && gtk_widget_has_focus(compose->focused_editable)
10253 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10254 GDK_SELECTION_CLIPBOARD, NULL);
10259 #ifndef GENERIC_UMPC
10260 gtk_widget_has_focus(compose->text) &&
10262 compose->gtkaspell &&
10263 compose->gtkaspell->check_while_typing)
10264 gtkaspell_highlight_all(compose->gtkaspell);
10268 static void compose_allsel_cb(GtkAction *action, gpointer data)
10270 Compose *compose = (Compose *)data;
10271 if (compose->focused_editable
10272 #ifndef GENERIC_UMPC
10273 && gtk_widget_has_focus(compose->focused_editable)
10276 entry_allsel(compose->focused_editable);
10279 static void textview_move_beginning_of_line (GtkTextView *text)
10281 GtkTextBuffer *buffer;
10285 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10287 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10288 mark = gtk_text_buffer_get_insert(buffer);
10289 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10290 gtk_text_iter_set_line_offset(&ins, 0);
10291 gtk_text_buffer_place_cursor(buffer, &ins);
10294 static void textview_move_forward_character (GtkTextView *text)
10296 GtkTextBuffer *buffer;
10300 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10302 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10303 mark = gtk_text_buffer_get_insert(buffer);
10304 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10305 if (gtk_text_iter_forward_cursor_position(&ins))
10306 gtk_text_buffer_place_cursor(buffer, &ins);
10309 static void textview_move_backward_character (GtkTextView *text)
10311 GtkTextBuffer *buffer;
10315 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10317 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10318 mark = gtk_text_buffer_get_insert(buffer);
10319 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10320 if (gtk_text_iter_backward_cursor_position(&ins))
10321 gtk_text_buffer_place_cursor(buffer, &ins);
10324 static void textview_move_forward_word (GtkTextView *text)
10326 GtkTextBuffer *buffer;
10331 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10333 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10334 mark = gtk_text_buffer_get_insert(buffer);
10335 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10336 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10337 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10338 gtk_text_iter_backward_word_start(&ins);
10339 gtk_text_buffer_place_cursor(buffer, &ins);
10343 static void textview_move_backward_word (GtkTextView *text)
10345 GtkTextBuffer *buffer;
10349 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10351 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10352 mark = gtk_text_buffer_get_insert(buffer);
10353 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10354 if (gtk_text_iter_backward_word_starts(&ins, 1))
10355 gtk_text_buffer_place_cursor(buffer, &ins);
10358 static void textview_move_end_of_line (GtkTextView *text)
10360 GtkTextBuffer *buffer;
10364 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10366 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10367 mark = gtk_text_buffer_get_insert(buffer);
10368 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10369 if (gtk_text_iter_forward_to_line_end(&ins))
10370 gtk_text_buffer_place_cursor(buffer, &ins);
10373 static void textview_move_next_line (GtkTextView *text)
10375 GtkTextBuffer *buffer;
10380 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10382 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10383 mark = gtk_text_buffer_get_insert(buffer);
10384 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10385 offset = gtk_text_iter_get_line_offset(&ins);
10386 if (gtk_text_iter_forward_line(&ins)) {
10387 gtk_text_iter_set_line_offset(&ins, offset);
10388 gtk_text_buffer_place_cursor(buffer, &ins);
10392 static void textview_move_previous_line (GtkTextView *text)
10394 GtkTextBuffer *buffer;
10399 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10401 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10402 mark = gtk_text_buffer_get_insert(buffer);
10403 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10404 offset = gtk_text_iter_get_line_offset(&ins);
10405 if (gtk_text_iter_backward_line(&ins)) {
10406 gtk_text_iter_set_line_offset(&ins, offset);
10407 gtk_text_buffer_place_cursor(buffer, &ins);
10411 static void textview_delete_forward_character (GtkTextView *text)
10413 GtkTextBuffer *buffer;
10415 GtkTextIter ins, end_iter;
10417 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10419 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10420 mark = gtk_text_buffer_get_insert(buffer);
10421 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10423 if (gtk_text_iter_forward_char(&end_iter)) {
10424 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10428 static void textview_delete_backward_character (GtkTextView *text)
10430 GtkTextBuffer *buffer;
10432 GtkTextIter ins, end_iter;
10434 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10436 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10437 mark = gtk_text_buffer_get_insert(buffer);
10438 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10440 if (gtk_text_iter_backward_char(&end_iter)) {
10441 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10445 static void textview_delete_forward_word (GtkTextView *text)
10447 GtkTextBuffer *buffer;
10449 GtkTextIter ins, end_iter;
10451 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10453 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10454 mark = gtk_text_buffer_get_insert(buffer);
10455 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10457 if (gtk_text_iter_forward_word_end(&end_iter)) {
10458 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10462 static void textview_delete_backward_word (GtkTextView *text)
10464 GtkTextBuffer *buffer;
10466 GtkTextIter ins, end_iter;
10468 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10470 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10471 mark = gtk_text_buffer_get_insert(buffer);
10472 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10474 if (gtk_text_iter_backward_word_start(&end_iter)) {
10475 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10479 static void textview_delete_line (GtkTextView *text)
10481 GtkTextBuffer *buffer;
10483 GtkTextIter ins, start_iter, end_iter;
10485 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10487 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10488 mark = gtk_text_buffer_get_insert(buffer);
10489 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10492 gtk_text_iter_set_line_offset(&start_iter, 0);
10495 if (gtk_text_iter_ends_line(&end_iter)){
10496 if (!gtk_text_iter_forward_char(&end_iter))
10497 gtk_text_iter_backward_char(&start_iter);
10500 gtk_text_iter_forward_to_line_end(&end_iter);
10501 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10504 static void textview_delete_to_line_end (GtkTextView *text)
10506 GtkTextBuffer *buffer;
10508 GtkTextIter ins, end_iter;
10510 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10512 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10513 mark = gtk_text_buffer_get_insert(buffer);
10514 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10516 if (gtk_text_iter_ends_line(&end_iter))
10517 gtk_text_iter_forward_char(&end_iter);
10519 gtk_text_iter_forward_to_line_end(&end_iter);
10520 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10523 #define DO_ACTION(name, act) { \
10524 if(!strcmp(name, a_name)) { \
10528 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10530 const gchar *a_name = gtk_action_get_name(action);
10531 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10532 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10533 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10534 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10535 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10536 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10537 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10538 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10539 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10540 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10541 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10542 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10543 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10544 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10548 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10550 Compose *compose = (Compose *)data;
10551 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10552 ComposeCallAdvancedAction action = -1;
10554 action = compose_call_advanced_action_from_path(gaction);
10557 void (*do_action) (GtkTextView *text);
10558 } action_table[] = {
10559 {textview_move_beginning_of_line},
10560 {textview_move_forward_character},
10561 {textview_move_backward_character},
10562 {textview_move_forward_word},
10563 {textview_move_backward_word},
10564 {textview_move_end_of_line},
10565 {textview_move_next_line},
10566 {textview_move_previous_line},
10567 {textview_delete_forward_character},
10568 {textview_delete_backward_character},
10569 {textview_delete_forward_word},
10570 {textview_delete_backward_word},
10571 {textview_delete_line},
10572 {textview_delete_to_line_end}
10575 if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
10577 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10578 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10579 if (action_table[action].do_action)
10580 action_table[action].do_action(text);
10582 g_warning("Not implemented yet.");
10586 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10588 GtkAllocation allocation;
10592 if (GTK_IS_EDITABLE(widget)) {
10593 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10594 gtk_editable_set_position(GTK_EDITABLE(widget),
10597 if ((parent = gtk_widget_get_parent(widget))
10598 && (parent = gtk_widget_get_parent(parent))
10599 && (parent = gtk_widget_get_parent(parent))) {
10600 if (GTK_IS_SCROLLED_WINDOW(parent)) {
10601 gtk_widget_get_allocation(widget, &allocation);
10602 gint y = allocation.y;
10603 gint height = allocation.height;
10604 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10605 (GTK_SCROLLED_WINDOW(parent));
10607 gfloat value = gtk_adjustment_get_value(shown);
10608 gfloat upper = gtk_adjustment_get_upper(shown);
10609 gfloat page_size = gtk_adjustment_get_page_size(shown);
10610 if (y < (int)value) {
10611 gtk_adjustment_set_value(shown, y - 1);
10613 if ((y + height) > ((int)value + (int)page_size)) {
10614 if ((y - height - 1) < ((int)upper - (int)page_size)) {
10615 gtk_adjustment_set_value(shown,
10616 y + height - (int)page_size - 1);
10618 gtk_adjustment_set_value(shown,
10619 (int)upper - (int)page_size - 1);
10626 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10627 compose->focused_editable = widget;
10629 #ifdef GENERIC_UMPC
10630 if (GTK_IS_TEXT_VIEW(widget)
10631 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10632 g_object_ref(compose->notebook);
10633 g_object_ref(compose->edit_vbox);
10634 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10635 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10636 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10637 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10638 g_object_unref(compose->notebook);
10639 g_object_unref(compose->edit_vbox);
10640 g_signal_handlers_block_by_func(G_OBJECT(widget),
10641 G_CALLBACK(compose_grab_focus_cb),
10643 gtk_widget_grab_focus(widget);
10644 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10645 G_CALLBACK(compose_grab_focus_cb),
10647 } else if (!GTK_IS_TEXT_VIEW(widget)
10648 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10649 g_object_ref(compose->notebook);
10650 g_object_ref(compose->edit_vbox);
10651 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10652 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10653 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10654 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10655 g_object_unref(compose->notebook);
10656 g_object_unref(compose->edit_vbox);
10657 g_signal_handlers_block_by_func(G_OBJECT(widget),
10658 G_CALLBACK(compose_grab_focus_cb),
10660 gtk_widget_grab_focus(widget);
10661 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10662 G_CALLBACK(compose_grab_focus_cb),
10668 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10670 compose->modified = TRUE;
10671 // compose_beautify_paragraph(compose, NULL, TRUE);
10672 #ifndef GENERIC_UMPC
10673 compose_set_title(compose);
10677 static void compose_wrap_cb(GtkAction *action, gpointer data)
10679 Compose *compose = (Compose *)data;
10680 compose_beautify_paragraph(compose, NULL, TRUE);
10683 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10685 Compose *compose = (Compose *)data;
10686 compose_wrap_all_full(compose, TRUE);
10689 static void compose_find_cb(GtkAction *action, gpointer data)
10691 Compose *compose = (Compose *)data;
10693 message_search_compose(compose);
10696 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10699 Compose *compose = (Compose *)data;
10700 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10701 if (compose->autowrap)
10702 compose_wrap_all_full(compose, TRUE);
10703 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10706 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10709 Compose *compose = (Compose *)data;
10710 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10713 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10715 Compose *compose = (Compose *)data;
10717 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10720 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10722 Compose *compose = (Compose *)data;
10724 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10727 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10729 g_free(compose->privacy_system);
10731 compose->privacy_system = g_strdup(account->default_privacy_system);
10732 compose_update_privacy_system_menu_item(compose, warn);
10735 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10737 Compose *compose = (Compose *)data;
10739 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10740 gtk_widget_show(compose->ruler_hbox);
10741 prefs_common.show_ruler = TRUE;
10743 gtk_widget_hide(compose->ruler_hbox);
10744 gtk_widget_queue_resize(compose->edit_vbox);
10745 prefs_common.show_ruler = FALSE;
10749 static void compose_attach_drag_received_cb (GtkWidget *widget,
10750 GdkDragContext *context,
10753 GtkSelectionData *data,
10756 gpointer user_data)
10758 Compose *compose = (Compose *)user_data;
10762 type = gtk_selection_data_get_data_type(data);
10763 if (((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
10765 || (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND"))
10767 ) && gtk_drag_get_source_widget(context) !=
10768 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10769 list = uri_list_extract_filenames(
10770 (const gchar *)gtk_selection_data_get_data(data));
10771 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10772 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10773 compose_attach_append
10774 (compose, (const gchar *)tmp->data,
10775 utf8_filename, NULL, NULL);
10776 g_free(utf8_filename);
10778 if (list) compose_changed_cb(NULL, compose);
10779 list_free_strings(list);
10781 } else if (gtk_drag_get_source_widget(context)
10782 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10783 /* comes from our summaryview */
10784 SummaryView * summaryview = NULL;
10785 GSList * list = NULL, *cur = NULL;
10787 if (mainwindow_get_mainwindow())
10788 summaryview = mainwindow_get_mainwindow()->summaryview;
10791 list = summary_get_selected_msg_list(summaryview);
10793 for (cur = list; cur; cur = cur->next) {
10794 MsgInfo *msginfo = (MsgInfo *)cur->data;
10795 gchar *file = NULL;
10797 file = procmsg_get_message_file_full(msginfo,
10800 compose_attach_append(compose, (const gchar *)file,
10801 (const gchar *)file, "message/rfc822", NULL);
10805 g_slist_free(list);
10809 static gboolean compose_drag_drop(GtkWidget *widget,
10810 GdkDragContext *drag_context,
10812 guint time, gpointer user_data)
10814 /* not handling this signal makes compose_insert_drag_received_cb
10819 static gboolean completion_set_focus_to_subject
10820 (GtkWidget *widget,
10821 GdkEventKey *event,
10824 cm_return_val_if_fail(compose != NULL, FALSE);
10826 /* make backtab move to subject field */
10827 if(event->keyval == GDK_KEY_ISO_Left_Tab) {
10828 gtk_widget_grab_focus(compose->subject_entry);
10834 static void compose_insert_drag_received_cb (GtkWidget *widget,
10835 GdkDragContext *drag_context,
10838 GtkSelectionData *data,
10841 gpointer user_data)
10843 Compose *compose = (Compose *)user_data;
10847 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10849 type = gtk_selection_data_get_data_type(data);
10851 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
10853 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND")) {
10855 AlertValue val = G_ALERTDEFAULT;
10856 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
10858 list = uri_list_extract_filenames(ddata);
10859 if (list == NULL && strstr(ddata, "://")) {
10860 /* Assume a list of no files, and data has ://, is a remote link */
10861 gchar *tmpdata = g_strstrip(g_strdup(ddata));
10862 gchar *tmpfile = get_tmp_file();
10863 str_write_to_file(tmpdata, tmpfile);
10865 compose_insert_file(compose, tmpfile);
10866 claws_unlink(tmpfile);
10868 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10869 compose_beautify_paragraph(compose, NULL, TRUE);
10872 switch (prefs_common.compose_dnd_mode) {
10873 case COMPOSE_DND_ASK:
10874 val = alertpanel_full(_("Insert or attach?"),
10875 _("Do you want to insert the contents of the file(s) "
10876 "into the message body, or attach it to the email?"),
10877 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10878 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10880 case COMPOSE_DND_INSERT:
10881 val = G_ALERTALTERNATE;
10883 case COMPOSE_DND_ATTACH:
10884 val = G_ALERTOTHER;
10887 /* unexpected case */
10888 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10891 if (val & G_ALERTDISABLE) {
10892 val &= ~G_ALERTDISABLE;
10893 /* remember what action to perform by default, only if we don't click Cancel */
10894 if (val == G_ALERTALTERNATE)
10895 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10896 else if (val == G_ALERTOTHER)
10897 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10900 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10901 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10902 list_free_strings(list);
10905 } else if (val == G_ALERTOTHER) {
10906 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10907 list_free_strings(list);
10912 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10913 compose_insert_file(compose, (const gchar *)tmp->data);
10915 list_free_strings(list);
10917 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10922 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10925 static void compose_header_drag_received_cb (GtkWidget *widget,
10926 GdkDragContext *drag_context,
10929 GtkSelectionData *data,
10932 gpointer user_data)
10934 GtkEditable *entry = (GtkEditable *)user_data;
10935 const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
10937 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10940 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10941 gchar *decoded=g_new(gchar, strlen(email));
10944 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
10945 gtk_editable_delete_text(entry, 0, -1);
10946 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10947 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10951 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10954 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10956 Compose *compose = (Compose *)data;
10958 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10959 compose->return_receipt = TRUE;
10961 compose->return_receipt = FALSE;
10964 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10966 Compose *compose = (Compose *)data;
10968 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10969 compose->remove_references = TRUE;
10971 compose->remove_references = FALSE;
10974 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10975 ComposeHeaderEntry *headerentry)
10977 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10981 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10982 GdkEventKey *event,
10983 ComposeHeaderEntry *headerentry)
10985 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10986 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10987 !(event->state & GDK_MODIFIER_MASK) &&
10988 (event->keyval == GDK_KEY_BackSpace) &&
10989 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10990 gtk_container_remove
10991 (GTK_CONTAINER(headerentry->compose->header_table),
10992 headerentry->combo);
10993 gtk_container_remove
10994 (GTK_CONTAINER(headerentry->compose->header_table),
10995 headerentry->entry);
10996 headerentry->compose->header_list =
10997 g_slist_remove(headerentry->compose->header_list,
10999 g_free(headerentry);
11000 } else if (event->keyval == GDK_KEY_Tab) {
11001 if (headerentry->compose->header_last == headerentry) {
11002 /* Override default next focus, and give it to subject_entry
11003 * instead of notebook tabs
11005 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
11006 gtk_widget_grab_focus(headerentry->compose->subject_entry);
11013 static gboolean scroll_postpone(gpointer data)
11015 Compose *compose = (Compose *)data;
11017 cm_return_val_if_fail(!compose->batch, FALSE);
11019 GTK_EVENTS_FLUSH();
11020 compose_show_first_last_header(compose, FALSE);
11024 static void compose_headerentry_changed_cb(GtkWidget *entry,
11025 ComposeHeaderEntry *headerentry)
11027 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
11028 compose_create_header_entry(headerentry->compose);
11029 g_signal_handlers_disconnect_matched
11030 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
11031 0, 0, NULL, NULL, headerentry);
11033 if (!headerentry->compose->batch)
11034 g_timeout_add(0, scroll_postpone, headerentry->compose);
11038 static gboolean compose_defer_auto_save_draft(Compose *compose)
11040 compose->draft_timeout_tag = -1;
11041 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
11045 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
11047 GtkAdjustment *vadj;
11049 cm_return_if_fail(compose);
11050 cm_return_if_fail(!compose->batch);
11051 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
11052 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
11053 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11054 gtk_widget_get_parent(compose->header_table)));
11055 gtk_adjustment_set_value(vadj, (show_first ?
11056 gtk_adjustment_get_lower(vadj) :
11057 (gtk_adjustment_get_upper(vadj) -
11058 gtk_adjustment_get_page_size(vadj))));
11059 gtk_adjustment_changed(vadj);
11062 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
11063 const gchar *text, gint len, Compose *compose)
11065 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
11066 (G_OBJECT(compose->text), "paste_as_quotation"));
11069 cm_return_if_fail(text != NULL);
11071 g_signal_handlers_block_by_func(G_OBJECT(buffer),
11072 G_CALLBACK(text_inserted),
11074 if (paste_as_quotation) {
11076 const gchar *qmark;
11078 GtkTextIter start_iter;
11081 len = strlen(text);
11083 new_text = g_strndup(text, len);
11085 qmark = compose_quote_char_from_context(compose);
11087 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11088 gtk_text_buffer_place_cursor(buffer, iter);
11090 pos = gtk_text_iter_get_offset(iter);
11092 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
11093 _("Quote format error at line %d."));
11094 quote_fmt_reset_vartable();
11096 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
11097 GINT_TO_POINTER(paste_as_quotation - 1));
11099 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11100 gtk_text_buffer_place_cursor(buffer, iter);
11101 gtk_text_buffer_delete_mark(buffer, mark);
11103 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
11104 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
11105 compose_beautify_paragraph(compose, &start_iter, FALSE);
11106 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
11107 gtk_text_buffer_delete_mark(buffer, mark);
11109 if (strcmp(text, "\n") || compose->automatic_break
11110 || gtk_text_iter_starts_line(iter)) {
11111 GtkTextIter before_ins;
11112 gtk_text_buffer_insert(buffer, iter, text, len);
11113 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
11114 before_ins = *iter;
11115 gtk_text_iter_backward_chars(&before_ins, len);
11116 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
11119 /* check if the preceding is just whitespace or quote */
11120 GtkTextIter start_line;
11121 gchar *tmp = NULL, *quote = NULL;
11122 gint quote_len = 0, is_normal = 0;
11123 start_line = *iter;
11124 gtk_text_iter_set_line_offset(&start_line, 0);
11125 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
11128 if (*tmp == '\0') {
11131 quote = compose_get_quote_str(buffer, &start_line, "e_len);
11139 gtk_text_buffer_insert(buffer, iter, text, len);
11141 gtk_text_buffer_insert_with_tags_by_name(buffer,
11142 iter, text, len, "no_join", NULL);
11147 if (!paste_as_quotation) {
11148 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11149 compose_beautify_paragraph(compose, iter, FALSE);
11150 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11151 gtk_text_buffer_delete_mark(buffer, mark);
11154 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
11155 G_CALLBACK(text_inserted),
11157 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
11159 if (prefs_common.autosave &&
11160 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
11161 compose->draft_timeout_tag != -2 /* disabled while loading */)
11162 compose->draft_timeout_tag = g_timeout_add
11163 (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
11167 static void compose_check_all(GtkAction *action, gpointer data)
11169 Compose *compose = (Compose *)data;
11170 if (!compose->gtkaspell)
11173 if (gtk_widget_has_focus(compose->subject_entry))
11174 claws_spell_entry_check_all(
11175 CLAWS_SPELL_ENTRY(compose->subject_entry));
11177 gtkaspell_check_all(compose->gtkaspell);
11180 static void compose_highlight_all(GtkAction *action, gpointer data)
11182 Compose *compose = (Compose *)data;
11183 if (compose->gtkaspell) {
11184 claws_spell_entry_recheck_all(
11185 CLAWS_SPELL_ENTRY(compose->subject_entry));
11186 gtkaspell_highlight_all(compose->gtkaspell);
11190 static void compose_check_backwards(GtkAction *action, gpointer data)
11192 Compose *compose = (Compose *)data;
11193 if (!compose->gtkaspell) {
11194 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11198 if (gtk_widget_has_focus(compose->subject_entry))
11199 claws_spell_entry_check_backwards(
11200 CLAWS_SPELL_ENTRY(compose->subject_entry));
11202 gtkaspell_check_backwards(compose->gtkaspell);
11205 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11207 Compose *compose = (Compose *)data;
11208 if (!compose->gtkaspell) {
11209 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11213 if (gtk_widget_has_focus(compose->subject_entry))
11214 claws_spell_entry_check_forwards_go(
11215 CLAWS_SPELL_ENTRY(compose->subject_entry));
11217 gtkaspell_check_forwards_go(compose->gtkaspell);
11222 *\brief Guess originating forward account from MsgInfo and several
11223 * "common preference" settings. Return NULL if no guess.
11225 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11227 PrefsAccount *account = NULL;
11229 cm_return_val_if_fail(msginfo, NULL);
11230 cm_return_val_if_fail(msginfo->folder, NULL);
11231 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11233 if (msginfo->folder->prefs->enable_default_account)
11234 account = account_find_from_id(msginfo->folder->prefs->default_account);
11237 account = msginfo->folder->folder->account;
11239 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11241 Xstrdup_a(to, msginfo->to, return NULL);
11242 extract_address(to);
11243 account = account_find_from_address(to, FALSE);
11246 if (!account && prefs_common.forward_account_autosel) {
11247 gchar cc[BUFFSIZE];
11248 if (!procheader_get_header_from_msginfo
11249 (msginfo, cc,sizeof cc , "Cc:")) {
11250 gchar *buf = cc + strlen("Cc:");
11251 extract_address(buf);
11252 account = account_find_from_address(buf, FALSE);
11256 if (!account && prefs_common.forward_account_autosel) {
11257 gchar deliveredto[BUFFSIZE];
11258 if (!procheader_get_header_from_msginfo
11259 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
11260 gchar *buf = deliveredto + strlen("Delivered-To:");
11261 extract_address(buf);
11262 account = account_find_from_address(buf, FALSE);
11269 gboolean compose_close(Compose *compose)
11273 if (!g_mutex_trylock(compose->mutex)) {
11274 /* we have to wait for the (possibly deferred by auto-save)
11275 * drafting to be done, before destroying the compose under
11277 debug_print("waiting for drafting to finish...\n");
11278 compose_allow_user_actions(compose, FALSE);
11279 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11282 cm_return_val_if_fail(compose, FALSE);
11283 gtkut_widget_get_uposition(compose->window, &x, &y);
11284 if (!compose->batch) {
11285 prefs_common.compose_x = x;
11286 prefs_common.compose_y = y;
11288 g_mutex_unlock(compose->mutex);
11289 compose_destroy(compose);
11294 * Add entry field for each address in list.
11295 * \param compose E-Mail composition object.
11296 * \param listAddress List of (formatted) E-Mail addresses.
11298 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11301 node = listAddress;
11303 addr = ( gchar * ) node->data;
11304 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11305 node = g_list_next( node );
11309 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11310 guint action, gboolean opening_multiple)
11312 gchar *body = NULL;
11313 GSList *new_msglist = NULL;
11314 MsgInfo *tmp_msginfo = NULL;
11315 gboolean originally_enc = FALSE;
11316 gboolean originally_sig = FALSE;
11317 Compose *compose = NULL;
11318 gchar *s_system = NULL;
11320 cm_return_if_fail(msgview != NULL);
11322 cm_return_if_fail(msginfo_list != NULL);
11324 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11325 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11326 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11328 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11329 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11330 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11331 orig_msginfo, mimeinfo);
11332 if (tmp_msginfo != NULL) {
11333 new_msglist = g_slist_append(NULL, tmp_msginfo);
11335 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11336 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11337 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11339 tmp_msginfo->folder = orig_msginfo->folder;
11340 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11341 if (orig_msginfo->tags) {
11342 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11343 tmp_msginfo->folder->tags_dirty = TRUE;
11349 if (!opening_multiple)
11350 body = messageview_get_selection(msgview);
11353 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11354 procmsg_msginfo_free(tmp_msginfo);
11355 g_slist_free(new_msglist);
11357 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11359 if (compose && originally_enc) {
11360 compose_force_encryption(compose, compose->account, FALSE, s_system);
11363 if (compose && originally_sig && compose->account->default_sign_reply) {
11364 compose_force_signing(compose, compose->account, s_system);
11368 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11371 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11374 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11375 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11376 GSList *cur = msginfo_list;
11377 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11378 "messages. Opening the windows "
11379 "could take some time. Do you "
11380 "want to continue?"),
11381 g_slist_length(msginfo_list));
11382 if (g_slist_length(msginfo_list) > 9
11383 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11384 != G_ALERTALTERNATE) {
11389 /* We'll open multiple compose windows */
11390 /* let the WM place the next windows */
11391 compose_force_window_origin = FALSE;
11392 for (; cur; cur = cur->next) {
11394 tmplist.data = cur->data;
11395 tmplist.next = NULL;
11396 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11398 compose_force_window_origin = TRUE;
11400 /* forwarding multiple mails as attachments is done via a
11401 * single compose window */
11402 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11406 void compose_check_for_email_account(Compose *compose)
11408 PrefsAccount *ac = NULL, *curr = NULL;
11414 if (compose->account && compose->account->protocol == A_NNTP) {
11415 ac = account_get_cur_account();
11416 if (ac->protocol == A_NNTP) {
11417 list = account_get_list();
11419 for( ; list != NULL ; list = g_list_next(list)) {
11420 curr = (PrefsAccount *) list->data;
11421 if (curr->protocol != A_NNTP) {
11427 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11432 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo,
11433 const gchar *address)
11435 GSList *msginfo_list = NULL;
11436 gchar *body = messageview_get_selection(msgview);
11439 msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11441 compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11442 compose_check_for_email_account(compose);
11443 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11444 compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11445 compose_reply_set_subject(compose, msginfo);
11448 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11451 void compose_set_position(Compose *compose, gint pos)
11453 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11455 gtkut_text_view_set_position(text, pos);
11458 gboolean compose_search_string(Compose *compose,
11459 const gchar *str, gboolean case_sens)
11461 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11463 return gtkut_text_view_search_string(text, str, case_sens);
11466 gboolean compose_search_string_backward(Compose *compose,
11467 const gchar *str, gboolean case_sens)
11469 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11471 return gtkut_text_view_search_string_backward(text, str, case_sens);
11474 /* allocate a msginfo structure and populate its data from a compose data structure */
11475 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11477 MsgInfo *newmsginfo;
11479 gchar buf[BUFFSIZE];
11481 cm_return_val_if_fail( compose != NULL, NULL );
11483 newmsginfo = procmsg_msginfo_new();
11486 get_rfc822_date(buf, sizeof(buf));
11487 newmsginfo->date = g_strdup(buf);
11490 if (compose->from_name) {
11491 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11492 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11496 if (compose->subject_entry)
11497 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11499 /* to, cc, reply-to, newsgroups */
11500 for (list = compose->header_list; list; list = list->next) {
11501 gchar *header = gtk_editable_get_chars(
11503 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11504 gchar *entry = gtk_editable_get_chars(
11505 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11507 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11508 if ( newmsginfo->to == NULL ) {
11509 newmsginfo->to = g_strdup(entry);
11510 } else if (entry && *entry) {
11511 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11512 g_free(newmsginfo->to);
11513 newmsginfo->to = tmp;
11516 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11517 if ( newmsginfo->cc == NULL ) {
11518 newmsginfo->cc = g_strdup(entry);
11519 } else if (entry && *entry) {
11520 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11521 g_free(newmsginfo->cc);
11522 newmsginfo->cc = tmp;
11525 if ( strcasecmp(header,
11526 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11527 if ( newmsginfo->newsgroups == NULL ) {
11528 newmsginfo->newsgroups = g_strdup(entry);
11529 } else if (entry && *entry) {
11530 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11531 g_free(newmsginfo->newsgroups);
11532 newmsginfo->newsgroups = tmp;
11540 /* other data is unset */
11546 /* update compose's dictionaries from folder dict settings */
11547 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11548 FolderItem *folder_item)
11550 cm_return_if_fail(compose != NULL);
11552 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11553 FolderItemPrefs *prefs = folder_item->prefs;
11555 if (prefs->enable_default_dictionary)
11556 gtkaspell_change_dict(compose->gtkaspell,
11557 prefs->default_dictionary, FALSE);
11558 if (folder_item->prefs->enable_default_alt_dictionary)
11559 gtkaspell_change_alt_dict(compose->gtkaspell,
11560 prefs->default_alt_dictionary);
11561 if (prefs->enable_default_dictionary
11562 || prefs->enable_default_alt_dictionary)
11563 compose_spell_menu_changed(compose);