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/>.
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #ifndef USE_NEW_ADDRBOOK
64 #include "addressbook.h"
66 #include "addressbook-dbus.h"
67 #include "addressadd.h"
69 #include "folderview.h"
72 #include "stock_pixmap.h"
73 #include "send_message.h"
76 #include "customheader.h"
77 #include "prefs_common.h"
78 #include "prefs_account.h"
82 #include "procheader.h"
84 #include "statusbar.h"
87 #include "quoted-printable.h"
91 #include "gtkshruler.h"
93 #include "alertpanel.h"
94 #include "manage_window.h"
96 #include "addr_compl.h"
97 #include "quote_fmt.h"
99 #include "foldersel.h"
102 #include "message_search.h"
103 #include "combobox.h"
107 #include "autofaces.h"
108 #include "spell_entry.h"
121 #define N_ATTACH_COLS (N_COL_COLUMNS)
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
129 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
131 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
132 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
134 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
135 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
136 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
137 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
138 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
139 } ComposeCallAdvancedAction;
143 PRIORITY_HIGHEST = 1,
152 COMPOSE_INSERT_SUCCESS,
153 COMPOSE_INSERT_READ_ERROR,
154 COMPOSE_INSERT_INVALID_CHARACTER,
155 COMPOSE_INSERT_NO_FILE
156 } ComposeInsertResult;
160 COMPOSE_WRITE_FOR_SEND,
161 COMPOSE_WRITE_FOR_STORE
166 COMPOSE_QUOTE_FORCED,
173 SUBJECT_FIELD_PRESENT,
178 #define B64_LINE_SIZE 57
179 #define B64_BUFFSIZE 77
181 #define MAX_REFERENCES_LEN 999
183 static GList *compose_list = NULL;
185 static Compose *compose_generic_new (PrefsAccount *account,
189 GList *listAddress );
191 static Compose *compose_create (PrefsAccount *account,
196 static void compose_entry_mark_default_to (Compose *compose,
197 const gchar *address);
198 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
199 ComposeQuoteMode quote_mode,
203 static Compose *compose_forward_multiple (PrefsAccount *account,
204 GSList *msginfo_list);
205 static Compose *compose_reply (MsgInfo *msginfo,
206 ComposeQuoteMode quote_mode,
211 static Compose *compose_reply_mode (ComposeMode mode,
212 GSList *msginfo_list,
214 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
215 static void compose_update_privacy_systems_menu(Compose *compose);
217 static GtkWidget *compose_account_option_menu_create
219 static void compose_set_out_encoding (Compose *compose);
220 static void compose_set_template_menu (Compose *compose);
221 static void compose_destroy (Compose *compose);
223 static MailField compose_entries_set (Compose *compose,
225 ComposeEntryType to_type);
226 static gint compose_parse_header (Compose *compose,
228 static gint compose_parse_manual_headers (Compose *compose,
230 HeaderEntry *entries);
231 static gchar *compose_parse_references (const gchar *ref,
234 static gchar *compose_quote_fmt (Compose *compose,
240 gboolean need_unescape,
241 const gchar *err_msg);
243 static void compose_reply_set_entry (Compose *compose,
249 followup_and_reply_to);
250 static void compose_reedit_set_entry (Compose *compose,
253 static void compose_insert_sig (Compose *compose,
255 static ComposeInsertResult compose_insert_file (Compose *compose,
258 static gboolean compose_attach_append (Compose *compose,
261 const gchar *content_type,
262 const gchar *charset);
263 static void compose_attach_parts (Compose *compose,
266 static gboolean compose_beautify_paragraph (Compose *compose,
267 GtkTextIter *par_iter,
269 static void compose_wrap_all (Compose *compose);
270 static void compose_wrap_all_full (Compose *compose,
273 static void compose_set_title (Compose *compose);
274 static void compose_select_account (Compose *compose,
275 PrefsAccount *account,
278 static PrefsAccount *compose_current_mail_account(void);
279 /* static gint compose_send (Compose *compose); */
280 static gboolean compose_check_for_valid_recipient
282 static gboolean compose_check_entries (Compose *compose,
283 gboolean check_everything);
284 static gint compose_write_to_file (Compose *compose,
287 gboolean attach_parts);
288 static gint compose_write_body_to_file (Compose *compose,
290 static gint compose_remove_reedit_target (Compose *compose,
292 static void compose_remove_draft (Compose *compose);
293 static gint compose_queue_sub (Compose *compose,
297 gboolean check_subject,
298 gboolean remove_reedit_target);
299 static int compose_add_attachments (Compose *compose,
301 static gchar *compose_get_header (Compose *compose);
302 static gchar *compose_get_manual_headers_info (Compose *compose);
304 static void compose_convert_header (Compose *compose,
309 gboolean addr_field);
311 static void compose_attach_info_free (AttachInfo *ainfo);
312 static void compose_attach_remove_selected (GtkAction *action,
315 static void compose_template_apply (Compose *compose,
318 static void compose_attach_property (GtkAction *action,
320 static void compose_attach_property_create (gboolean *cancelled);
321 static void attach_property_ok (GtkWidget *widget,
322 gboolean *cancelled);
323 static void attach_property_cancel (GtkWidget *widget,
324 gboolean *cancelled);
325 static gint attach_property_delete_event (GtkWidget *widget,
327 gboolean *cancelled);
328 static gboolean attach_property_key_pressed (GtkWidget *widget,
330 gboolean *cancelled);
332 static void compose_exec_ext_editor (Compose *compose);
334 static gint compose_exec_ext_editor_real (const gchar *file);
335 static gboolean compose_ext_editor_kill (Compose *compose);
336 static gboolean compose_input_cb (GIOChannel *source,
337 GIOCondition condition,
339 static void compose_set_ext_editor_sensitive (Compose *compose,
341 #endif /* G_OS_UNIX */
343 static void compose_undo_state_changed (UndoMain *undostruct,
348 static void compose_create_header_entry (Compose *compose);
349 static void compose_add_header_entry (Compose *compose, const gchar *header,
350 gchar *text, ComposePrefType pref_type);
351 static void compose_remove_header_entries(Compose *compose);
353 static void compose_update_priority_menu_item(Compose * compose);
355 static void compose_spell_menu_changed (void *data);
356 static void compose_dict_changed (void *data);
358 static void compose_add_field_list ( Compose *compose,
359 GList *listAddress );
361 /* callback functions */
363 static void compose_notebook_size_alloc (GtkNotebook *notebook,
364 GtkAllocation *allocation,
366 static gboolean compose_edit_size_alloc (GtkEditable *widget,
367 GtkAllocation *allocation,
368 GtkSHRuler *shruler);
369 static void account_activated (GtkComboBox *optmenu,
371 static void attach_selected (GtkTreeView *tree_view,
372 GtkTreePath *tree_path,
373 GtkTreeViewColumn *column,
375 static gboolean attach_button_pressed (GtkWidget *widget,
376 GdkEventButton *event,
378 static gboolean attach_key_pressed (GtkWidget *widget,
381 static void compose_send_cb (GtkAction *action, gpointer data);
382 static void compose_send_later_cb (GtkAction *action, gpointer data);
384 static void compose_save_cb (GtkAction *action,
387 static void compose_attach_cb (GtkAction *action,
389 static void compose_insert_file_cb (GtkAction *action,
391 static void compose_insert_sig_cb (GtkAction *action,
394 static void compose_close_cb (GtkAction *action,
396 static void compose_print_cb (GtkAction *action,
399 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
401 static void compose_address_cb (GtkAction *action,
403 static void about_show_cb (GtkAction *action,
405 static void compose_template_activate_cb(GtkWidget *widget,
408 static void compose_ext_editor_cb (GtkAction *action,
411 static gint compose_delete_cb (GtkWidget *widget,
415 static void compose_undo_cb (GtkAction *action,
417 static void compose_redo_cb (GtkAction *action,
419 static void compose_cut_cb (GtkAction *action,
421 static void compose_copy_cb (GtkAction *action,
423 static void compose_paste_cb (GtkAction *action,
425 static void compose_paste_as_quote_cb (GtkAction *action,
427 static void compose_paste_no_wrap_cb (GtkAction *action,
429 static void compose_paste_wrap_cb (GtkAction *action,
431 static void compose_allsel_cb (GtkAction *action,
434 static void compose_advanced_action_cb (GtkAction *action,
437 static void compose_grab_focus_cb (GtkWidget *widget,
440 static void compose_changed_cb (GtkTextBuffer *textbuf,
443 static void compose_wrap_cb (GtkAction *action,
445 static void compose_wrap_all_cb (GtkAction *action,
447 static void compose_find_cb (GtkAction *action,
449 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
451 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
454 static void compose_toggle_ruler_cb (GtkToggleAction *action,
456 static void compose_toggle_sign_cb (GtkToggleAction *action,
458 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
460 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
461 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
462 static void activate_privacy_system (Compose *compose,
463 PrefsAccount *account,
465 static void compose_use_signing(Compose *compose, gboolean use_signing);
466 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
467 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
469 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
471 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
472 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
473 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
475 static void compose_attach_drag_received_cb (GtkWidget *widget,
476 GdkDragContext *drag_context,
479 GtkSelectionData *data,
483 static void compose_insert_drag_received_cb (GtkWidget *widget,
484 GdkDragContext *drag_context,
487 GtkSelectionData *data,
491 static void compose_header_drag_received_cb (GtkWidget *widget,
492 GdkDragContext *drag_context,
495 GtkSelectionData *data,
500 static gboolean compose_drag_drop (GtkWidget *widget,
501 GdkDragContext *drag_context,
503 guint time, gpointer user_data);
504 static gboolean completion_set_focus_to_subject
509 static void text_inserted (GtkTextBuffer *buffer,
514 static Compose *compose_generic_reply(MsgInfo *msginfo,
515 ComposeQuoteMode quote_mode,
519 gboolean followup_and_reply_to,
522 static void compose_headerentry_changed_cb (GtkWidget *entry,
523 ComposeHeaderEntry *headerentry);
524 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
526 ComposeHeaderEntry *headerentry);
527 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
528 ComposeHeaderEntry *headerentry);
530 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
532 static void compose_allow_user_actions (Compose *compose, gboolean allow);
534 static void compose_nothing_cb (GtkAction *action, gpointer data)
540 static void compose_check_all (GtkAction *action, gpointer data);
541 static void compose_highlight_all (GtkAction *action, gpointer data);
542 static void compose_check_backwards (GtkAction *action, gpointer data);
543 static void compose_check_forwards_go (GtkAction *action, gpointer data);
546 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
548 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
551 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
552 FolderItem *folder_item);
554 static void compose_attach_update_label(Compose *compose);
555 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
556 gboolean respect_default_to);
558 static GtkActionEntry compose_popup_entries[] =
560 {"Compose", NULL, "Compose" },
561 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
562 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
563 {"Compose/---", NULL, "---", NULL, NULL, NULL },
564 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
567 static GtkActionEntry compose_entries[] =
569 {"Menu", NULL, "Menu" },
571 {"Message", NULL, N_("_Message") },
572 {"Edit", NULL, N_("_Edit") },
574 {"Spelling", NULL, N_("_Spelling") },
576 {"Options", NULL, N_("_Options") },
577 {"Tools", NULL, N_("_Tools") },
578 {"Help", NULL, N_("_Help") },
580 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
581 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
582 {"Message/---", NULL, "---" },
584 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
585 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
586 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
587 /* {"Message/---", NULL, "---" }, */
588 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
589 /* {"Message/---", NULL, "---" }, */
590 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
591 /* {"Message/---", NULL, "---" }, */
592 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
595 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
596 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
597 {"Edit/---", NULL, "---" },
599 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
600 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
601 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
603 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
604 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
605 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
606 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
608 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
610 {"Edit/Advanced", NULL, N_("A_dvanced") },
611 {"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*/
612 {"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*/
613 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
614 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
615 {"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*/
616 {"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*/
617 {"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*/
618 {"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*/
619 {"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*/
620 {"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*/
621 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
622 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
623 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
624 {"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*/
626 /* {"Edit/---", NULL, "---" }, */
627 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
629 /* {"Edit/---", NULL, "---" }, */
630 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
631 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
632 /* {"Edit/---", NULL, "---" }, */
633 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
636 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
637 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
638 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
639 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
641 {"Spelling/---", NULL, "---" },
642 {"Spelling/Options", NULL, N_("_Options") },
647 {"Options/ReplyMode", NULL, N_("Reply _mode") },
648 {"Options/---", NULL, "---" },
649 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
650 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
652 /* {"Options/---", NULL, "---" }, */
654 {"Options/Priority", NULL, N_("_Priority") },
656 {"Options/Encoding", NULL, N_("Character _encoding") },
657 {"Options/Encoding/---", NULL, "---" },
658 #define ENC_ACTION(cs_char,c_char,string) \
659 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
661 {"Options/Encoding/Western", NULL, N_("Western European") },
662 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
663 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
664 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
665 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
666 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
667 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
668 {"Options/Encoding/Korean", NULL, N_("Korean") },
669 {"Options/Encoding/Thai", NULL, N_("Thai") },
672 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
674 {"Tools/Template", NULL, N_("_Template") },
675 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
676 {"Tools/Actions", NULL, N_("Actio_ns") },
677 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
680 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
683 static GtkToggleActionEntry compose_toggle_entries[] =
685 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
686 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
687 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
688 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
689 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
690 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
691 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
694 static GtkRadioActionEntry compose_radio_rm_entries[] =
696 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
697 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
698 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
699 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
702 static GtkRadioActionEntry compose_radio_prio_entries[] =
704 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
705 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
706 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
707 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
708 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
711 static GtkRadioActionEntry compose_radio_enc_entries[] =
713 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
729 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
730 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
731 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
732 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
733 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
734 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
735 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
736 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
737 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
738 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
739 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
740 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
741 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
742 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
743 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
744 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
747 static GtkTargetEntry compose_mime_types[] =
749 {"text/uri-list", 0, 0},
750 {"UTF8_STRING", 0, 0},
754 static gboolean compose_put_existing_to_front(MsgInfo *info)
756 GList *compose_list = compose_get_compose_list();
760 for (elem = compose_list; elem != NULL && elem->data != NULL;
762 Compose *c = (Compose*)elem->data;
764 if (!c->targetinfo || !c->targetinfo->msgid ||
768 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
769 gtkut_window_popup(c->window);
777 static GdkColor quote_color1 =
778 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
779 static GdkColor quote_color2 =
780 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
781 static GdkColor quote_color3 =
782 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
784 static GdkColor quote_bgcolor1 =
785 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
786 static GdkColor quote_bgcolor2 =
787 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
788 static GdkColor quote_bgcolor3 =
789 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
791 static GdkColor signature_color = {
798 static GdkColor uri_color = {
805 static void compose_create_tags(GtkTextView *text, Compose *compose)
807 GtkTextBuffer *buffer;
808 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
809 #if !GTK_CHECK_VERSION(2, 24, 0)
816 buffer = gtk_text_view_get_buffer(text);
818 if (prefs_common.enable_color) {
819 /* grab the quote colors, converting from an int to a GdkColor */
820 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
822 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
824 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
826 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
828 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
830 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
832 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
834 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
837 signature_color = quote_color1 = quote_color2 = quote_color3 =
838 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
841 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
842 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
843 "foreground-gdk", "e_color1,
844 "paragraph-background-gdk", "e_bgcolor1,
846 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
847 "foreground-gdk", "e_color2,
848 "paragraph-background-gdk", "e_bgcolor2,
850 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
851 "foreground-gdk", "e_color3,
852 "paragraph-background-gdk", "e_bgcolor3,
855 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
856 "foreground-gdk", "e_color1,
858 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
859 "foreground-gdk", "e_color2,
861 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
862 "foreground-gdk", "e_color3,
866 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
867 "foreground-gdk", &signature_color,
870 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
871 "foreground-gdk", &uri_color,
873 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
874 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
876 color[0] = quote_color1;
877 color[1] = quote_color2;
878 color[2] = quote_color3;
879 color[3] = quote_bgcolor1;
880 color[4] = quote_bgcolor2;
881 color[5] = quote_bgcolor3;
882 color[6] = signature_color;
883 color[7] = uri_color;
884 #if !GTK_CHECK_VERSION(2, 24, 0)
885 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
886 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
888 for (i = 0; i < 8; i++) {
889 if (success[i] == FALSE) {
892 g_warning("Compose: color allocation failed.\n");
893 style = gtk_widget_get_style(GTK_WIDGET(text));
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 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1809 body_fmt = gettext(prefs_common.fw_quotefmt);
1813 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1815 full_msginfo = procmsg_msginfo_copy(msginfo);
1817 /* use the forward format of folder (if enabled), or the account's one
1818 (if enabled) or fallback to the global forward format, which is always
1819 enabled (even if empty), and use the relevant quotemark */
1820 if (msginfo->folder && msginfo->folder->prefs &&
1821 msginfo->folder->prefs->forward_with_format) {
1822 qmark = msginfo->folder->prefs->forward_quotemark;
1823 body_fmt = msginfo->folder->prefs->forward_body_format;
1825 } else if (account->forward_with_format) {
1826 qmark = account->forward_quotemark;
1827 body_fmt = account->forward_body_format;
1830 qmark = prefs_common.fw_quotemark;
1831 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1832 body_fmt = gettext(prefs_common.fw_quotefmt);
1837 /* empty quotemark is not allowed */
1838 if (qmark == NULL || *qmark == '\0')
1841 compose_quote_fmt(compose, full_msginfo,
1842 body_fmt, qmark, body, FALSE, TRUE,
1843 _("The body of the \"Forward\" template has an error at line %d."));
1844 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1845 quote_fmt_reset_vartable();
1846 compose_attach_parts(compose, msginfo);
1848 procmsg_msginfo_free(full_msginfo);
1851 SIGNAL_BLOCK(textbuf);
1853 if (account->auto_sig)
1854 compose_insert_sig(compose, FALSE);
1856 compose_wrap_all(compose);
1859 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1860 gtkaspell_highlight_all(compose->gtkaspell);
1861 gtkaspell_unblock_check(compose->gtkaspell);
1863 SIGNAL_UNBLOCK(textbuf);
1865 cursor_pos = quote_fmt_get_cursor_pos();
1866 if (cursor_pos == -1)
1867 gtk_widget_grab_focus(compose->header_last->entry);
1869 gtk_widget_grab_focus(compose->text);
1871 if (!no_extedit && prefs_common.auto_exteditor)
1872 compose_exec_ext_editor(compose);
1875 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1876 gchar *folderidentifier;
1878 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1879 folderidentifier = folder_item_get_identifier(msginfo->folder);
1880 compose_set_save_to(compose, folderidentifier);
1881 g_free(folderidentifier);
1884 undo_unblock(compose->undostruct);
1886 compose->modified = FALSE;
1887 compose_set_title(compose);
1889 compose->updating = FALSE;
1890 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1891 SCROLL_TO_CURSOR(compose);
1893 if (compose->deferred_destroy) {
1894 compose_destroy(compose);
1898 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1903 #undef INSERT_FW_HEADER
1905 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1908 GtkTextView *textview;
1909 GtkTextBuffer *textbuf;
1913 gboolean single_mail = TRUE;
1915 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1917 if (g_slist_length(msginfo_list) > 1)
1918 single_mail = FALSE;
1920 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1921 if (((MsgInfo *)msginfo->data)->folder == NULL)
1924 /* guess account from first selected message */
1926 !(account = compose_guess_forward_account_from_msginfo
1927 (msginfo_list->data)))
1928 account = cur_account;
1930 cm_return_val_if_fail(account != NULL, NULL);
1932 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1933 if (msginfo->data) {
1934 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1935 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1939 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1940 g_warning("no msginfo_list");
1944 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1946 compose->updating = TRUE;
1948 /* override from name according to folder properties */
1949 if (msginfo_list->data) {
1950 MsgInfo *msginfo = msginfo_list->data;
1952 if (msginfo->folder && msginfo->folder->prefs &&
1953 msginfo->folder->prefs->forward_with_format &&
1954 msginfo->folder->prefs->forward_override_from_format &&
1955 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1960 /* decode \-escape sequences in the internal representation of the quote format */
1961 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1962 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1965 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1966 compose->gtkaspell);
1968 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1970 quote_fmt_scan_string(tmp);
1973 buf = quote_fmt_get_buffer();
1975 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1977 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1978 quote_fmt_reset_vartable();
1984 textview = GTK_TEXT_VIEW(compose->text);
1985 textbuf = gtk_text_view_get_buffer(textview);
1986 compose_create_tags(textview, compose);
1988 undo_block(compose->undostruct);
1989 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1990 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1992 if (!is_file_exist(msgfile))
1993 g_warning("%s: file not exist\n", msgfile);
1995 compose_attach_append(compose, msgfile, msgfile,
1996 "message/rfc822", NULL);
2001 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2002 if (info->subject && *info->subject) {
2003 gchar *buf, *buf2, *p;
2005 buf = p = g_strdup(info->subject);
2006 p += subject_get_prefix_length(p);
2007 memmove(buf, p, strlen(p) + 1);
2009 buf2 = g_strdup_printf("Fw: %s", buf);
2010 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2016 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2017 _("Fw: multiple emails"));
2020 SIGNAL_BLOCK(textbuf);
2022 if (account->auto_sig)
2023 compose_insert_sig(compose, FALSE);
2025 compose_wrap_all(compose);
2027 SIGNAL_UNBLOCK(textbuf);
2029 gtk_text_buffer_get_start_iter(textbuf, &iter);
2030 gtk_text_buffer_place_cursor(textbuf, &iter);
2032 gtk_widget_grab_focus(compose->header_last->entry);
2033 undo_unblock(compose->undostruct);
2034 compose->modified = FALSE;
2035 compose_set_title(compose);
2037 compose->updating = FALSE;
2038 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2039 SCROLL_TO_CURSOR(compose);
2041 if (compose->deferred_destroy) {
2042 compose_destroy(compose);
2046 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2051 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2053 GtkTextIter start = *iter;
2054 GtkTextIter end_iter;
2055 int start_pos = gtk_text_iter_get_offset(&start);
2057 if (!compose->account->sig_sep)
2060 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2061 start_pos+strlen(compose->account->sig_sep));
2063 /* check sig separator */
2064 str = gtk_text_iter_get_text(&start, &end_iter);
2065 if (!strcmp(str, compose->account->sig_sep)) {
2067 /* check end of line (\n) */
2068 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2069 start_pos+strlen(compose->account->sig_sep));
2070 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2071 start_pos+strlen(compose->account->sig_sep)+1);
2072 tmp = gtk_text_iter_get_text(&start, &end_iter);
2073 if (!strcmp(tmp,"\n")) {
2085 static void compose_colorize_signature(Compose *compose)
2087 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2089 GtkTextIter end_iter;
2090 gtk_text_buffer_get_start_iter(buffer, &iter);
2091 while (gtk_text_iter_forward_line(&iter))
2092 if (compose_is_sig_separator(compose, buffer, &iter)) {
2093 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2094 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2098 #define BLOCK_WRAP() { \
2099 prev_autowrap = compose->autowrap; \
2100 buffer = gtk_text_view_get_buffer( \
2101 GTK_TEXT_VIEW(compose->text)); \
2102 compose->autowrap = FALSE; \
2104 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2105 G_CALLBACK(compose_changed_cb), \
2107 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2108 G_CALLBACK(text_inserted), \
2111 #define UNBLOCK_WRAP() { \
2112 compose->autowrap = prev_autowrap; \
2113 if (compose->autowrap) { \
2114 gint old = compose->draft_timeout_tag; \
2115 compose->draft_timeout_tag = -2; \
2116 compose_wrap_all(compose); \
2117 compose->draft_timeout_tag = old; \
2120 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2121 G_CALLBACK(compose_changed_cb), \
2123 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2124 G_CALLBACK(text_inserted), \
2128 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2130 Compose *compose = NULL;
2131 PrefsAccount *account = NULL;
2132 GtkTextView *textview;
2133 GtkTextBuffer *textbuf;
2137 gchar buf[BUFFSIZE];
2138 gboolean use_signing = FALSE;
2139 gboolean use_encryption = FALSE;
2140 gchar *privacy_system = NULL;
2141 int priority = PRIORITY_NORMAL;
2142 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2143 gboolean autowrap = prefs_common.autowrap;
2144 gboolean autoindent = prefs_common.auto_indent;
2145 HeaderEntry *manual_headers = NULL;
2147 cm_return_val_if_fail(msginfo != NULL, NULL);
2148 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2150 if (compose_put_existing_to_front(msginfo)) {
2154 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2155 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2156 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2157 gchar queueheader_buf[BUFFSIZE];
2160 /* Select Account from queue headers */
2161 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2162 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2163 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2164 account = account_find_from_id(id);
2166 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2167 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2168 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2169 account = account_find_from_id(id);
2171 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2172 sizeof(queueheader_buf), "NAID:")) {
2173 id = atoi(&queueheader_buf[strlen("NAID:")]);
2174 account = account_find_from_id(id);
2176 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2177 sizeof(queueheader_buf), "MAID:")) {
2178 id = atoi(&queueheader_buf[strlen("MAID:")]);
2179 account = account_find_from_id(id);
2181 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2182 sizeof(queueheader_buf), "S:")) {
2183 account = account_find_from_address(queueheader_buf, FALSE);
2185 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2186 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2187 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2188 use_signing = param;
2191 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2192 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2193 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2194 use_signing = param;
2197 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2198 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2199 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2200 use_encryption = param;
2202 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2203 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2204 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2205 use_encryption = param;
2207 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2208 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2209 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2212 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2213 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2214 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2217 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2218 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2219 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2221 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2222 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2223 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2225 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2226 sizeof(queueheader_buf), "X-Priority: ")) {
2227 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2230 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2231 sizeof(queueheader_buf), "RMID:")) {
2232 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2233 if (tokens[0] && tokens[1] && tokens[2]) {
2234 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2235 if (orig_item != NULL) {
2236 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2241 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2242 sizeof(queueheader_buf), "FMID:")) {
2243 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2244 if (tokens[0] && tokens[1] && tokens[2]) {
2245 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2246 if (orig_item != NULL) {
2247 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2252 /* Get manual headers */
2253 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2254 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2255 if (*listmh != '\0') {
2256 debug_print("Got manual headers: %s\n", listmh);
2257 manual_headers = procheader_entries_from_str(listmh);
2262 account = msginfo->folder->folder->account;
2265 if (!account && prefs_common.reedit_account_autosel) {
2266 gchar from[BUFFSIZE];
2267 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2268 extract_address(from);
2269 account = account_find_from_address(from, FALSE);
2273 account = cur_account;
2275 cm_return_val_if_fail(account != NULL, NULL);
2277 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2279 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2280 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2281 compose->autowrap = autowrap;
2282 compose->replyinfo = replyinfo;
2283 compose->fwdinfo = fwdinfo;
2285 compose->updating = TRUE;
2286 compose->priority = priority;
2288 if (privacy_system != NULL) {
2289 compose->privacy_system = privacy_system;
2290 compose_use_signing(compose, use_signing);
2291 compose_use_encryption(compose, use_encryption);
2292 compose_update_privacy_system_menu_item(compose, FALSE);
2294 activate_privacy_system(compose, account, FALSE);
2297 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2299 compose_extract_original_charset(compose);
2301 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2302 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2303 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2304 gchar queueheader_buf[BUFFSIZE];
2306 /* Set message save folder */
2307 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2308 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2309 compose_set_save_to(compose, &queueheader_buf[4]);
2311 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2312 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2314 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2319 if (compose_parse_header(compose, msginfo) < 0) {
2320 compose->updating = FALSE;
2321 compose_destroy(compose);
2324 compose_reedit_set_entry(compose, msginfo);
2326 textview = GTK_TEXT_VIEW(compose->text);
2327 textbuf = gtk_text_view_get_buffer(textview);
2328 compose_create_tags(textview, compose);
2330 mark = gtk_text_buffer_get_insert(textbuf);
2331 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2333 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2334 G_CALLBACK(compose_changed_cb),
2337 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2338 fp = procmime_get_first_encrypted_text_content(msginfo);
2340 compose_force_encryption(compose, account, TRUE, NULL);
2343 fp = procmime_get_first_text_content(msginfo);
2346 g_warning("Can't get text part\n");
2350 gboolean prev_autowrap = compose->autowrap;
2351 GtkTextBuffer *buffer = textbuf;
2353 while (fgets(buf, sizeof(buf), fp) != NULL) {
2355 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2361 compose_attach_parts(compose, msginfo);
2363 compose_colorize_signature(compose);
2365 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2366 G_CALLBACK(compose_changed_cb),
2369 if (manual_headers != NULL) {
2370 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2371 procheader_entries_free(manual_headers);
2372 compose->updating = FALSE;
2373 compose_destroy(compose);
2376 procheader_entries_free(manual_headers);
2379 gtk_widget_grab_focus(compose->text);
2381 if (prefs_common.auto_exteditor) {
2382 compose_exec_ext_editor(compose);
2384 compose->modified = FALSE;
2385 compose_set_title(compose);
2387 compose->updating = FALSE;
2388 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2389 SCROLL_TO_CURSOR(compose);
2391 if (compose->deferred_destroy) {
2392 compose_destroy(compose);
2396 compose->sig_str = account_get_signature_str(compose->account);
2398 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2403 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2410 cm_return_val_if_fail(msginfo != NULL, NULL);
2413 account = account_get_reply_account(msginfo,
2414 prefs_common.reply_account_autosel);
2415 cm_return_val_if_fail(account != NULL, NULL);
2417 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2419 compose->updating = TRUE;
2421 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2422 compose->replyinfo = NULL;
2423 compose->fwdinfo = NULL;
2425 compose_show_first_last_header(compose, TRUE);
2427 gtk_widget_grab_focus(compose->header_last->entry);
2429 filename = procmsg_get_message_file(msginfo);
2431 if (filename == NULL) {
2432 compose->updating = FALSE;
2433 compose_destroy(compose);
2438 compose->redirect_filename = filename;
2440 /* Set save folder */
2441 item = msginfo->folder;
2442 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2443 gchar *folderidentifier;
2445 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2446 folderidentifier = folder_item_get_identifier(item);
2447 compose_set_save_to(compose, folderidentifier);
2448 g_free(folderidentifier);
2451 compose_attach_parts(compose, msginfo);
2453 if (msginfo->subject)
2454 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2456 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2458 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2459 _("The body of the \"Redirect\" template has an error at line %d."));
2460 quote_fmt_reset_vartable();
2461 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2463 compose_colorize_signature(compose);
2466 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2467 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2468 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2470 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2471 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2472 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2473 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2474 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2475 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2476 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2477 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2479 if (compose->toolbar->draft_btn)
2480 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2481 if (compose->toolbar->insert_btn)
2482 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2483 if (compose->toolbar->attach_btn)
2484 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2485 if (compose->toolbar->sig_btn)
2486 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2487 if (compose->toolbar->exteditor_btn)
2488 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2489 if (compose->toolbar->linewrap_current_btn)
2490 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2491 if (compose->toolbar->linewrap_all_btn)
2492 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2494 compose->modified = FALSE;
2495 compose_set_title(compose);
2496 compose->updating = FALSE;
2497 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2498 SCROLL_TO_CURSOR(compose);
2500 if (compose->deferred_destroy) {
2501 compose_destroy(compose);
2505 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2510 GList *compose_get_compose_list(void)
2512 return compose_list;
2515 void compose_entry_append(Compose *compose, const gchar *address,
2516 ComposeEntryType type, ComposePrefType pref_type)
2518 const gchar *header;
2520 gboolean in_quote = FALSE;
2521 if (!address || *address == '\0') return;
2528 header = N_("Bcc:");
2530 case COMPOSE_REPLYTO:
2531 header = N_("Reply-To:");
2533 case COMPOSE_NEWSGROUPS:
2534 header = N_("Newsgroups:");
2536 case COMPOSE_FOLLOWUPTO:
2537 header = N_( "Followup-To:");
2539 case COMPOSE_INREPLYTO:
2540 header = N_( "In-Reply-To:");
2547 header = prefs_common_translated_header_name(header);
2549 cur = begin = (gchar *)address;
2551 /* we separate the line by commas, but not if we're inside a quoted
2553 while (*cur != '\0') {
2555 in_quote = !in_quote;
2556 if (*cur == ',' && !in_quote) {
2557 gchar *tmp = g_strdup(begin);
2559 tmp[cur-begin]='\0';
2562 while (*tmp == ' ' || *tmp == '\t')
2564 compose_add_header_entry(compose, header, tmp, pref_type);
2571 gchar *tmp = g_strdup(begin);
2573 tmp[cur-begin]='\0';
2576 while (*tmp == ' ' || *tmp == '\t')
2578 compose_add_header_entry(compose, header, tmp, pref_type);
2583 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2585 #if !GTK_CHECK_VERSION(3, 0, 0)
2586 static GdkColor yellow;
2587 static GdkColor black;
2588 static gboolean yellow_initialised = FALSE;
2590 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2591 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2596 #if !GTK_CHECK_VERSION(3, 0, 0)
2597 if (!yellow_initialised) {
2598 gdk_color_parse("#f5f6be", &yellow);
2599 gdk_color_parse("#000000", &black);
2600 yellow_initialised = gdk_colormap_alloc_color(
2601 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2602 yellow_initialised &= gdk_colormap_alloc_color(
2603 gdk_colormap_get_system(), &black, FALSE, TRUE);
2607 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2608 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2609 if (gtk_entry_get_text(entry) &&
2610 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2611 #if !GTK_CHECK_VERSION(3, 0, 0)
2612 if (yellow_initialised) {
2614 gtk_widget_modify_base(
2615 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2616 GTK_STATE_NORMAL, &yellow);
2617 gtk_widget_modify_text(
2618 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2619 GTK_STATE_NORMAL, &black);
2620 #if !GTK_CHECK_VERSION(3, 0, 0)
2627 void compose_toolbar_cb(gint action, gpointer data)
2629 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2630 Compose *compose = (Compose*)toolbar_item->parent;
2632 cm_return_if_fail(compose != NULL);
2636 compose_send_cb(NULL, compose);
2639 compose_send_later_cb(NULL, compose);
2642 compose_draft(compose, COMPOSE_QUIT_EDITING);
2645 compose_insert_file_cb(NULL, compose);
2648 compose_attach_cb(NULL, compose);
2651 compose_insert_sig(compose, FALSE);
2654 compose_ext_editor_cb(NULL, compose);
2656 case A_LINEWRAP_CURRENT:
2657 compose_beautify_paragraph(compose, NULL, TRUE);
2659 case A_LINEWRAP_ALL:
2660 compose_wrap_all_full(compose, TRUE);
2663 compose_address_cb(NULL, compose);
2666 case A_CHECK_SPELLING:
2667 compose_check_all(NULL, compose);
2675 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2680 gchar *subject = NULL;
2684 gchar **attach = NULL;
2685 gchar *inreplyto = NULL;
2686 MailField mfield = NO_FIELD_PRESENT;
2688 /* get mailto parts but skip from */
2689 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2692 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2693 mfield = TO_FIELD_PRESENT;
2696 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2698 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2700 if (!g_utf8_validate (subject, -1, NULL)) {
2701 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2702 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2705 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2707 mfield = SUBJECT_FIELD_PRESENT;
2710 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2711 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2714 gboolean prev_autowrap = compose->autowrap;
2716 compose->autowrap = FALSE;
2718 mark = gtk_text_buffer_get_insert(buffer);
2719 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2721 if (!g_utf8_validate (body, -1, NULL)) {
2722 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2723 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2726 gtk_text_buffer_insert(buffer, &iter, body, -1);
2728 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2730 compose->autowrap = prev_autowrap;
2731 if (compose->autowrap)
2732 compose_wrap_all(compose);
2733 mfield = BODY_FIELD_PRESENT;
2737 gint i = 0, att = 0;
2738 gchar *warn_files = NULL;
2739 while (attach[i] != NULL) {
2740 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2741 if (utf8_filename) {
2742 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2743 gchar *tmp = g_strdup_printf("%s%s\n",
2744 warn_files?warn_files:"",
2750 g_free(utf8_filename);
2752 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2757 alertpanel_notice(ngettext(
2758 "The following file has been attached: \n%s",
2759 "The following files have been attached: \n%s", att), warn_files);
2764 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2777 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2779 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2780 {"Cc:", NULL, TRUE},
2781 {"References:", NULL, FALSE},
2782 {"Bcc:", NULL, TRUE},
2783 {"Newsgroups:", NULL, TRUE},
2784 {"Followup-To:", NULL, TRUE},
2785 {"List-Post:", NULL, FALSE},
2786 {"X-Priority:", NULL, FALSE},
2787 {NULL, NULL, FALSE}};
2803 cm_return_val_if_fail(msginfo != NULL, -1);
2805 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2806 procheader_get_header_fields(fp, hentry);
2809 if (hentry[H_REPLY_TO].body != NULL) {
2810 if (hentry[H_REPLY_TO].body[0] != '\0') {
2812 conv_unmime_header(hentry[H_REPLY_TO].body,
2815 g_free(hentry[H_REPLY_TO].body);
2816 hentry[H_REPLY_TO].body = NULL;
2818 if (hentry[H_CC].body != NULL) {
2819 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2820 g_free(hentry[H_CC].body);
2821 hentry[H_CC].body = NULL;
2823 if (hentry[H_REFERENCES].body != NULL) {
2824 if (compose->mode == COMPOSE_REEDIT)
2825 compose->references = hentry[H_REFERENCES].body;
2827 compose->references = compose_parse_references
2828 (hentry[H_REFERENCES].body, msginfo->msgid);
2829 g_free(hentry[H_REFERENCES].body);
2831 hentry[H_REFERENCES].body = NULL;
2833 if (hentry[H_BCC].body != NULL) {
2834 if (compose->mode == COMPOSE_REEDIT)
2836 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2837 g_free(hentry[H_BCC].body);
2838 hentry[H_BCC].body = NULL;
2840 if (hentry[H_NEWSGROUPS].body != NULL) {
2841 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2842 hentry[H_NEWSGROUPS].body = NULL;
2844 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2845 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2846 compose->followup_to =
2847 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2850 g_free(hentry[H_FOLLOWUP_TO].body);
2851 hentry[H_FOLLOWUP_TO].body = NULL;
2853 if (hentry[H_LIST_POST].body != NULL) {
2854 gchar *to = NULL, *start = NULL;
2856 extract_address(hentry[H_LIST_POST].body);
2857 if (hentry[H_LIST_POST].body[0] != '\0') {
2858 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2860 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2861 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2864 g_free(compose->ml_post);
2865 compose->ml_post = to;
2868 g_free(hentry[H_LIST_POST].body);
2869 hentry[H_LIST_POST].body = NULL;
2872 /* CLAWS - X-Priority */
2873 if (compose->mode == COMPOSE_REEDIT)
2874 if (hentry[H_X_PRIORITY].body != NULL) {
2877 priority = atoi(hentry[H_X_PRIORITY].body);
2878 g_free(hentry[H_X_PRIORITY].body);
2880 hentry[H_X_PRIORITY].body = NULL;
2882 if (priority < PRIORITY_HIGHEST ||
2883 priority > PRIORITY_LOWEST)
2884 priority = PRIORITY_NORMAL;
2886 compose->priority = priority;
2889 if (compose->mode == COMPOSE_REEDIT) {
2890 if (msginfo->inreplyto && *msginfo->inreplyto)
2891 compose->inreplyto = g_strdup(msginfo->inreplyto);
2895 if (msginfo->msgid && *msginfo->msgid)
2896 compose->inreplyto = g_strdup(msginfo->msgid);
2898 if (!compose->references) {
2899 if (msginfo->msgid && *msginfo->msgid) {
2900 if (msginfo->inreplyto && *msginfo->inreplyto)
2901 compose->references =
2902 g_strdup_printf("<%s>\n\t<%s>",
2906 compose->references =
2907 g_strconcat("<", msginfo->msgid, ">",
2909 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2910 compose->references =
2911 g_strconcat("<", msginfo->inreplyto, ">",
2919 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2924 cm_return_val_if_fail(msginfo != NULL, -1);
2926 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2927 procheader_get_header_fields(fp, entries);
2931 while (he != NULL && he->name != NULL) {
2933 GtkListStore *model = NULL;
2935 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
2936 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
2937 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
2938 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
2939 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
2946 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2948 GSList *ref_id_list, *cur;
2952 ref_id_list = references_list_append(NULL, ref);
2953 if (!ref_id_list) return NULL;
2954 if (msgid && *msgid)
2955 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2960 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2961 /* "<" + Message-ID + ">" + CR+LF+TAB */
2962 len += strlen((gchar *)cur->data) + 5;
2964 if (len > MAX_REFERENCES_LEN) {
2965 /* remove second message-ID */
2966 if (ref_id_list && ref_id_list->next &&
2967 ref_id_list->next->next) {
2968 g_free(ref_id_list->next->data);
2969 ref_id_list = g_slist_remove
2970 (ref_id_list, ref_id_list->next->data);
2972 slist_free_strings(ref_id_list);
2973 g_slist_free(ref_id_list);
2980 new_ref = g_string_new("");
2981 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2982 if (new_ref->len > 0)
2983 g_string_append(new_ref, "\n\t");
2984 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2987 slist_free_strings(ref_id_list);
2988 g_slist_free(ref_id_list);
2990 new_ref_str = new_ref->str;
2991 g_string_free(new_ref, FALSE);
2996 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2997 const gchar *fmt, const gchar *qmark,
2998 const gchar *body, gboolean rewrap,
2999 gboolean need_unescape,
3000 const gchar *err_msg)
3002 MsgInfo* dummyinfo = NULL;
3003 gchar *quote_str = NULL;
3005 gboolean prev_autowrap;
3006 const gchar *trimmed_body = body;
3007 gint cursor_pos = -1;
3008 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3009 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3014 SIGNAL_BLOCK(buffer);
3017 dummyinfo = compose_msginfo_new_from_compose(compose);
3018 msginfo = dummyinfo;
3021 if (qmark != NULL) {
3023 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3024 compose->gtkaspell);
3026 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3028 quote_fmt_scan_string(qmark);
3031 buf = quote_fmt_get_buffer();
3033 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3035 Xstrdup_a(quote_str, buf, goto error)
3038 if (fmt && *fmt != '\0') {
3041 while (*trimmed_body == '\n')
3045 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3046 compose->gtkaspell);
3048 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3050 if (need_unescape) {
3053 /* decode \-escape sequences in the internal representation of the quote format */
3054 tmp = g_malloc(strlen(fmt)+1);
3055 pref_get_unescaped_pref(tmp, fmt);
3056 quote_fmt_scan_string(tmp);
3060 quote_fmt_scan_string(fmt);
3064 buf = quote_fmt_get_buffer();
3066 gint line = quote_fmt_get_line();
3067 alertpanel_error(err_msg, line);
3073 prev_autowrap = compose->autowrap;
3074 compose->autowrap = FALSE;
3076 mark = gtk_text_buffer_get_insert(buffer);
3077 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3078 if (g_utf8_validate(buf, -1, NULL)) {
3079 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3081 gchar *tmpout = NULL;
3082 tmpout = conv_codeset_strdup
3083 (buf, conv_get_locale_charset_str_no_utf8(),
3085 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3087 tmpout = g_malloc(strlen(buf)*2+1);
3088 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3090 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3094 cursor_pos = quote_fmt_get_cursor_pos();
3095 if (cursor_pos == -1)
3096 cursor_pos = gtk_text_iter_get_offset(&iter);
3097 compose->set_cursor_pos = cursor_pos;
3099 gtk_text_buffer_get_start_iter(buffer, &iter);
3100 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3101 gtk_text_buffer_place_cursor(buffer, &iter);
3103 compose->autowrap = prev_autowrap;
3104 if (compose->autowrap && rewrap)
3105 compose_wrap_all(compose);
3112 SIGNAL_UNBLOCK(buffer);
3114 procmsg_msginfo_free( dummyinfo );
3119 /* if ml_post is of type addr@host and from is of type
3120 * addr-anything@host, return TRUE
3122 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3124 gchar *left_ml = NULL;
3125 gchar *right_ml = NULL;
3126 gchar *left_from = NULL;
3127 gchar *right_from = NULL;
3128 gboolean result = FALSE;
3130 if (!ml_post || !from)
3133 left_ml = g_strdup(ml_post);
3134 if (strstr(left_ml, "@")) {
3135 right_ml = strstr(left_ml, "@")+1;
3136 *(strstr(left_ml, "@")) = '\0';
3139 left_from = g_strdup(from);
3140 if (strstr(left_from, "@")) {
3141 right_from = strstr(left_from, "@")+1;
3142 *(strstr(left_from, "@")) = '\0';
3145 if (left_ml && left_from && right_ml && right_from
3146 && !strncmp(left_from, left_ml, strlen(left_ml))
3147 && !strcmp(right_from, right_ml)) {
3156 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3157 gboolean respect_default_to)
3161 if (!folder || !folder->prefs)
3164 if (respect_default_to && folder->prefs->enable_default_to) {
3165 compose_entry_append(compose, folder->prefs->default_to,
3166 COMPOSE_TO, PREF_FOLDER);
3167 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3169 if (folder->prefs->enable_default_cc)
3170 compose_entry_append(compose, folder->prefs->default_cc,
3171 COMPOSE_CC, PREF_FOLDER);
3172 if (folder->prefs->enable_default_bcc)
3173 compose_entry_append(compose, folder->prefs->default_bcc,
3174 COMPOSE_BCC, PREF_FOLDER);
3175 if (folder->prefs->enable_default_replyto)
3176 compose_entry_append(compose, folder->prefs->default_replyto,
3177 COMPOSE_REPLYTO, PREF_FOLDER);
3180 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3185 if (!compose || !msginfo)
3188 if (msginfo->subject && *msginfo->subject) {
3189 buf = p = g_strdup(msginfo->subject);
3190 p += subject_get_prefix_length(p);
3191 memmove(buf, p, strlen(p) + 1);
3193 buf2 = g_strdup_printf("Re: %s", buf);
3194 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3199 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3202 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3203 gboolean to_all, gboolean to_ml,
3205 gboolean followup_and_reply_to)
3207 GSList *cc_list = NULL;
3210 gchar *replyto = NULL;
3211 gchar *ac_email = NULL;
3213 gboolean reply_to_ml = FALSE;
3214 gboolean default_reply_to = FALSE;
3216 cm_return_if_fail(compose->account != NULL);
3217 cm_return_if_fail(msginfo != NULL);
3219 reply_to_ml = to_ml && compose->ml_post;
3221 default_reply_to = msginfo->folder &&
3222 msginfo->folder->prefs->enable_default_reply_to;
3224 if (compose->account->protocol != A_NNTP) {
3225 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3227 if (reply_to_ml && !default_reply_to) {
3229 gboolean is_subscr = is_subscription(compose->ml_post,
3232 /* normal answer to ml post with a reply-to */
3233 compose_entry_append(compose,
3235 COMPOSE_TO, PREF_ML);
3236 if (compose->replyto)
3237 compose_entry_append(compose,
3239 COMPOSE_CC, PREF_ML);
3241 /* answer to subscription confirmation */
3242 if (compose->replyto)
3243 compose_entry_append(compose,
3245 COMPOSE_TO, PREF_ML);
3246 else if (msginfo->from)
3247 compose_entry_append(compose,
3249 COMPOSE_TO, PREF_ML);
3252 else if (!(to_all || to_sender) && default_reply_to) {
3253 compose_entry_append(compose,
3254 msginfo->folder->prefs->default_reply_to,
3255 COMPOSE_TO, PREF_FOLDER);
3256 compose_entry_mark_default_to(compose,
3257 msginfo->folder->prefs->default_reply_to);
3262 Xstrdup_a(tmp1, msginfo->from, return);
3263 extract_address(tmp1);
3264 if (to_all || to_sender ||
3265 !account_find_from_address(tmp1, FALSE))
3266 compose_entry_append(compose,
3267 (compose->replyto && !to_sender)
3268 ? compose->replyto :
3269 msginfo->from ? msginfo->from : "",
3270 COMPOSE_TO, PREF_NONE);
3271 else if (!to_all && !to_sender) {
3272 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3273 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3274 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3275 if (compose->replyto) {
3276 compose_entry_append(compose,
3278 COMPOSE_TO, PREF_NONE);
3280 compose_entry_append(compose,
3281 msginfo->from ? msginfo->from : "",
3282 COMPOSE_TO, PREF_NONE);
3285 /* replying to own mail, use original recp */
3286 compose_entry_append(compose,
3287 msginfo->to ? msginfo->to : "",
3288 COMPOSE_TO, PREF_NONE);
3289 compose_entry_append(compose,
3290 msginfo->cc ? msginfo->cc : "",
3291 COMPOSE_CC, PREF_NONE);
3296 if (to_sender || (compose->followup_to &&
3297 !strncmp(compose->followup_to, "poster", 6)))
3298 compose_entry_append
3300 (compose->replyto ? compose->replyto :
3301 msginfo->from ? msginfo->from : ""),
3302 COMPOSE_TO, PREF_NONE);
3304 else if (followup_and_reply_to || to_all) {
3305 compose_entry_append
3307 (compose->replyto ? compose->replyto :
3308 msginfo->from ? msginfo->from : ""),
3309 COMPOSE_TO, PREF_NONE);
3311 compose_entry_append
3313 compose->followup_to ? compose->followup_to :
3314 compose->newsgroups ? compose->newsgroups : "",
3315 COMPOSE_NEWSGROUPS, PREF_NONE);
3318 compose_entry_append
3320 compose->followup_to ? compose->followup_to :
3321 compose->newsgroups ? compose->newsgroups : "",
3322 COMPOSE_NEWSGROUPS, PREF_NONE);
3324 compose_reply_set_subject(compose, msginfo);
3326 if (to_ml && compose->ml_post) return;
3327 if (!to_all || compose->account->protocol == A_NNTP) return;
3329 if (compose->replyto) {
3330 Xstrdup_a(replyto, compose->replyto, return);
3331 extract_address(replyto);
3333 if (msginfo->from) {
3334 Xstrdup_a(from, msginfo->from, return);
3335 extract_address(from);
3338 if (replyto && from)
3339 cc_list = address_list_append_with_comments(cc_list, from);
3340 if (to_all && msginfo->folder &&
3341 msginfo->folder->prefs->enable_default_reply_to)
3342 cc_list = address_list_append_with_comments(cc_list,
3343 msginfo->folder->prefs->default_reply_to);
3344 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3345 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3347 ac_email = g_utf8_strdown(compose->account->address, -1);
3350 for (cur = cc_list; cur != NULL; cur = cur->next) {
3351 gchar *addr = g_utf8_strdown(cur->data, -1);
3352 extract_address(addr);
3354 if (strcmp(ac_email, addr))
3355 compose_entry_append(compose, (gchar *)cur->data,
3356 COMPOSE_CC, PREF_NONE);
3358 debug_print("Cc address same as compose account's, ignoring\n");
3363 slist_free_strings(cc_list);
3364 g_slist_free(cc_list);
3370 #define SET_ENTRY(entry, str) \
3373 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3376 #define SET_ADDRESS(type, str) \
3379 compose_entry_append(compose, str, type, PREF_NONE); \
3382 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3384 cm_return_if_fail(msginfo != NULL);
3386 SET_ENTRY(subject_entry, msginfo->subject);
3387 SET_ENTRY(from_name, msginfo->from);
3388 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3389 SET_ADDRESS(COMPOSE_CC, compose->cc);
3390 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3391 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3392 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3393 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3395 compose_update_priority_menu_item(compose);
3396 compose_update_privacy_system_menu_item(compose, FALSE);
3397 compose_show_first_last_header(compose, TRUE);
3403 static void compose_insert_sig(Compose *compose, gboolean replace)
3405 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3406 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3408 GtkTextIter iter, iter_end;
3409 gint cur_pos, ins_pos;
3410 gboolean prev_autowrap;
3411 gboolean found = FALSE;
3412 gboolean exists = FALSE;
3414 cm_return_if_fail(compose->account != NULL);
3418 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3419 G_CALLBACK(compose_changed_cb),
3422 mark = gtk_text_buffer_get_insert(buffer);
3423 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3424 cur_pos = gtk_text_iter_get_offset (&iter);
3427 gtk_text_buffer_get_end_iter(buffer, &iter);
3429 exists = (compose->sig_str != NULL);
3432 GtkTextIter first_iter, start_iter, end_iter;
3434 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3436 if (!exists || compose->sig_str[0] == '\0')
3439 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3440 compose->signature_tag);
3443 /* include previous \n\n */
3444 gtk_text_iter_backward_chars(&first_iter, 1);
3445 start_iter = first_iter;
3446 end_iter = first_iter;
3448 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3449 compose->signature_tag);
3450 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3451 compose->signature_tag);
3453 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3459 g_free(compose->sig_str);
3460 compose->sig_str = account_get_signature_str(compose->account);
3462 cur_pos = gtk_text_iter_get_offset(&iter);
3464 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3465 g_free(compose->sig_str);
3466 compose->sig_str = NULL;
3468 if (compose->sig_inserted == FALSE)
3469 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3470 compose->sig_inserted = TRUE;
3472 cur_pos = gtk_text_iter_get_offset(&iter);
3473 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3475 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3476 gtk_text_iter_forward_chars(&iter, 1);
3477 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3478 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3480 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3481 cur_pos = gtk_text_buffer_get_char_count (buffer);
3484 /* put the cursor where it should be
3485 * either where the quote_fmt says, either where it was */
3486 if (compose->set_cursor_pos < 0)
3487 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3489 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3490 compose->set_cursor_pos);
3492 compose->set_cursor_pos = -1;
3493 gtk_text_buffer_place_cursor(buffer, &iter);
3494 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3495 G_CALLBACK(compose_changed_cb),
3501 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3504 GtkTextBuffer *buffer;
3507 const gchar *cur_encoding;
3508 gchar buf[BUFFSIZE];
3511 gboolean prev_autowrap;
3512 gboolean badtxt = FALSE;
3513 struct stat file_stat;
3516 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3518 /* get the size of the file we are about to insert */
3519 ret = g_stat(file, &file_stat);
3521 gchar *shortfile = g_path_get_basename(file);
3522 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3524 return COMPOSE_INSERT_NO_FILE;
3525 } else if (prefs_common.warn_large_insert == TRUE) {
3527 /* ask user for confirmation if the file is large */
3528 if (prefs_common.warn_large_insert_size < 0 ||
3529 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3533 msg = g_strdup_printf(_("You are about to insert a file of %s "
3534 "in the message body. Are you sure you want to do that?"),
3535 to_human_readable(file_stat.st_size));
3536 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3537 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3540 /* do we ask for confirmation next time? */
3541 if (aval & G_ALERTDISABLE) {
3542 /* no confirmation next time, disable feature in preferences */
3543 aval &= ~G_ALERTDISABLE;
3544 prefs_common.warn_large_insert = FALSE;
3547 /* abort file insertion if user canceled action */
3548 if (aval != G_ALERTALTERNATE) {
3549 return COMPOSE_INSERT_NO_FILE;
3555 if ((fp = g_fopen(file, "rb")) == NULL) {
3556 FILE_OP_ERROR(file, "fopen");
3557 return COMPOSE_INSERT_READ_ERROR;
3560 prev_autowrap = compose->autowrap;
3561 compose->autowrap = FALSE;
3563 text = GTK_TEXT_VIEW(compose->text);
3564 buffer = gtk_text_view_get_buffer(text);
3565 mark = gtk_text_buffer_get_insert(buffer);
3566 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3568 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3569 G_CALLBACK(text_inserted),
3572 cur_encoding = conv_get_locale_charset_str_no_utf8();
3574 while (fgets(buf, sizeof(buf), fp) != NULL) {
3577 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3578 str = g_strdup(buf);
3580 str = conv_codeset_strdup
3581 (buf, cur_encoding, CS_INTERNAL);
3584 /* strip <CR> if DOS/Windows file,
3585 replace <CR> with <LF> if Macintosh file. */
3588 if (len > 0 && str[len - 1] != '\n') {
3590 if (str[len] == '\r') str[len] = '\n';
3593 gtk_text_buffer_insert(buffer, &iter, str, -1);
3597 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3598 G_CALLBACK(text_inserted),
3600 compose->autowrap = prev_autowrap;
3601 if (compose->autowrap)
3602 compose_wrap_all(compose);
3607 return COMPOSE_INSERT_INVALID_CHARACTER;
3609 return COMPOSE_INSERT_SUCCESS;
3612 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3613 const gchar *filename,
3614 const gchar *content_type,
3615 const gchar *charset)
3623 GtkListStore *store;
3625 gboolean has_binary = FALSE;
3627 if (!is_file_exist(file)) {
3628 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3629 gboolean result = FALSE;
3630 if (file_from_uri && is_file_exist(file_from_uri)) {
3631 result = compose_attach_append(
3632 compose, file_from_uri,
3633 filename, content_type,
3636 g_free(file_from_uri);
3639 alertpanel_error("File %s doesn't exist\n", filename);
3642 if ((size = get_file_size(file)) < 0) {
3643 alertpanel_error("Can't get file size of %s\n", filename);
3647 alertpanel_error(_("File %s is empty."), filename);
3650 if ((fp = g_fopen(file, "rb")) == NULL) {
3651 alertpanel_error(_("Can't read %s."), filename);
3656 ainfo = g_new0(AttachInfo, 1);
3657 auto_ainfo = g_auto_pointer_new_with_free
3658 (ainfo, (GFreeFunc) compose_attach_info_free);
3659 ainfo->file = g_strdup(file);
3662 ainfo->content_type = g_strdup(content_type);
3663 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3665 MsgFlags flags = {0, 0};
3667 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3668 ainfo->encoding = ENC_7BIT;
3670 ainfo->encoding = ENC_8BIT;
3672 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3673 if (msginfo && msginfo->subject)
3674 name = g_strdup(msginfo->subject);
3676 name = g_path_get_basename(filename ? filename : file);
3678 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3680 procmsg_msginfo_free(msginfo);
3682 if (!g_ascii_strncasecmp(content_type, "text/", 5)) {
3683 ainfo->charset = g_strdup(charset);
3684 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3686 ainfo->encoding = ENC_BASE64;
3688 name = g_path_get_basename(filename ? filename : file);
3689 ainfo->name = g_strdup(name);
3693 ainfo->content_type = procmime_get_mime_type(file);
3694 if (!ainfo->content_type) {
3695 ainfo->content_type =
3696 g_strdup("application/octet-stream");
3697 ainfo->encoding = ENC_BASE64;
3698 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text/", 5))
3700 procmime_get_encoding_for_text_file(file, &has_binary);
3702 ainfo->encoding = ENC_BASE64;
3703 name = g_path_get_basename(filename ? filename : file);
3704 ainfo->name = g_strdup(name);
3708 if (ainfo->name != NULL
3709 && !strcmp(ainfo->name, ".")) {
3710 g_free(ainfo->name);
3714 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3715 g_free(ainfo->content_type);
3716 ainfo->content_type = g_strdup("application/octet-stream");
3717 g_free(ainfo->charset);
3718 ainfo->charset = NULL;
3721 ainfo->size = (goffset)size;
3722 size_text = to_human_readable((goffset)size);
3724 store = GTK_LIST_STORE(gtk_tree_view_get_model
3725 (GTK_TREE_VIEW(compose->attach_clist)));
3727 gtk_list_store_append(store, &iter);
3728 gtk_list_store_set(store, &iter,
3729 COL_MIMETYPE, ainfo->content_type,
3730 COL_SIZE, size_text,
3731 COL_NAME, ainfo->name,
3732 COL_CHARSET, ainfo->charset,
3734 COL_AUTODATA, auto_ainfo,
3737 g_auto_pointer_free(auto_ainfo);
3738 compose_attach_update_label(compose);
3742 static void compose_use_signing(Compose *compose, gboolean use_signing)
3744 compose->use_signing = use_signing;
3745 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3748 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3750 compose->use_encryption = use_encryption;
3751 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3754 #define NEXT_PART_NOT_CHILD(info) \
3756 node = info->node; \
3757 while (node->children) \
3758 node = g_node_last_child(node); \
3759 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3762 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3766 MimeInfo *firsttext = NULL;
3767 MimeInfo *encrypted = NULL;
3770 const gchar *partname = NULL;
3772 mimeinfo = procmime_scan_message(msginfo);
3773 if (!mimeinfo) return;
3775 if (mimeinfo->node->children == NULL) {
3776 procmime_mimeinfo_free_all(mimeinfo);
3780 /* find first content part */
3781 child = (MimeInfo *) mimeinfo->node->children->data;
3782 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3783 child = (MimeInfo *)child->node->children->data;
3786 if (child->type == MIMETYPE_TEXT) {
3788 debug_print("First text part found\n");
3789 } else if (compose->mode == COMPOSE_REEDIT &&
3790 child->type == MIMETYPE_APPLICATION &&
3791 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3792 encrypted = (MimeInfo *)child->node->parent->data;
3795 child = (MimeInfo *) mimeinfo->node->children->data;
3796 while (child != NULL) {
3799 if (child == encrypted) {
3800 /* skip this part of tree */
3801 NEXT_PART_NOT_CHILD(child);
3805 if (child->type == MIMETYPE_MULTIPART) {
3806 /* get the actual content */
3807 child = procmime_mimeinfo_next(child);
3811 if (child == firsttext) {
3812 child = procmime_mimeinfo_next(child);
3816 outfile = procmime_get_tmp_file_name(child);
3817 if ((err = procmime_get_part(outfile, child)) < 0)
3818 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3820 gchar *content_type;
3822 content_type = procmime_get_content_type_str(child->type, child->subtype);
3824 /* if we meet a pgp signature, we don't attach it, but
3825 * we force signing. */
3826 if ((strcmp(content_type, "application/pgp-signature") &&
3827 strcmp(content_type, "application/pkcs7-signature") &&
3828 strcmp(content_type, "application/x-pkcs7-signature"))
3829 || compose->mode == COMPOSE_REDIRECT) {
3830 partname = procmime_mimeinfo_get_parameter(child, "filename");
3831 if (partname == NULL)
3832 partname = procmime_mimeinfo_get_parameter(child, "name");
3833 if (partname == NULL)
3835 compose_attach_append(compose, outfile,
3836 partname, content_type,
3837 procmime_mimeinfo_get_parameter(child, "charset"));
3839 compose_force_signing(compose, compose->account, NULL);
3841 g_free(content_type);
3844 NEXT_PART_NOT_CHILD(child);
3846 procmime_mimeinfo_free_all(mimeinfo);
3849 #undef NEXT_PART_NOT_CHILD
3854 WAIT_FOR_INDENT_CHAR,
3855 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3858 /* return indent length, we allow:
3859 indent characters followed by indent characters or spaces/tabs,
3860 alphabets and numbers immediately followed by indent characters,
3861 and the repeating sequences of the above
3862 If quote ends with multiple spaces, only the first one is included. */
3863 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3864 const GtkTextIter *start, gint *len)
3866 GtkTextIter iter = *start;
3870 IndentState state = WAIT_FOR_INDENT_CHAR;
3873 gint alnum_count = 0;
3874 gint space_count = 0;
3877 if (prefs_common.quote_chars == NULL) {
3881 while (!gtk_text_iter_ends_line(&iter)) {
3882 wc = gtk_text_iter_get_char(&iter);
3883 if (g_unichar_iswide(wc))
3885 clen = g_unichar_to_utf8(wc, ch);
3889 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3890 is_space = g_unichar_isspace(wc);
3892 if (state == WAIT_FOR_INDENT_CHAR) {
3893 if (!is_indent && !g_unichar_isalnum(wc))
3896 quote_len += alnum_count + space_count + 1;
3897 alnum_count = space_count = 0;
3898 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3901 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3902 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3906 else if (is_indent) {
3907 quote_len += alnum_count + space_count + 1;
3908 alnum_count = space_count = 0;
3911 state = WAIT_FOR_INDENT_CHAR;
3915 gtk_text_iter_forward_char(&iter);
3918 if (quote_len > 0 && space_count > 0)
3924 if (quote_len > 0) {
3926 gtk_text_iter_forward_chars(&iter, quote_len);
3927 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3933 /* return >0 if the line is itemized */
3934 static int compose_itemized_length(GtkTextBuffer *buffer,
3935 const GtkTextIter *start)
3937 GtkTextIter iter = *start;
3942 if (gtk_text_iter_ends_line(&iter))
3947 wc = gtk_text_iter_get_char(&iter);
3948 if (!g_unichar_isspace(wc))
3950 gtk_text_iter_forward_char(&iter);
3951 if (gtk_text_iter_ends_line(&iter))
3955 clen = g_unichar_to_utf8(wc, ch);
3959 if (!strchr("*-+", ch[0]))
3962 gtk_text_iter_forward_char(&iter);
3963 if (gtk_text_iter_ends_line(&iter))
3965 wc = gtk_text_iter_get_char(&iter);
3966 if (g_unichar_isspace(wc)) {
3972 /* return the string at the start of the itemization */
3973 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3974 const GtkTextIter *start)
3976 GtkTextIter iter = *start;
3979 GString *item_chars = g_string_new("");
3982 if (gtk_text_iter_ends_line(&iter))
3987 wc = gtk_text_iter_get_char(&iter);
3988 if (!g_unichar_isspace(wc))
3990 gtk_text_iter_forward_char(&iter);
3991 if (gtk_text_iter_ends_line(&iter))
3993 g_string_append_unichar(item_chars, wc);
3996 str = item_chars->str;
3997 g_string_free(item_chars, FALSE);
4001 /* return the number of spaces at a line's start */
4002 static int compose_left_offset_length(GtkTextBuffer *buffer,
4003 const GtkTextIter *start)
4005 GtkTextIter iter = *start;
4008 if (gtk_text_iter_ends_line(&iter))
4012 wc = gtk_text_iter_get_char(&iter);
4013 if (!g_unichar_isspace(wc))
4016 gtk_text_iter_forward_char(&iter);
4017 if (gtk_text_iter_ends_line(&iter))
4021 gtk_text_iter_forward_char(&iter);
4022 if (gtk_text_iter_ends_line(&iter))
4027 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
4028 const GtkTextIter *start,
4029 GtkTextIter *break_pos,
4033 GtkTextIter iter = *start, line_end = *start;
4034 PangoLogAttr *attrs;
4041 gboolean can_break = FALSE;
4042 gboolean do_break = FALSE;
4043 gboolean was_white = FALSE;
4044 gboolean prev_dont_break = FALSE;
4046 gtk_text_iter_forward_to_line_end(&line_end);
4047 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
4048 len = g_utf8_strlen(str, -1);
4052 g_warning("compose_get_line_break_pos: len = 0!\n");
4056 /* g_print("breaking line: %d: %s (len = %d)\n",
4057 gtk_text_iter_get_line(&iter), str, len); */
4059 attrs = g_new(PangoLogAttr, len + 1);
4061 pango_default_break(str, -1, NULL, attrs, len + 1);
4065 /* skip quote and leading spaces */
4066 for (i = 0; *p != '\0' && i < len; i++) {
4069 wc = g_utf8_get_char(p);
4070 if (i >= quote_len && !g_unichar_isspace(wc))
4072 if (g_unichar_iswide(wc))
4074 else if (*p == '\t')
4078 p = g_utf8_next_char(p);
4081 for (; *p != '\0' && i < len; i++) {
4082 PangoLogAttr *attr = attrs + i;
4086 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
4089 was_white = attr->is_white;
4091 /* don't wrap URI */
4092 if ((uri_len = get_uri_len(p)) > 0) {
4094 if (pos > 0 && col > max_col) {
4104 wc = g_utf8_get_char(p);
4105 if (g_unichar_iswide(wc)) {
4107 if (prev_dont_break && can_break && attr->is_line_break)
4109 } else if (*p == '\t')
4113 if (pos > 0 && col > max_col) {
4118 if (*p == '-' || *p == '/')
4119 prev_dont_break = TRUE;
4121 prev_dont_break = FALSE;
4123 p = g_utf8_next_char(p);
4127 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4132 *break_pos = *start;
4133 gtk_text_iter_set_line_offset(break_pos, pos);
4138 static gboolean compose_join_next_line(Compose *compose,
4139 GtkTextBuffer *buffer,
4141 const gchar *quote_str)
4143 GtkTextIter iter_ = *iter, cur, prev, next, end;
4144 PangoLogAttr attrs[3];
4146 gchar *next_quote_str;
4149 gboolean keep_cursor = FALSE;
4151 if (!gtk_text_iter_forward_line(&iter_) ||
4152 gtk_text_iter_ends_line(&iter_)) {
4155 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4157 if ((quote_str || next_quote_str) &&
4158 strcmp2(quote_str, next_quote_str) != 0) {
4159 g_free(next_quote_str);
4162 g_free(next_quote_str);
4165 if (quote_len > 0) {
4166 gtk_text_iter_forward_chars(&end, quote_len);
4167 if (gtk_text_iter_ends_line(&end)) {
4172 /* don't join itemized lines */
4173 if (compose_itemized_length(buffer, &end) > 0) {
4177 /* don't join signature separator */
4178 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4181 /* delete quote str */
4183 gtk_text_buffer_delete(buffer, &iter_, &end);
4185 /* don't join line breaks put by the user */
4187 gtk_text_iter_backward_char(&cur);
4188 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4189 gtk_text_iter_forward_char(&cur);
4193 gtk_text_iter_forward_char(&cur);
4194 /* delete linebreak and extra spaces */
4195 while (gtk_text_iter_backward_char(&cur)) {
4196 wc1 = gtk_text_iter_get_char(&cur);
4197 if (!g_unichar_isspace(wc1))
4202 while (!gtk_text_iter_ends_line(&cur)) {
4203 wc1 = gtk_text_iter_get_char(&cur);
4204 if (!g_unichar_isspace(wc1))
4206 gtk_text_iter_forward_char(&cur);
4209 if (!gtk_text_iter_equal(&prev, &next)) {
4212 mark = gtk_text_buffer_get_insert(buffer);
4213 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4214 if (gtk_text_iter_equal(&prev, &cur))
4216 gtk_text_buffer_delete(buffer, &prev, &next);
4220 /* insert space if required */
4221 gtk_text_iter_backward_char(&prev);
4222 wc1 = gtk_text_iter_get_char(&prev);
4223 wc2 = gtk_text_iter_get_char(&next);
4224 gtk_text_iter_forward_char(&next);
4225 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4226 pango_default_break(str, -1, NULL, attrs, 3);
4227 if (!attrs[1].is_line_break ||
4228 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4229 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4231 gtk_text_iter_backward_char(&iter_);
4232 gtk_text_buffer_place_cursor(buffer, &iter_);
4241 #define ADD_TXT_POS(bp_, ep_, pti_) \
4242 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4243 last = last->next; \
4244 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4245 last->next = NULL; \
4247 g_warning("alloc error scanning URIs\n"); \
4250 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4252 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4253 GtkTextBuffer *buffer;
4254 GtkTextIter iter, break_pos, end_of_line;
4255 gchar *quote_str = NULL;
4257 gboolean wrap_quote = prefs_common.linewrap_quote;
4258 gboolean prev_autowrap = compose->autowrap;
4259 gint startq_offset = -1, noq_offset = -1;
4260 gint uri_start = -1, uri_stop = -1;
4261 gint nouri_start = -1, nouri_stop = -1;
4262 gint num_blocks = 0;
4263 gint quotelevel = -1;
4264 gboolean modified = force;
4265 gboolean removed = FALSE;
4266 gboolean modified_before_remove = FALSE;
4268 gboolean start = TRUE;
4269 gint itemized_len = 0, rem_item_len = 0;
4270 gchar *itemized_chars = NULL;
4271 gboolean item_continuation = FALSE;
4276 if (compose->draft_timeout_tag == -2) {
4280 compose->autowrap = FALSE;
4282 buffer = gtk_text_view_get_buffer(text);
4283 undo_wrapping(compose->undostruct, TRUE);
4288 mark = gtk_text_buffer_get_insert(buffer);
4289 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4293 if (compose->draft_timeout_tag == -2) {
4294 if (gtk_text_iter_ends_line(&iter)) {
4295 while (gtk_text_iter_ends_line(&iter) &&
4296 gtk_text_iter_forward_line(&iter))
4299 while (gtk_text_iter_backward_line(&iter)) {
4300 if (gtk_text_iter_ends_line(&iter)) {
4301 gtk_text_iter_forward_line(&iter);
4307 /* move to line start */
4308 gtk_text_iter_set_line_offset(&iter, 0);
4311 itemized_len = compose_itemized_length(buffer, &iter);
4313 if (!itemized_len) {
4314 itemized_len = compose_left_offset_length(buffer, &iter);
4315 item_continuation = TRUE;
4319 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4321 /* go until paragraph end (empty line) */
4322 while (start || !gtk_text_iter_ends_line(&iter)) {
4323 gchar *scanpos = NULL;
4324 /* parse table - in order of priority */
4326 const gchar *needle; /* token */
4328 /* token search function */
4329 gchar *(*search) (const gchar *haystack,
4330 const gchar *needle);
4331 /* part parsing function */
4332 gboolean (*parse) (const gchar *start,
4333 const gchar *scanpos,
4337 /* part to URI function */
4338 gchar *(*build_uri) (const gchar *bp,
4342 static struct table parser[] = {
4343 {"http://", strcasestr, get_uri_part, make_uri_string},
4344 {"https://", strcasestr, get_uri_part, make_uri_string},
4345 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4346 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4347 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4348 {"www.", strcasestr, get_uri_part, make_http_string},
4349 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4350 {"@", strcasestr, get_email_part, make_email_string}
4352 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4353 gint last_index = PARSE_ELEMS;
4355 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4359 if (!prev_autowrap && num_blocks == 0) {
4361 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4362 G_CALLBACK(text_inserted),
4365 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4368 uri_start = uri_stop = -1;
4370 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4373 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4374 if (startq_offset == -1)
4375 startq_offset = gtk_text_iter_get_offset(&iter);
4376 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4377 if (quotelevel > 2) {
4378 /* recycle colors */
4379 if (prefs_common.recycle_quote_colors)
4388 if (startq_offset == -1)
4389 noq_offset = gtk_text_iter_get_offset(&iter);
4393 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4396 if (gtk_text_iter_ends_line(&iter)) {
4398 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4399 prefs_common.linewrap_len,
4401 GtkTextIter prev, next, cur;
4402 if (prev_autowrap != FALSE || force) {
4403 compose->automatic_break = TRUE;
4405 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4406 compose->automatic_break = FALSE;
4407 if (itemized_len && compose->autoindent) {
4408 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4409 if (!item_continuation)
4410 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4412 } else if (quote_str && wrap_quote) {
4413 compose->automatic_break = TRUE;
4415 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4416 compose->automatic_break = FALSE;
4417 if (itemized_len && compose->autoindent) {
4418 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4419 if (!item_continuation)
4420 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4424 /* remove trailing spaces */
4426 rem_item_len = itemized_len;
4427 while (compose->autoindent && rem_item_len-- > 0)
4428 gtk_text_iter_backward_char(&cur);
4429 gtk_text_iter_backward_char(&cur);
4432 while (!gtk_text_iter_starts_line(&cur)) {
4435 gtk_text_iter_backward_char(&cur);
4436 wc = gtk_text_iter_get_char(&cur);
4437 if (!g_unichar_isspace(wc))
4441 if (!gtk_text_iter_equal(&prev, &next)) {
4442 gtk_text_buffer_delete(buffer, &prev, &next);
4444 gtk_text_iter_forward_char(&break_pos);
4448 gtk_text_buffer_insert(buffer, &break_pos,
4452 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4454 /* move iter to current line start */
4455 gtk_text_iter_set_line_offset(&iter, 0);
4462 /* move iter to next line start */
4468 if (!prev_autowrap && num_blocks > 0) {
4470 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4471 G_CALLBACK(text_inserted),
4475 while (!gtk_text_iter_ends_line(&end_of_line)) {
4476 gtk_text_iter_forward_char(&end_of_line);
4478 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4480 nouri_start = gtk_text_iter_get_offset(&iter);
4481 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4483 walk_pos = gtk_text_iter_get_offset(&iter);
4484 /* FIXME: this looks phony. scanning for anything in the parse table */
4485 for (n = 0; n < PARSE_ELEMS; n++) {
4488 tmp = parser[n].search(walk, parser[n].needle);
4490 if (scanpos == NULL || tmp < scanpos) {
4499 /* check if URI can be parsed */
4500 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4501 (const gchar **)&ep, FALSE)
4502 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4506 strlen(parser[last_index].needle);
4509 uri_start = walk_pos + (bp - o_walk);
4510 uri_stop = walk_pos + (ep - o_walk);
4514 gtk_text_iter_forward_line(&iter);
4517 if (startq_offset != -1) {
4518 GtkTextIter startquote, endquote;
4519 gtk_text_buffer_get_iter_at_offset(
4520 buffer, &startquote, startq_offset);
4523 switch (quotelevel) {
4525 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4526 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4527 gtk_text_buffer_apply_tag_by_name(
4528 buffer, "quote0", &startquote, &endquote);
4529 gtk_text_buffer_remove_tag_by_name(
4530 buffer, "quote1", &startquote, &endquote);
4531 gtk_text_buffer_remove_tag_by_name(
4532 buffer, "quote2", &startquote, &endquote);
4537 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4538 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4539 gtk_text_buffer_apply_tag_by_name(
4540 buffer, "quote1", &startquote, &endquote);
4541 gtk_text_buffer_remove_tag_by_name(
4542 buffer, "quote0", &startquote, &endquote);
4543 gtk_text_buffer_remove_tag_by_name(
4544 buffer, "quote2", &startquote, &endquote);
4549 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4550 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4551 gtk_text_buffer_apply_tag_by_name(
4552 buffer, "quote2", &startquote, &endquote);
4553 gtk_text_buffer_remove_tag_by_name(
4554 buffer, "quote0", &startquote, &endquote);
4555 gtk_text_buffer_remove_tag_by_name(
4556 buffer, "quote1", &startquote, &endquote);
4562 } else if (noq_offset != -1) {
4563 GtkTextIter startnoquote, endnoquote;
4564 gtk_text_buffer_get_iter_at_offset(
4565 buffer, &startnoquote, noq_offset);
4568 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4569 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4570 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4571 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4572 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4573 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4574 gtk_text_buffer_remove_tag_by_name(
4575 buffer, "quote0", &startnoquote, &endnoquote);
4576 gtk_text_buffer_remove_tag_by_name(
4577 buffer, "quote1", &startnoquote, &endnoquote);
4578 gtk_text_buffer_remove_tag_by_name(
4579 buffer, "quote2", &startnoquote, &endnoquote);
4585 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4586 GtkTextIter nouri_start_iter, nouri_end_iter;
4587 gtk_text_buffer_get_iter_at_offset(
4588 buffer, &nouri_start_iter, nouri_start);
4589 gtk_text_buffer_get_iter_at_offset(
4590 buffer, &nouri_end_iter, nouri_stop);
4591 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4592 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4593 gtk_text_buffer_remove_tag_by_name(
4594 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4595 modified_before_remove = modified;
4600 if (uri_start >= 0 && uri_stop > 0) {
4601 GtkTextIter uri_start_iter, uri_end_iter, back;
4602 gtk_text_buffer_get_iter_at_offset(
4603 buffer, &uri_start_iter, uri_start);
4604 gtk_text_buffer_get_iter_at_offset(
4605 buffer, &uri_end_iter, uri_stop);
4606 back = uri_end_iter;
4607 gtk_text_iter_backward_char(&back);
4608 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4609 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4610 gtk_text_buffer_apply_tag_by_name(
4611 buffer, "link", &uri_start_iter, &uri_end_iter);
4613 if (removed && !modified_before_remove) {
4619 // debug_print("not modified, out after %d lines\n", lines);
4623 // debug_print("modified, out after %d lines\n", lines);
4625 g_free(itemized_chars);
4628 undo_wrapping(compose->undostruct, FALSE);
4629 compose->autowrap = prev_autowrap;
4634 void compose_action_cb(void *data)
4636 Compose *compose = (Compose *)data;
4637 compose_wrap_all(compose);
4640 static void compose_wrap_all(Compose *compose)
4642 compose_wrap_all_full(compose, FALSE);
4645 static void compose_wrap_all_full(Compose *compose, gboolean force)
4647 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4648 GtkTextBuffer *buffer;
4650 gboolean modified = TRUE;
4652 buffer = gtk_text_view_get_buffer(text);
4654 gtk_text_buffer_get_start_iter(buffer, &iter);
4655 while (!gtk_text_iter_is_end(&iter) && modified)
4656 modified = compose_beautify_paragraph(compose, &iter, force);
4660 static void compose_set_title(Compose *compose)
4666 edited = compose->modified ? _(" [Edited]") : "";
4668 subject = gtk_editable_get_chars(
4669 GTK_EDITABLE(compose->subject_entry), 0, -1);
4671 #ifndef GENERIC_UMPC
4672 if (subject && strlen(subject))
4673 str = g_strdup_printf(_("%s - Compose message%s"),
4676 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4678 str = g_strdup(_("Compose message"));
4681 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4687 * compose_current_mail_account:
4689 * Find a current mail account (the currently selected account, or the
4690 * default account, if a news account is currently selected). If a
4691 * mail account cannot be found, display an error message.
4693 * Return value: Mail account, or NULL if not found.
4695 static PrefsAccount *
4696 compose_current_mail_account(void)
4700 if (cur_account && cur_account->protocol != A_NNTP)
4703 ac = account_get_default();
4704 if (!ac || ac->protocol == A_NNTP) {
4705 alertpanel_error(_("Account for sending mail is not specified.\n"
4706 "Please select a mail account before sending."));
4713 #define QUOTE_IF_REQUIRED(out, str) \
4715 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4719 len = strlen(str) + 3; \
4720 if ((__tmp = alloca(len)) == NULL) { \
4721 g_warning("can't allocate memory\n"); \
4722 g_string_free(header, TRUE); \
4725 g_snprintf(__tmp, len, "\"%s\"", str); \
4730 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4731 g_warning("can't allocate memory\n"); \
4732 g_string_free(header, TRUE); \
4735 strcpy(__tmp, str); \
4741 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4743 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4747 len = strlen(str) + 3; \
4748 if ((__tmp = alloca(len)) == NULL) { \
4749 g_warning("can't allocate memory\n"); \
4752 g_snprintf(__tmp, len, "\"%s\"", str); \
4757 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4758 g_warning("can't allocate memory\n"); \
4761 strcpy(__tmp, str); \
4767 static void compose_select_account(Compose *compose, PrefsAccount *account,
4770 gchar *from = NULL, *header;
4771 ComposeHeaderEntry *header_entry;
4773 cm_return_if_fail(account != NULL);
4775 compose->account = account;
4776 if (account->name && *account->name) {
4778 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4779 from = g_strdup_printf("%s <%s>",
4780 buf, account->address);
4781 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4783 from = g_strdup_printf("<%s>",
4785 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4790 compose_set_title(compose);
4792 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4793 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4795 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4796 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4797 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4799 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4801 activate_privacy_system(compose, account, FALSE);
4803 if (!init && compose->mode != COMPOSE_REDIRECT) {
4804 undo_block(compose->undostruct);
4805 compose_insert_sig(compose, TRUE);
4806 undo_unblock(compose->undostruct);
4809 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4810 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4812 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4813 if (account->protocol == A_NNTP) {
4814 if (!strcmp(header, _("To:")))
4815 combobox_select_by_text(
4816 GTK_COMBO_BOX(header_entry->combo),
4819 if (!strcmp(header, _("Newsgroups:")))
4820 combobox_select_by_text(
4821 GTK_COMBO_BOX(header_entry->combo),
4829 /* use account's dict info if set */
4830 if (compose->gtkaspell) {
4831 if (account->enable_default_dictionary)
4832 gtkaspell_change_dict(compose->gtkaspell,
4833 account->default_dictionary, FALSE);
4834 if (account->enable_default_alt_dictionary)
4835 gtkaspell_change_alt_dict(compose->gtkaspell,
4836 account->default_alt_dictionary);
4837 if (account->enable_default_dictionary
4838 || account->enable_default_alt_dictionary)
4839 compose_spell_menu_changed(compose);
4844 gboolean compose_check_for_valid_recipient(Compose *compose) {
4845 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4846 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4847 gboolean recipient_found = FALSE;
4851 /* free to and newsgroup list */
4852 slist_free_strings(compose->to_list);
4853 g_slist_free(compose->to_list);
4854 compose->to_list = NULL;
4856 slist_free_strings(compose->newsgroup_list);
4857 g_slist_free(compose->newsgroup_list);
4858 compose->newsgroup_list = NULL;
4860 /* search header entries for to and newsgroup entries */
4861 for (list = compose->header_list; list; list = list->next) {
4864 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4865 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4868 if (entry[0] != '\0') {
4869 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4870 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4871 compose->to_list = address_list_append(compose->to_list, entry);
4872 recipient_found = TRUE;
4875 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4876 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4877 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4878 recipient_found = TRUE;
4885 return recipient_found;
4888 static gboolean compose_check_for_set_recipients(Compose *compose)
4890 if (compose->account->set_autocc && compose->account->auto_cc) {
4891 gboolean found_other = FALSE;
4893 /* search header entries for to and newsgroup entries */
4894 for (list = compose->header_list; list; list = list->next) {
4897 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4898 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4901 if (strcmp(entry, compose->account->auto_cc)
4902 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4912 if (compose->batch) {
4913 gtk_widget_show_all(compose->window);
4915 aval = alertpanel(_("Send"),
4916 _("The only recipient is the default CC address. Send anyway?"),
4917 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4918 if (aval != G_ALERTALTERNATE)
4922 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4923 gboolean found_other = FALSE;
4925 /* search header entries for to and newsgroup entries */
4926 for (list = compose->header_list; list; list = list->next) {
4929 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4930 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4933 if (strcmp(entry, compose->account->auto_bcc)
4934 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4944 if (compose->batch) {
4945 gtk_widget_show_all(compose->window);
4947 aval = alertpanel(_("Send"),
4948 _("The only recipient is the default BCC address. Send anyway?"),
4949 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4950 if (aval != G_ALERTALTERNATE)
4957 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4961 if (compose_check_for_valid_recipient(compose) == FALSE) {
4962 if (compose->batch) {
4963 gtk_widget_show_all(compose->window);
4965 alertpanel_error(_("Recipient is not specified."));
4969 if (compose_check_for_set_recipients(compose) == FALSE) {
4973 if (!compose->batch) {
4974 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4975 if (*str == '\0' && check_everything == TRUE &&
4976 compose->mode != COMPOSE_REDIRECT) {
4978 gchar *button_label;
4981 if (compose->sending)
4982 button_label = _("+_Send");
4984 button_label = _("+_Queue");
4985 message = g_strdup_printf(_("Subject is empty. %s"),
4986 compose->sending?_("Send it anyway?"):
4987 _("Queue it anyway?"));
4989 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4990 GTK_STOCK_CANCEL, button_label, NULL);
4992 if (aval != G_ALERTALTERNATE)
4997 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
5003 gint compose_send(Compose *compose)
5006 FolderItem *folder = NULL;
5008 gchar *msgpath = NULL;
5009 gboolean discard_window = FALSE;
5010 gchar *errstr = NULL;
5011 gchar *tmsgid = NULL;
5012 MainWindow *mainwin = mainwindow_get_mainwindow();
5013 gboolean queued_removed = FALSE;
5015 if (prefs_common.send_dialog_invisible
5016 || compose->batch == TRUE)
5017 discard_window = TRUE;
5019 compose_allow_user_actions (compose, FALSE);
5020 compose->sending = TRUE;
5022 if (compose_check_entries(compose, TRUE) == FALSE) {
5023 if (compose->batch) {
5024 gtk_widget_show_all(compose->window);
5030 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
5033 if (compose->batch) {
5034 gtk_widget_show_all(compose->window);
5037 alertpanel_error(_("Could not queue message for sending:\n\n"
5038 "Charset conversion failed."));
5039 } else if (val == -5) {
5040 alertpanel_error(_("Could not queue message for sending:\n\n"
5041 "Couldn't get recipient encryption key."));
5042 } else if (val == -6) {
5044 } else if (val == -3) {
5045 if (privacy_peek_error())
5046 alertpanel_error(_("Could not queue message for sending:\n\n"
5047 "Signature failed: %s"), privacy_get_error());
5048 } else if (val == -2 && errno != 0) {
5049 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
5051 alertpanel_error(_("Could not queue message for sending."));
5056 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
5057 if (discard_window) {
5058 compose->sending = FALSE;
5059 compose_close(compose);
5060 /* No more compose access in the normal codepath
5061 * after this point! */
5066 alertpanel_error(_("The message was queued but could not be "
5067 "sent.\nUse \"Send queued messages\" from "
5068 "the main window to retry."));
5069 if (!discard_window) {
5076 if (msgpath == NULL) {
5077 msgpath = folder_item_fetch_msg(folder, msgnum);
5078 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5081 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5082 claws_unlink(msgpath);
5085 if (!discard_window) {
5087 if (!queued_removed)
5088 folder_item_remove_msg(folder, msgnum);
5089 folder_item_scan(folder);
5091 /* make sure we delete that */
5092 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5094 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5095 folder_item_remove_msg(folder, tmp->msgnum);
5096 procmsg_msginfo_free(tmp);
5103 if (!queued_removed)
5104 folder_item_remove_msg(folder, msgnum);
5105 folder_item_scan(folder);
5107 /* make sure we delete that */
5108 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5110 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5111 folder_item_remove_msg(folder, tmp->msgnum);
5112 procmsg_msginfo_free(tmp);
5115 if (!discard_window) {
5116 compose->sending = FALSE;
5117 compose_allow_user_actions (compose, TRUE);
5118 compose_close(compose);
5122 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5123 "the main window to retry."), errstr);
5126 alertpanel_error_log(_("The message was queued but could not be "
5127 "sent.\nUse \"Send queued messages\" from "
5128 "the main window to retry."));
5130 if (!discard_window) {
5139 toolbar_main_set_sensitive(mainwin);
5140 main_window_set_menu_sensitive(mainwin);
5146 compose_allow_user_actions (compose, TRUE);
5147 compose->sending = FALSE;
5148 compose->modified = TRUE;
5149 toolbar_main_set_sensitive(mainwin);
5150 main_window_set_menu_sensitive(mainwin);
5155 static gboolean compose_use_attach(Compose *compose)
5157 GtkTreeModel *model = gtk_tree_view_get_model
5158 (GTK_TREE_VIEW(compose->attach_clist));
5159 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5162 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5165 gchar buf[BUFFSIZE];
5167 gboolean first_to_address;
5168 gboolean first_cc_address;
5170 ComposeHeaderEntry *headerentry;
5171 const gchar *headerentryname;
5172 const gchar *cc_hdr;
5173 const gchar *to_hdr;
5174 gboolean err = FALSE;
5176 debug_print("Writing redirect header\n");
5178 cc_hdr = prefs_common_translated_header_name("Cc:");
5179 to_hdr = prefs_common_translated_header_name("To:");
5181 first_to_address = TRUE;
5182 for (list = compose->header_list; list; list = list->next) {
5183 headerentry = ((ComposeHeaderEntry *)list->data);
5184 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5186 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5187 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5188 Xstrdup_a(str, entstr, return -1);
5190 if (str[0] != '\0') {
5191 compose_convert_header
5192 (compose, buf, sizeof(buf), str,
5193 strlen("Resent-To") + 2, TRUE);
5195 if (first_to_address) {
5196 err |= (fprintf(fp, "Resent-To: ") < 0);
5197 first_to_address = FALSE;
5199 err |= (fprintf(fp, ",") < 0);
5201 err |= (fprintf(fp, "%s", buf) < 0);
5205 if (!first_to_address) {
5206 err |= (fprintf(fp, "\n") < 0);
5209 first_cc_address = TRUE;
5210 for (list = compose->header_list; list; list = list->next) {
5211 headerentry = ((ComposeHeaderEntry *)list->data);
5212 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5214 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5215 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5216 Xstrdup_a(str, strg, return -1);
5218 if (str[0] != '\0') {
5219 compose_convert_header
5220 (compose, buf, sizeof(buf), str,
5221 strlen("Resent-Cc") + 2, TRUE);
5223 if (first_cc_address) {
5224 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5225 first_cc_address = FALSE;
5227 err |= (fprintf(fp, ",") < 0);
5229 err |= (fprintf(fp, "%s", buf) < 0);
5233 if (!first_cc_address) {
5234 err |= (fprintf(fp, "\n") < 0);
5237 return (err ? -1:0);
5240 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5242 gchar buf[BUFFSIZE];
5244 const gchar *entstr;
5245 /* struct utsname utsbuf; */
5246 gboolean err = FALSE;
5248 cm_return_val_if_fail(fp != NULL, -1);
5249 cm_return_val_if_fail(compose->account != NULL, -1);
5250 cm_return_val_if_fail(compose->account->address != NULL, -1);
5253 get_rfc822_date(buf, sizeof(buf));
5254 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5257 if (compose->account->name && *compose->account->name) {
5258 compose_convert_header
5259 (compose, buf, sizeof(buf), compose->account->name,
5260 strlen("From: "), TRUE);
5261 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5262 buf, compose->account->address) < 0);
5264 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5267 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5268 if (*entstr != '\0') {
5269 Xstrdup_a(str, entstr, return -1);
5272 compose_convert_header(compose, buf, sizeof(buf), str,
5273 strlen("Subject: "), FALSE);
5274 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5278 /* Resent-Message-ID */
5279 if (compose->account->set_domain && compose->account->domain) {
5280 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5281 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5282 g_snprintf(buf, sizeof(buf), "%s",
5283 strchr(compose->account->address, '@') ?
5284 strchr(compose->account->address, '@')+1 :
5285 compose->account->address);
5287 g_snprintf(buf, sizeof(buf), "%s", "");
5290 if (compose->account->gen_msgid) {
5292 if (compose->account->msgid_with_addr) {
5293 addr = compose->account->address;
5295 generate_msgid(buf, sizeof(buf), addr);
5296 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5297 compose->msgid = g_strdup(buf);
5299 compose->msgid = NULL;
5302 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5305 /* separator between header and body */
5306 err |= (fputs("\n", fp) == EOF);
5308 return (err ? -1:0);
5311 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5315 gchar buf[BUFFSIZE];
5317 gboolean skip = FALSE;
5318 gboolean err = FALSE;
5319 gchar *not_included[]={
5320 "Return-Path:", "Delivered-To:", "Received:",
5321 "Subject:", "X-UIDL:", "AF:",
5322 "NF:", "PS:", "SRH:",
5323 "SFN:", "DSR:", "MID:",
5324 "CFG:", "PT:", "S:",
5325 "RQ:", "SSV:", "NSV:",
5326 "SSH:", "R:", "MAID:",
5327 "NAID:", "RMID:", "FMID:",
5328 "SCF:", "RRCPT:", "NG:",
5329 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5330 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5331 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5332 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5333 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5336 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5337 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5341 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5343 for (i = 0; not_included[i] != NULL; i++) {
5344 if (g_ascii_strncasecmp(buf, not_included[i],
5345 strlen(not_included[i])) == 0) {
5352 if (fputs(buf, fdest) == -1)
5355 if (!prefs_common.redirect_keep_from) {
5356 if (g_ascii_strncasecmp(buf, "From:",
5357 strlen("From:")) == 0) {
5358 err |= (fputs(" (by way of ", fdest) == EOF);
5359 if (compose->account->name
5360 && *compose->account->name) {
5361 compose_convert_header
5362 (compose, buf, sizeof(buf),
5363 compose->account->name,
5366 err |= (fprintf(fdest, "%s <%s>",
5368 compose->account->address) < 0);
5370 err |= (fprintf(fdest, "%s",
5371 compose->account->address) < 0);
5372 err |= (fputs(")", fdest) == EOF);
5376 if (fputs("\n", fdest) == -1)
5383 if (compose_redirect_write_headers(compose, fdest))
5386 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5387 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5400 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5402 GtkTextBuffer *buffer;
5403 GtkTextIter start, end;
5406 const gchar *out_codeset;
5407 EncodingType encoding = ENC_UNKNOWN;
5408 MimeInfo *mimemsg, *mimetext;
5410 const gchar *src_codeset = CS_INTERNAL;
5411 gchar *from_addr = NULL;
5412 gchar *from_name = NULL;
5414 if (action == COMPOSE_WRITE_FOR_SEND)
5415 attach_parts = TRUE;
5417 /* create message MimeInfo */
5418 mimemsg = procmime_mimeinfo_new();
5419 mimemsg->type = MIMETYPE_MESSAGE;
5420 mimemsg->subtype = g_strdup("rfc822");
5421 mimemsg->content = MIMECONTENT_MEM;
5422 mimemsg->tmp = TRUE; /* must free content later */
5423 mimemsg->data.mem = compose_get_header(compose);
5425 /* Create text part MimeInfo */
5426 /* get all composed text */
5427 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5428 gtk_text_buffer_get_start_iter(buffer, &start);
5429 gtk_text_buffer_get_end_iter(buffer, &end);
5430 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5432 out_codeset = conv_get_charset_str(compose->out_encoding);
5434 if (!out_codeset && is_ascii_str(chars)) {
5435 out_codeset = CS_US_ASCII;
5436 } else if (prefs_common.outgoing_fallback_to_ascii &&
5437 is_ascii_str(chars)) {
5438 out_codeset = CS_US_ASCII;
5439 encoding = ENC_7BIT;
5443 gchar *test_conv_global_out = NULL;
5444 gchar *test_conv_reply = NULL;
5446 /* automatic mode. be automatic. */
5447 codeconv_set_strict(TRUE);
5449 out_codeset = conv_get_outgoing_charset_str();
5451 debug_print("trying to convert to %s\n", out_codeset);
5452 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5455 if (!test_conv_global_out && compose->orig_charset
5456 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5457 out_codeset = compose->orig_charset;
5458 debug_print("failure; trying to convert to %s\n", out_codeset);
5459 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5462 if (!test_conv_global_out && !test_conv_reply) {
5464 out_codeset = CS_INTERNAL;
5465 debug_print("failure; finally using %s\n", out_codeset);
5467 g_free(test_conv_global_out);
5468 g_free(test_conv_reply);
5469 codeconv_set_strict(FALSE);
5472 if (encoding == ENC_UNKNOWN) {
5473 if (prefs_common.encoding_method == CTE_BASE64)
5474 encoding = ENC_BASE64;
5475 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5476 encoding = ENC_QUOTED_PRINTABLE;
5477 else if (prefs_common.encoding_method == CTE_8BIT)
5478 encoding = ENC_8BIT;
5480 encoding = procmime_get_encoding_for_charset(out_codeset);
5483 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5484 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5486 if (action == COMPOSE_WRITE_FOR_SEND) {
5487 codeconv_set_strict(TRUE);
5488 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5489 codeconv_set_strict(FALSE);
5495 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5496 "to the specified %s charset.\n"
5497 "Send it as %s?"), out_codeset, src_codeset);
5498 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5499 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5502 if (aval != G_ALERTALTERNATE) {
5507 out_codeset = src_codeset;
5513 out_codeset = src_codeset;
5518 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5519 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5520 strstr(buf, "\nFrom ") != NULL) {
5521 encoding = ENC_QUOTED_PRINTABLE;
5525 mimetext = procmime_mimeinfo_new();
5526 mimetext->content = MIMECONTENT_MEM;
5527 mimetext->tmp = TRUE; /* must free content later */
5528 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5529 * and free the data, which we need later. */
5530 mimetext->data.mem = g_strdup(buf);
5531 mimetext->type = MIMETYPE_TEXT;
5532 mimetext->subtype = g_strdup("plain");
5533 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5534 g_strdup(out_codeset));
5536 /* protect trailing spaces when signing message */
5537 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5538 privacy_system_can_sign(compose->privacy_system)) {
5539 encoding = ENC_QUOTED_PRINTABLE;
5542 debug_print("main text: %zd bytes encoded as %s in %d\n",
5543 strlen(buf), out_codeset, encoding);
5545 /* check for line length limit */
5546 if (action == COMPOSE_WRITE_FOR_SEND &&
5547 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5548 check_line_length(buf, 1000, &line) < 0) {
5552 msg = g_strdup_printf
5553 (_("Line %d exceeds the line length limit (998 bytes).\n"
5554 "The contents of the message might be broken on the way to the delivery.\n"
5556 "Send it anyway?"), line + 1);
5557 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5559 if (aval != G_ALERTALTERNATE) {
5565 if (encoding != ENC_UNKNOWN)
5566 procmime_encode_content(mimetext, encoding);
5568 /* append attachment parts */
5569 if (compose_use_attach(compose) && attach_parts) {
5570 MimeInfo *mimempart;
5571 gchar *boundary = NULL;
5572 mimempart = procmime_mimeinfo_new();
5573 mimempart->content = MIMECONTENT_EMPTY;
5574 mimempart->type = MIMETYPE_MULTIPART;
5575 mimempart->subtype = g_strdup("mixed");
5579 boundary = generate_mime_boundary(NULL);
5580 } while (strstr(buf, boundary) != NULL);
5582 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5585 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5587 g_node_append(mimempart->node, mimetext->node);
5588 g_node_append(mimemsg->node, mimempart->node);
5590 if (compose_add_attachments(compose, mimempart) < 0)
5593 g_node_append(mimemsg->node, mimetext->node);
5597 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5598 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5599 /* extract name and address */
5600 if (strstr(spec, " <") && strstr(spec, ">")) {
5601 from_addr = g_strdup(strrchr(spec, '<')+1);
5602 *(strrchr(from_addr, '>')) = '\0';
5603 from_name = g_strdup(spec);
5604 *(strrchr(from_name, '<')) = '\0';
5611 /* sign message if sending */
5612 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5613 privacy_system_can_sign(compose->privacy_system))
5614 if (!privacy_sign(compose->privacy_system, mimemsg,
5615 compose->account, from_addr)) {
5622 procmime_write_mimeinfo(mimemsg, fp);
5624 procmime_mimeinfo_free_all(mimemsg);
5629 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5631 GtkTextBuffer *buffer;
5632 GtkTextIter start, end;
5637 if ((fp = g_fopen(file, "wb")) == NULL) {
5638 FILE_OP_ERROR(file, "fopen");
5642 /* chmod for security */
5643 if (change_file_mode_rw(fp, file) < 0) {
5644 FILE_OP_ERROR(file, "chmod");
5645 g_warning("can't change file mode\n");
5648 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5649 gtk_text_buffer_get_start_iter(buffer, &start);
5650 gtk_text_buffer_get_end_iter(buffer, &end);
5651 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5653 chars = conv_codeset_strdup
5654 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5657 if (!chars) return -1;
5660 len = strlen(chars);
5661 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5662 FILE_OP_ERROR(file, "fwrite");
5671 if (fclose(fp) == EOF) {
5672 FILE_OP_ERROR(file, "fclose");
5679 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5682 MsgInfo *msginfo = compose->targetinfo;
5684 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5685 if (!msginfo) return -1;
5687 if (!force && MSG_IS_LOCKED(msginfo->flags))
5690 item = msginfo->folder;
5691 cm_return_val_if_fail(item != NULL, -1);
5693 if (procmsg_msg_exist(msginfo) &&
5694 (folder_has_parent_of_type(item, F_QUEUE) ||
5695 folder_has_parent_of_type(item, F_DRAFT)
5696 || msginfo == compose->autosaved_draft)) {
5697 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5698 g_warning("can't remove the old message\n");
5701 debug_print("removed reedit target %d\n", msginfo->msgnum);
5708 static void compose_remove_draft(Compose *compose)
5711 MsgInfo *msginfo = compose->targetinfo;
5712 drafts = account_get_special_folder(compose->account, F_DRAFT);
5714 if (procmsg_msg_exist(msginfo)) {
5715 folder_item_remove_msg(drafts, msginfo->msgnum);
5720 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5721 gboolean remove_reedit_target)
5723 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5726 static gboolean compose_warn_encryption(Compose *compose)
5728 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5729 AlertValue val = G_ALERTALTERNATE;
5731 if (warning == NULL)
5734 val = alertpanel_full(_("Encryption warning"), warning,
5735 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5736 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5737 if (val & G_ALERTDISABLE) {
5738 val &= ~G_ALERTDISABLE;
5739 if (val == G_ALERTALTERNATE)
5740 privacy_inhibit_encrypt_warning(compose->privacy_system,
5744 if (val == G_ALERTALTERNATE) {
5751 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5752 gchar **msgpath, gboolean check_subject,
5753 gboolean remove_reedit_target)
5760 static gboolean lock = FALSE;
5761 PrefsAccount *mailac = NULL, *newsac = NULL;
5762 gboolean err = FALSE;
5764 debug_print("queueing message...\n");
5765 cm_return_val_if_fail(compose->account != NULL, -1);
5769 if (compose_check_entries(compose, check_subject) == FALSE) {
5771 if (compose->batch) {
5772 gtk_widget_show_all(compose->window);
5777 if (!compose->to_list && !compose->newsgroup_list) {
5778 g_warning("can't get recipient list.");
5783 if (compose->to_list) {
5784 if (compose->account->protocol != A_NNTP)
5785 mailac = compose->account;
5786 else if (cur_account && cur_account->protocol != A_NNTP)
5787 mailac = cur_account;
5788 else if (!(mailac = compose_current_mail_account())) {
5790 alertpanel_error(_("No account for sending mails available!"));
5795 if (compose->newsgroup_list) {
5796 if (compose->account->protocol == A_NNTP)
5797 newsac = compose->account;
5800 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5805 /* write queue header */
5806 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5807 G_DIR_SEPARATOR, compose, (guint) rand());
5808 debug_print("queuing to %s\n", tmp);
5809 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5810 FILE_OP_ERROR(tmp, "fopen");
5816 if (change_file_mode_rw(fp, tmp) < 0) {
5817 FILE_OP_ERROR(tmp, "chmod");
5818 g_warning("can't change file mode\n");
5821 /* queueing variables */
5822 err |= (fprintf(fp, "AF:\n") < 0);
5823 err |= (fprintf(fp, "NF:0\n") < 0);
5824 err |= (fprintf(fp, "PS:10\n") < 0);
5825 err |= (fprintf(fp, "SRH:1\n") < 0);
5826 err |= (fprintf(fp, "SFN:\n") < 0);
5827 err |= (fprintf(fp, "DSR:\n") < 0);
5829 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5831 err |= (fprintf(fp, "MID:\n") < 0);
5832 err |= (fprintf(fp, "CFG:\n") < 0);
5833 err |= (fprintf(fp, "PT:0\n") < 0);
5834 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5835 err |= (fprintf(fp, "RQ:\n") < 0);
5837 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5839 err |= (fprintf(fp, "SSV:\n") < 0);
5841 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5843 err |= (fprintf(fp, "NSV:\n") < 0);
5844 err |= (fprintf(fp, "SSH:\n") < 0);
5845 /* write recepient list */
5846 if (compose->to_list) {
5847 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5848 for (cur = compose->to_list->next; cur != NULL;
5850 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5851 err |= (fprintf(fp, "\n") < 0);
5853 /* write newsgroup list */
5854 if (compose->newsgroup_list) {
5855 err |= (fprintf(fp, "NG:") < 0);
5856 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5857 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5858 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5859 err |= (fprintf(fp, "\n") < 0);
5861 /* Sylpheed account IDs */
5863 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5865 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5868 if (compose->privacy_system != NULL) {
5869 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5870 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5871 if (compose->use_encryption) {
5873 if (!compose_warn_encryption(compose)) {
5880 if (mailac && mailac->encrypt_to_self) {
5881 GSList *tmp_list = g_slist_copy(compose->to_list);
5882 tmp_list = g_slist_append(tmp_list, compose->account->address);
5883 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5884 g_slist_free(tmp_list);
5886 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5888 if (encdata != NULL) {
5889 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5890 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5891 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5893 } /* else we finally dont want to encrypt */
5895 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5896 /* and if encdata was null, it means there's been a problem in
5908 /* Save copy folder */
5909 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5910 gchar *savefolderid;
5912 savefolderid = compose_get_save_to(compose);
5913 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5914 g_free(savefolderid);
5916 /* Save copy folder */
5917 if (compose->return_receipt) {
5918 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5920 /* Message-ID of message replying to */
5921 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5924 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5925 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5928 /* Message-ID of message forwarding to */
5929 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5932 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5933 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5937 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5938 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5940 /* end of headers */
5941 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5943 if (compose->redirect_filename != NULL) {
5944 if (compose_redirect_write_to_file(compose, fp) < 0) {
5953 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5958 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5962 g_warning("failed to write queue message\n");
5969 if (fclose(fp) == EOF) {
5970 FILE_OP_ERROR(tmp, "fclose");
5977 if (item && *item) {
5980 queue = account_get_special_folder(compose->account, F_QUEUE);
5983 g_warning("can't find queue folder\n");
5989 folder_item_scan(queue);
5990 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5991 g_warning("can't queue the message\n");
5998 if (msgpath == NULL) {
6004 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
6005 compose_remove_reedit_target(compose, FALSE);
6008 if ((msgnum != NULL) && (item != NULL)) {
6016 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
6019 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
6021 struct stat statbuf;
6022 gchar *type, *subtype;
6023 GtkTreeModel *model;
6026 model = gtk_tree_view_get_model(tree_view);
6028 if (!gtk_tree_model_get_iter_first(model, &iter))
6031 gtk_tree_model_get(model, &iter,
6035 if (!is_file_exist(ainfo->file)) {
6036 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
6037 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
6038 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
6040 if (val == G_ALERTDEFAULT) {
6045 mimepart = procmime_mimeinfo_new();
6046 mimepart->content = MIMECONTENT_FILE;
6047 mimepart->data.filename = g_strdup(ainfo->file);
6048 mimepart->tmp = FALSE; /* or we destroy our attachment */
6049 mimepart->offset = 0;
6051 g_stat(ainfo->file, &statbuf);
6052 mimepart->length = statbuf.st_size;
6054 type = g_strdup(ainfo->content_type);
6056 if (!strchr(type, '/')) {
6058 type = g_strdup("application/octet-stream");
6061 subtype = strchr(type, '/') + 1;
6062 *(subtype - 1) = '\0';
6063 mimepart->type = procmime_get_media_type(type);
6064 mimepart->subtype = g_strdup(subtype);
6067 if (mimepart->type == MIMETYPE_MESSAGE &&
6068 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
6069 mimepart->disposition = DISPOSITIONTYPE_INLINE;
6070 } else if (mimepart->type == MIMETYPE_TEXT) {
6071 if (!ainfo->name && compose->mode == COMPOSE_FORWARD_INLINE) {
6072 /* Text parts with no name come from multipart/alternative
6073 * forwards. Make sure the recipient won't look at the
6074 * original HTML part by mistake. */
6075 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6076 ainfo->name = g_strdup_printf(_("Original %s part"),
6080 g_hash_table_insert(mimepart->typeparameters,
6081 g_strdup("charset"), g_strdup(ainfo->charset));
6083 if (ainfo->name && mimepart->type != MIMETYPE_MESSAGE) {
6084 if (mimepart->type == MIMETYPE_APPLICATION &&
6085 !strcmp2(mimepart->subtype, "octet-stream"))
6086 g_hash_table_insert(mimepart->typeparameters,
6087 g_strdup("name"), g_strdup(ainfo->name));
6088 g_hash_table_insert(mimepart->dispositionparameters,
6089 g_strdup("filename"), g_strdup(ainfo->name));
6090 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6093 if (mimepart->type == MIMETYPE_MESSAGE
6094 || mimepart->type == MIMETYPE_MULTIPART)
6095 ainfo->encoding = ENC_BINARY;
6096 else if (compose->use_signing) {
6097 if (ainfo->encoding == ENC_7BIT)
6098 ainfo->encoding = ENC_QUOTED_PRINTABLE;
6099 else if (ainfo->encoding == ENC_8BIT)
6100 ainfo->encoding = ENC_BASE64;
6105 procmime_encode_content(mimepart, ainfo->encoding);
6107 g_node_append(parent->node, mimepart->node);
6108 } while (gtk_tree_model_iter_next(model, &iter));
6113 #define IS_IN_CUSTOM_HEADER(header) \
6114 (compose->account->add_customhdr && \
6115 custom_header_find(compose->account->customhdr_list, header) != NULL)
6117 static void compose_add_headerfield_from_headerlist(Compose *compose,
6119 const gchar *fieldname,
6120 const gchar *seperator)
6122 gchar *str, *fieldname_w_colon;
6123 gboolean add_field = FALSE;
6125 ComposeHeaderEntry *headerentry;
6126 const gchar *headerentryname;
6127 const gchar *trans_fieldname;
6130 if (IS_IN_CUSTOM_HEADER(fieldname))
6133 debug_print("Adding %s-fields\n", fieldname);
6135 fieldstr = g_string_sized_new(64);
6137 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6138 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6140 for (list = compose->header_list; list; list = list->next) {
6141 headerentry = ((ComposeHeaderEntry *)list->data);
6142 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6144 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6145 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6147 if (str[0] != '\0') {
6149 g_string_append(fieldstr, seperator);
6150 g_string_append(fieldstr, str);
6159 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6160 compose_convert_header
6161 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6162 strlen(fieldname) + 2, TRUE);
6163 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6167 g_free(fieldname_w_colon);
6168 g_string_free(fieldstr, TRUE);
6173 static gchar *compose_get_manual_headers_info(Compose *compose)
6175 GString *sh_header = g_string_new(" ");
6177 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6179 for (list = compose->header_list; list; list = list->next) {
6180 ComposeHeaderEntry *headerentry;
6183 gchar *headername_wcolon;
6184 const gchar *headername_trans;
6186 gboolean standard_header = FALSE;
6188 headerentry = ((ComposeHeaderEntry *)list->data);
6190 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6192 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6197 if (!strstr(tmp, ":")) {
6198 headername_wcolon = g_strconcat(tmp, ":", NULL);
6199 headername = g_strdup(tmp);
6201 headername_wcolon = g_strdup(tmp);
6202 headername = g_strdup(strtok(tmp, ":"));
6206 string = std_headers;
6207 while (*string != NULL) {
6208 headername_trans = prefs_common_translated_header_name(*string);
6209 if (!strcmp(headername_trans, headername_wcolon))
6210 standard_header = TRUE;
6213 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6214 g_string_append_printf(sh_header, "%s ", headername);
6216 g_free(headername_wcolon);
6218 g_string_truncate(sh_header, strlen(sh_header->str) - 1); /* remove last space */
6219 return g_string_free(sh_header, FALSE);
6222 static gchar *compose_get_header(Compose *compose)
6224 gchar buf[BUFFSIZE];
6225 const gchar *entry_str;
6229 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6231 gchar *from_name = NULL, *from_address = NULL;
6234 cm_return_val_if_fail(compose->account != NULL, NULL);
6235 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6237 header = g_string_sized_new(64);
6240 get_rfc822_date(buf, sizeof(buf));
6241 g_string_append_printf(header, "Date: %s\n", buf);
6245 if (compose->account->name && *compose->account->name) {
6247 QUOTE_IF_REQUIRED(buf, compose->account->name);
6248 tmp = g_strdup_printf("%s <%s>",
6249 buf, compose->account->address);
6251 tmp = g_strdup_printf("%s",
6252 compose->account->address);
6254 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6255 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6257 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6258 from_address = g_strdup(compose->account->address);
6260 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6261 /* extract name and address */
6262 if (strstr(spec, " <") && strstr(spec, ">")) {
6263 from_address = g_strdup(strrchr(spec, '<')+1);
6264 *(strrchr(from_address, '>')) = '\0';
6265 from_name = g_strdup(spec);
6266 *(strrchr(from_name, '<')) = '\0';
6269 from_address = g_strdup(spec);
6276 if (from_name && *from_name) {
6277 compose_convert_header
6278 (compose, buf, sizeof(buf), from_name,
6279 strlen("From: "), TRUE);
6280 QUOTE_IF_REQUIRED(name, buf);
6282 g_string_append_printf(header, "From: %s <%s>\n",
6283 name, from_address);
6285 g_string_append_printf(header, "From: %s\n", from_address);
6288 g_free(from_address);
6291 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6294 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6297 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6301 * If this account is a NNTP account remove Bcc header from
6302 * message body since it otherwise will be publicly shown
6304 if (compose->account->protocol != A_NNTP)
6305 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6308 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6310 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6313 compose_convert_header(compose, buf, sizeof(buf), str,
6314 strlen("Subject: "), FALSE);
6315 g_string_append_printf(header, "Subject: %s\n", buf);
6321 if (compose->account->set_domain && compose->account->domain) {
6322 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6323 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6324 g_snprintf(buf, sizeof(buf), "%s",
6325 strchr(compose->account->address, '@') ?
6326 strchr(compose->account->address, '@')+1 :
6327 compose->account->address);
6329 g_snprintf(buf, sizeof(buf), "%s", "");
6332 if (compose->account->gen_msgid) {
6334 if (compose->account->msgid_with_addr) {
6335 addr = compose->account->address;
6337 generate_msgid(buf, sizeof(buf), addr);
6338 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6339 compose->msgid = g_strdup(buf);
6341 compose->msgid = NULL;
6344 if (compose->remove_references == FALSE) {
6346 if (compose->inreplyto && compose->to_list)
6347 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6350 if (compose->references)
6351 g_string_append_printf(header, "References: %s\n", compose->references);
6355 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6358 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6361 if (compose->account->organization &&
6362 strlen(compose->account->organization) &&
6363 !IS_IN_CUSTOM_HEADER("Organization")) {
6364 compose_convert_header(compose, buf, sizeof(buf),
6365 compose->account->organization,
6366 strlen("Organization: "), FALSE);
6367 g_string_append_printf(header, "Organization: %s\n", buf);
6370 /* Program version and system info */
6371 if (compose->account->gen_xmailer &&
6372 g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6373 !compose->newsgroup_list) {
6374 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6376 gtk_major_version, gtk_minor_version, gtk_micro_version,
6379 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6380 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6382 gtk_major_version, gtk_minor_version, gtk_micro_version,
6386 /* custom headers */
6387 if (compose->account->add_customhdr) {
6390 for (cur = compose->account->customhdr_list; cur != NULL;
6392 CustomHeader *chdr = (CustomHeader *)cur->data;
6394 if (custom_header_is_allowed(chdr->name)
6395 && chdr->value != NULL
6396 && *(chdr->value) != '\0') {
6397 compose_convert_header
6398 (compose, buf, sizeof(buf),
6400 strlen(chdr->name) + 2, FALSE);
6401 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6406 /* Automatic Faces and X-Faces */
6407 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6408 g_string_append_printf(header, "X-Face: %s\n", buf);
6410 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6411 g_string_append_printf(header, "X-Face: %s\n", buf);
6413 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6414 g_string_append_printf(header, "Face: %s\n", buf);
6416 else if (get_default_face (buf, sizeof(buf)) == 0) {
6417 g_string_append_printf(header, "Face: %s\n", buf);
6421 switch (compose->priority) {
6422 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6423 "X-Priority: 1 (Highest)\n");
6425 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6426 "X-Priority: 2 (High)\n");
6428 case PRIORITY_NORMAL: break;
6429 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6430 "X-Priority: 4 (Low)\n");
6432 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6433 "X-Priority: 5 (Lowest)\n");
6435 default: debug_print("compose: priority unknown : %d\n",
6439 /* Request Return Receipt */
6440 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6441 if (compose->return_receipt) {
6442 if (compose->account->name
6443 && *compose->account->name) {
6444 compose_convert_header(compose, buf, sizeof(buf),
6445 compose->account->name,
6446 strlen("Disposition-Notification-To: "),
6448 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6450 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6454 /* get special headers */
6455 for (list = compose->header_list; list; list = list->next) {
6456 ComposeHeaderEntry *headerentry;
6459 gchar *headername_wcolon;
6460 const gchar *headername_trans;
6463 gboolean standard_header = FALSE;
6465 headerentry = ((ComposeHeaderEntry *)list->data);
6467 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6469 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6474 if (!strstr(tmp, ":")) {
6475 headername_wcolon = g_strconcat(tmp, ":", NULL);
6476 headername = g_strdup(tmp);
6478 headername_wcolon = g_strdup(tmp);
6479 headername = g_strdup(strtok(tmp, ":"));
6483 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6484 Xstrdup_a(headervalue, entry_str, return NULL);
6485 subst_char(headervalue, '\r', ' ');
6486 subst_char(headervalue, '\n', ' ');
6487 string = std_headers;
6488 while (*string != NULL) {
6489 headername_trans = prefs_common_translated_header_name(*string);
6490 if (!strcmp(headername_trans, headername_wcolon))
6491 standard_header = TRUE;
6494 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6495 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6498 g_free(headername_wcolon);
6502 g_string_free(header, FALSE);
6507 #undef IS_IN_CUSTOM_HEADER
6509 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6510 gint header_len, gboolean addr_field)
6512 gchar *tmpstr = NULL;
6513 const gchar *out_codeset = NULL;
6515 cm_return_if_fail(src != NULL);
6516 cm_return_if_fail(dest != NULL);
6518 if (len < 1) return;
6520 tmpstr = g_strdup(src);
6522 subst_char(tmpstr, '\n', ' ');
6523 subst_char(tmpstr, '\r', ' ');
6526 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6527 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6528 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6533 codeconv_set_strict(TRUE);
6534 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6535 conv_get_charset_str(compose->out_encoding));
6536 codeconv_set_strict(FALSE);
6538 if (!dest || *dest == '\0') {
6539 gchar *test_conv_global_out = NULL;
6540 gchar *test_conv_reply = NULL;
6542 /* automatic mode. be automatic. */
6543 codeconv_set_strict(TRUE);
6545 out_codeset = conv_get_outgoing_charset_str();
6547 debug_print("trying to convert to %s\n", out_codeset);
6548 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6551 if (!test_conv_global_out && compose->orig_charset
6552 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6553 out_codeset = compose->orig_charset;
6554 debug_print("failure; trying to convert to %s\n", out_codeset);
6555 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6558 if (!test_conv_global_out && !test_conv_reply) {
6560 out_codeset = CS_INTERNAL;
6561 debug_print("finally using %s\n", out_codeset);
6563 g_free(test_conv_global_out);
6564 g_free(test_conv_reply);
6565 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6567 codeconv_set_strict(FALSE);
6572 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6576 cm_return_if_fail(user_data != NULL);
6578 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6579 g_strstrip(address);
6580 if (*address != '\0') {
6581 gchar *name = procheader_get_fromname(address);
6582 extract_address(address);
6583 #ifndef USE_NEW_ADDRBOOK
6584 addressbook_add_contact(name, address, NULL, NULL);
6586 debug_print("%s: %s\n", name, address);
6587 if (addressadd_selection(name, address, NULL, NULL)) {
6588 debug_print( "addressbook_add_contact - added\n" );
6595 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6597 GtkWidget *menuitem;
6600 cm_return_if_fail(menu != NULL);
6601 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6603 menuitem = gtk_separator_menu_item_new();
6604 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6605 gtk_widget_show(menuitem);
6607 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6608 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6610 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6611 g_strstrip(address);
6612 if (*address == '\0') {
6613 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6616 g_signal_connect(G_OBJECT(menuitem), "activate",
6617 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6618 gtk_widget_show(menuitem);
6621 static void compose_create_header_entry(Compose *compose)
6623 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6630 const gchar *header = NULL;
6631 ComposeHeaderEntry *headerentry;
6632 gboolean standard_header = FALSE;
6633 GtkListStore *model;
6635 #if !(GTK_CHECK_VERSION(2,12,0))
6636 GtkTooltips *tips = compose->tooltips;
6639 headerentry = g_new0(ComposeHeaderEntry, 1);
6642 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6643 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6644 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6646 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6648 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6650 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6651 COMPOSE_NEWSGROUPS);
6652 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6654 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6655 COMPOSE_FOLLOWUPTO);
6657 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6658 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6659 G_CALLBACK(compose_grab_focus_cb), compose);
6660 gtk_widget_show(combo);
6661 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6662 compose->header_nextrow, compose->header_nextrow+1,
6663 GTK_SHRINK, GTK_FILL, 0, 0);
6664 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6665 const gchar *last_header_entry = gtk_entry_get_text(
6666 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6668 while (*string != NULL) {
6669 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6670 standard_header = TRUE;
6673 if (standard_header)
6674 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6676 if (!compose->header_last || !standard_header) {
6677 switch(compose->account->protocol) {
6679 header = prefs_common_translated_header_name("Newsgroups:");
6682 header = prefs_common_translated_header_name("To:");
6687 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6689 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6690 G_CALLBACK(compose_grab_focus_cb), compose);
6692 /* Entry field with cleanup button */
6693 button = gtk_button_new();
6694 gtk_button_set_image(GTK_BUTTON(button),
6695 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6696 gtk_widget_show(button);
6697 CLAWS_SET_TIP(button,
6698 _("Delete entry contents"));
6699 entry = gtk_entry_new();
6700 gtk_widget_show(entry);
6701 CLAWS_SET_TIP(entry,
6702 _("Use <tab> to autocomplete from addressbook"));
6703 hbox = gtk_hbox_new (FALSE, 0);
6704 gtk_widget_show(hbox);
6705 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6706 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6707 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6708 compose->header_nextrow, compose->header_nextrow+1,
6709 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6711 g_signal_connect(G_OBJECT(entry), "key-press-event",
6712 G_CALLBACK(compose_headerentry_key_press_event_cb),
6714 g_signal_connect(G_OBJECT(entry), "changed",
6715 G_CALLBACK(compose_headerentry_changed_cb),
6717 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6718 G_CALLBACK(compose_grab_focus_cb), compose);
6720 g_signal_connect(G_OBJECT(button), "clicked",
6721 G_CALLBACK(compose_headerentry_button_clicked_cb),
6725 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6726 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6727 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6728 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6729 G_CALLBACK(compose_header_drag_received_cb),
6731 g_signal_connect(G_OBJECT(entry), "drag-drop",
6732 G_CALLBACK(compose_drag_drop),
6734 g_signal_connect(G_OBJECT(entry), "populate-popup",
6735 G_CALLBACK(compose_entry_popup_extend),
6738 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6740 headerentry->compose = compose;
6741 headerentry->combo = combo;
6742 headerentry->entry = entry;
6743 headerentry->button = button;
6744 headerentry->hbox = hbox;
6745 headerentry->headernum = compose->header_nextrow;
6746 headerentry->type = PREF_NONE;
6748 compose->header_nextrow++;
6749 compose->header_last = headerentry;
6750 compose->header_list =
6751 g_slist_append(compose->header_list,
6755 static void compose_add_header_entry(Compose *compose, const gchar *header,
6756 gchar *text, ComposePrefType pref_type)
6758 ComposeHeaderEntry *last_header = compose->header_last;
6759 gchar *tmp = g_strdup(text), *email;
6760 gboolean replyto_hdr;
6762 replyto_hdr = (!strcasecmp(header,
6763 prefs_common_translated_header_name("Reply-To:")) ||
6765 prefs_common_translated_header_name("Followup-To:")) ||
6767 prefs_common_translated_header_name("In-Reply-To:")));
6769 extract_address(tmp);
6770 email = g_utf8_strdown(tmp, -1);
6772 if (replyto_hdr == FALSE &&
6773 g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6775 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6776 header, text, (gint) pref_type);
6782 if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6783 gtk_entry_set_text(GTK_ENTRY(
6784 gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6786 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6787 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6788 last_header->type = pref_type;
6790 if (replyto_hdr == FALSE)
6791 g_hash_table_insert(compose->email_hashtable, email,
6792 GUINT_TO_POINTER(1));
6799 static void compose_destroy_headerentry(Compose *compose,
6800 ComposeHeaderEntry *headerentry)
6802 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6805 extract_address(text);
6806 email = g_utf8_strdown(text, -1);
6807 g_hash_table_remove(compose->email_hashtable, email);
6811 gtk_widget_destroy(headerentry->combo);
6812 gtk_widget_destroy(headerentry->entry);
6813 gtk_widget_destroy(headerentry->button);
6814 gtk_widget_destroy(headerentry->hbox);
6815 g_free(headerentry);
6818 static void compose_remove_header_entries(Compose *compose)
6821 for (list = compose->header_list; list; list = list->next)
6822 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6824 compose->header_last = NULL;
6825 g_slist_free(compose->header_list);
6826 compose->header_list = NULL;
6827 compose->header_nextrow = 1;
6828 compose_create_header_entry(compose);
6831 static GtkWidget *compose_create_header(Compose *compose)
6833 GtkWidget *from_optmenu_hbox;
6834 GtkWidget *header_scrolledwin_main;
6835 GtkWidget *header_table_main;
6836 GtkWidget *header_scrolledwin;
6837 GtkWidget *header_table;
6839 /* parent with account selection and from header */
6840 header_scrolledwin_main = gtk_scrolled_window_new(NULL, NULL);
6841 gtk_widget_show(header_scrolledwin_main);
6842 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin_main), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6844 header_table_main = gtk_table_new(2, 2, FALSE);
6845 gtk_widget_show(header_table_main);
6846 gtk_container_set_border_width(GTK_CONTAINER(header_table_main), BORDER_WIDTH);
6847 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin_main), header_table_main);
6848 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin_main)))), GTK_SHADOW_NONE);
6850 from_optmenu_hbox = compose_account_option_menu_create(compose);
6851 gtk_table_attach(GTK_TABLE(header_table_main), from_optmenu_hbox,
6852 0, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6854 /* child with header labels and entries */
6855 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6856 gtk_widget_show(header_scrolledwin);
6857 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6859 header_table = gtk_table_new(2, 2, FALSE);
6860 gtk_widget_show(header_table);
6861 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6862 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6863 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6865 gtk_table_attach(GTK_TABLE(header_table_main), header_scrolledwin,
6866 0, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 2);
6868 compose->header_table = header_table;
6869 compose->header_list = NULL;
6870 compose->header_nextrow = 0;
6872 compose_create_header_entry(compose);
6874 compose->table = NULL;
6876 return header_scrolledwin_main;
6879 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6881 Compose *compose = (Compose *)data;
6882 GdkEventButton event;
6885 event.time = gtk_get_current_event_time();
6887 return attach_button_pressed(compose->attach_clist, &event, compose);
6890 static GtkWidget *compose_create_attach(Compose *compose)
6892 GtkWidget *attach_scrwin;
6893 GtkWidget *attach_clist;
6895 GtkListStore *store;
6896 GtkCellRenderer *renderer;
6897 GtkTreeViewColumn *column;
6898 GtkTreeSelection *selection;
6900 /* attachment list */
6901 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6902 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6903 GTK_POLICY_AUTOMATIC,
6904 GTK_POLICY_AUTOMATIC);
6905 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6907 store = gtk_list_store_new(N_ATTACH_COLS,
6913 G_TYPE_AUTO_POINTER,
6915 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6916 (GTK_TREE_MODEL(store)));
6917 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6918 g_object_unref(store);
6920 renderer = gtk_cell_renderer_text_new();
6921 column = gtk_tree_view_column_new_with_attributes
6922 (_("Mime type"), renderer, "text",
6923 COL_MIMETYPE, NULL);
6924 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6926 renderer = gtk_cell_renderer_text_new();
6927 column = gtk_tree_view_column_new_with_attributes
6928 (_("Size"), renderer, "text",
6930 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6932 renderer = gtk_cell_renderer_text_new();
6933 column = gtk_tree_view_column_new_with_attributes
6934 (_("Name"), renderer, "text",
6936 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6938 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6939 prefs_common.use_stripes_everywhere);
6940 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6941 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6943 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6944 G_CALLBACK(attach_selected), compose);
6945 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6946 G_CALLBACK(attach_button_pressed), compose);
6948 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6949 G_CALLBACK(popup_attach_button_pressed), compose);
6951 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6952 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6953 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6954 G_CALLBACK(popup_attach_button_pressed), compose);
6956 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6957 G_CALLBACK(attach_key_pressed), compose);
6960 gtk_drag_dest_set(attach_clist,
6961 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6962 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6963 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6964 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6965 G_CALLBACK(compose_attach_drag_received_cb),
6967 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6968 G_CALLBACK(compose_drag_drop),
6971 compose->attach_scrwin = attach_scrwin;
6972 compose->attach_clist = attach_clist;
6974 return attach_scrwin;
6977 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6978 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6980 static GtkWidget *compose_create_others(Compose *compose)
6983 GtkWidget *savemsg_checkbtn;
6984 GtkWidget *savemsg_combo;
6985 GtkWidget *savemsg_select;
6988 gchar *folderidentifier;
6990 /* Table for settings */
6991 table = gtk_table_new(3, 1, FALSE);
6992 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6993 gtk_widget_show(table);
6994 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6997 /* Save Message to folder */
6998 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6999 gtk_widget_show(savemsg_checkbtn);
7000 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7001 if (account_get_special_folder(compose->account, F_OUTBOX)) {
7002 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
7004 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
7005 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
7007 savemsg_combo = gtk_combo_box_entry_new_text();
7008 compose->savemsg_checkbtn = savemsg_checkbtn;
7009 compose->savemsg_combo = savemsg_combo;
7010 gtk_widget_show(savemsg_combo);
7012 if (prefs_common.compose_save_to_history)
7013 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
7014 prefs_common.compose_save_to_history);
7016 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
7017 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
7018 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
7019 G_CALLBACK(compose_grab_focus_cb), compose);
7020 if (account_get_special_folder(compose->account, F_OUTBOX)) {
7021 folderidentifier = folder_item_get_identifier(account_get_special_folder
7022 (compose->account, F_OUTBOX));
7023 compose_set_save_to(compose, folderidentifier);
7024 g_free(folderidentifier);
7027 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
7028 gtk_widget_show(savemsg_select);
7029 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7030 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
7031 G_CALLBACK(compose_savemsg_select_cb),
7039 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
7041 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
7042 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
7045 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
7050 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
7053 path = folder_item_get_identifier(dest);
7055 compose_set_save_to(compose, path);
7059 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
7060 GdkAtom clip, GtkTextIter *insert_place);
7063 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
7067 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7069 if (event->button == 3) {
7071 GtkTextIter sel_start, sel_end;
7072 gboolean stuff_selected;
7074 /* move the cursor to allow GtkAspell to check the word
7075 * under the mouse */
7076 if (event->x && event->y) {
7077 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7078 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7080 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7083 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
7084 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7087 stuff_selected = gtk_text_buffer_get_selection_bounds(
7089 &sel_start, &sel_end);
7091 gtk_text_buffer_place_cursor (buffer, &iter);
7092 /* reselect stuff */
7094 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
7095 gtk_text_buffer_select_range(buffer,
7096 &sel_start, &sel_end);
7098 return FALSE; /* pass the event so that the right-click goes through */
7101 if (event->button == 2) {
7106 /* get the middle-click position to paste at the correct place */
7107 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7108 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7110 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7113 entry_paste_clipboard(compose, text,
7114 prefs_common.linewrap_pastes,
7115 GDK_SELECTION_PRIMARY, &iter);
7123 static void compose_spell_menu_changed(void *data)
7125 Compose *compose = (Compose *)data;
7127 GtkWidget *menuitem;
7128 GtkWidget *parent_item;
7129 GtkMenu *menu = GTK_MENU(gtk_menu_new());
7132 if (compose->gtkaspell == NULL)
7135 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
7136 "/Menu/Spelling/Options");
7138 /* setting the submenu removes /Spelling/Options from the factory
7139 * so we need to save it */
7141 if (parent_item == NULL) {
7142 parent_item = compose->aspell_options_menu;
7143 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
7145 compose->aspell_options_menu = parent_item;
7147 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
7149 spell_menu = g_slist_reverse(spell_menu);
7150 for (items = spell_menu;
7151 items; items = items->next) {
7152 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
7153 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
7154 gtk_widget_show(GTK_WIDGET(menuitem));
7156 g_slist_free(spell_menu);
7158 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7159 gtk_widget_show(parent_item);
7162 static void compose_dict_changed(void *data)
7164 Compose *compose = (Compose *) data;
7166 if(compose->gtkaspell &&
7167 compose->gtkaspell->recheck_when_changing_dict == FALSE)
7170 gtkaspell_highlight_all(compose->gtkaspell);
7171 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7175 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7177 Compose *compose = (Compose *)data;
7178 GdkEventButton event;
7181 event.time = gtk_get_current_event_time();
7185 return text_clicked(compose->text, &event, compose);
7188 static gboolean compose_force_window_origin = TRUE;
7189 static Compose *compose_create(PrefsAccount *account,
7198 GtkWidget *handlebox;
7200 GtkWidget *notebook;
7202 GtkWidget *attach_hbox;
7203 GtkWidget *attach_lab1;
7204 GtkWidget *attach_lab2;
7209 GtkWidget *subject_hbox;
7210 GtkWidget *subject_frame;
7211 GtkWidget *subject_entry;
7215 GtkWidget *edit_vbox;
7216 GtkWidget *ruler_hbox;
7218 GtkWidget *scrolledwin;
7220 GtkTextBuffer *buffer;
7221 GtkClipboard *clipboard;
7223 UndoMain *undostruct;
7225 gchar *titles[N_ATTACH_COLS];
7226 GtkWidget *popupmenu;
7227 GtkWidget *tmpl_menu;
7228 GtkActionGroup *action_group = NULL;
7231 GtkAspell * gtkaspell = NULL;
7234 static GdkGeometry geometry;
7236 cm_return_val_if_fail(account != NULL, NULL);
7238 debug_print("Creating compose window...\n");
7239 compose = g_new0(Compose, 1);
7241 titles[COL_MIMETYPE] = _("MIME type");
7242 titles[COL_SIZE] = _("Size");
7243 titles[COL_NAME] = _("Name");
7244 titles[COL_CHARSET] = _("Charset");
7246 compose->batch = batch;
7247 compose->account = account;
7248 compose->folder = folder;
7250 compose->mutex = g_mutex_new();
7251 compose->set_cursor_pos = -1;
7253 #if !(GTK_CHECK_VERSION(2,12,0))
7254 compose->tooltips = tips;
7257 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7259 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7260 gtk_widget_set_size_request(window, prefs_common.compose_width,
7261 prefs_common.compose_height);
7263 if (!geometry.max_width) {
7264 geometry.max_width = gdk_screen_width();
7265 geometry.max_height = gdk_screen_height();
7268 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7269 &geometry, GDK_HINT_MAX_SIZE);
7270 if (!geometry.min_width) {
7271 geometry.min_width = 600;
7272 geometry.min_height = 440;
7274 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7275 &geometry, GDK_HINT_MIN_SIZE);
7277 #ifndef GENERIC_UMPC
7278 if (compose_force_window_origin)
7279 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7280 prefs_common.compose_y);
7282 g_signal_connect(G_OBJECT(window), "delete_event",
7283 G_CALLBACK(compose_delete_cb), compose);
7284 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7285 gtk_widget_realize(window);
7287 gtkut_widget_set_composer_icon(window);
7289 vbox = gtk_vbox_new(FALSE, 0);
7290 gtk_container_add(GTK_CONTAINER(window), vbox);
7292 compose->ui_manager = gtk_ui_manager_new();
7293 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7294 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7295 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7296 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7297 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7298 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7299 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7300 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7301 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7302 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7305 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7307 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7310 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7311 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7313 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7315 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7316 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7317 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7320 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7321 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7322 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7323 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7324 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7325 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7326 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7327 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7328 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7329 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM)
7330 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7331 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7334 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7335 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7336 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7338 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7339 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7340 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7342 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7343 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7344 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7345 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7347 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7349 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7350 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7351 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7352 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7353 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7354 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7355 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7356 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7357 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7358 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7359 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7360 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7361 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7362 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7363 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7365 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7367 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7368 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7369 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7370 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7371 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7373 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7375 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7379 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7380 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7381 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7382 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7383 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7384 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7388 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7389 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7390 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7391 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7392 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7394 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7395 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7396 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7397 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7398 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7401 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7402 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7403 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7404 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7405 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7406 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7407 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7409 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7410 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7411 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7412 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7413 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7415 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7417 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7418 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7419 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7420 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7421 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7423 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7424 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)
7425 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)
7426 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7428 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7430 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7431 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)
7432 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)
7434 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7436 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7437 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)
7438 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7440 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7441 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)
7442 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7444 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7446 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7447 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)
7448 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7449 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7450 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7452 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7453 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)
7454 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)
7455 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7456 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7458 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7459 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7460 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7461 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7462 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7463 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7465 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7466 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7467 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)
7469 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7470 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7471 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7475 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7476 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7477 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7478 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7479 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7480 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7483 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7485 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7486 gtk_widget_show_all(menubar);
7488 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7490 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7492 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7495 if (prefs_common.toolbar_detachable) {
7496 handlebox = gtk_handle_box_new();
7498 handlebox = gtk_hbox_new(FALSE, 0);
7500 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7502 gtk_widget_realize(handlebox);
7504 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7507 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7511 vbox2 = gtk_vbox_new(FALSE, 2);
7512 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7513 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7516 notebook = gtk_notebook_new();
7517 gtk_widget_set_size_request(notebook, -1, prefs_common.compose_notebook_height);
7518 gtk_widget_show(notebook);
7520 /* header labels and entries */
7521 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7522 compose_create_header(compose),
7523 gtk_label_new_with_mnemonic(_("Hea_der")));
7524 /* attachment list */
7525 attach_hbox = gtk_hbox_new(FALSE, 0);
7526 gtk_widget_show(attach_hbox);
7528 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7529 gtk_widget_show(attach_lab1);
7530 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7532 attach_lab2 = gtk_label_new("");
7533 gtk_widget_show(attach_lab2);
7534 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7536 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7537 compose_create_attach(compose),
7540 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7541 compose_create_others(compose),
7542 gtk_label_new_with_mnemonic(_("Othe_rs")));
7545 subject_hbox = gtk_hbox_new(FALSE, 0);
7546 gtk_widget_show(subject_hbox);
7548 subject_frame = gtk_frame_new(NULL);
7549 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7550 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7551 gtk_widget_show(subject_frame);
7553 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7554 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7555 gtk_widget_show(subject);
7557 label = gtk_label_new(_("Subject:"));
7558 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7559 gtk_widget_show(label);
7562 subject_entry = claws_spell_entry_new();
7564 subject_entry = gtk_entry_new();
7566 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7567 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7568 G_CALLBACK(compose_grab_focus_cb), compose);
7569 gtk_widget_show(subject_entry);
7570 compose->subject_entry = subject_entry;
7571 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7573 edit_vbox = gtk_vbox_new(FALSE, 0);
7575 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7578 ruler_hbox = gtk_hbox_new(FALSE, 0);
7579 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7581 ruler = gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL);
7582 gtk_shruler_set_range(GTK_SHRULER(ruler), 0.0, 100.0, 1.0);
7583 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7587 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7588 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7589 GTK_POLICY_AUTOMATIC,
7590 GTK_POLICY_AUTOMATIC);
7591 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7593 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7595 text = gtk_text_view_new();
7596 if (prefs_common.show_compose_margin) {
7597 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7598 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7600 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7601 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7602 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7603 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7604 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7606 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7607 g_signal_connect(G_OBJECT(notebook), "size_allocate",
7608 G_CALLBACK(compose_notebook_size_alloc), compose);
7609 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7610 G_CALLBACK(compose_edit_size_alloc),
7612 g_signal_connect(G_OBJECT(buffer), "changed",
7613 G_CALLBACK(compose_changed_cb), compose);
7614 g_signal_connect(G_OBJECT(text), "grab_focus",
7615 G_CALLBACK(compose_grab_focus_cb), compose);
7616 g_signal_connect(G_OBJECT(buffer), "insert_text",
7617 G_CALLBACK(text_inserted), compose);
7618 g_signal_connect(G_OBJECT(text), "button_press_event",
7619 G_CALLBACK(text_clicked), compose);
7621 g_signal_connect(G_OBJECT(text), "popup-menu",
7622 G_CALLBACK(compose_popup_menu), compose);
7624 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7625 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7626 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7627 G_CALLBACK(compose_popup_menu), compose);
7629 g_signal_connect(G_OBJECT(subject_entry), "changed",
7630 G_CALLBACK(compose_changed_cb), compose);
7633 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7634 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7635 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7636 g_signal_connect(G_OBJECT(text), "drag_data_received",
7637 G_CALLBACK(compose_insert_drag_received_cb),
7639 g_signal_connect(G_OBJECT(text), "drag-drop",
7640 G_CALLBACK(compose_drag_drop),
7642 g_signal_connect(G_OBJECT(text), "key-press-event",
7643 G_CALLBACK(completion_set_focus_to_subject),
7645 gtk_widget_show_all(vbox);
7647 /* pane between attach clist and text */
7648 paned = gtk_vpaned_new();
7649 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7651 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7652 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7654 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7656 gtk_paned_add1(GTK_PANED(paned), notebook);
7657 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7658 gtk_widget_show_all(paned);
7661 if (prefs_common.textfont) {
7662 PangoFontDescription *font_desc;
7664 font_desc = pango_font_description_from_string
7665 (prefs_common.textfont);
7667 gtk_widget_modify_font(text, font_desc);
7668 pango_font_description_free(font_desc);
7672 gtk_action_group_add_actions(action_group, compose_popup_entries,
7673 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7674 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7675 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7676 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7677 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7678 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7679 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7681 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7683 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7684 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7685 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7687 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7689 undostruct = undo_init(text);
7690 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7693 address_completion_start(window);
7695 compose->window = window;
7696 compose->vbox = vbox;
7697 compose->menubar = menubar;
7698 compose->handlebox = handlebox;
7700 compose->vbox2 = vbox2;
7702 compose->paned = paned;
7704 compose->attach_label = attach_lab2;
7706 compose->notebook = notebook;
7707 compose->edit_vbox = edit_vbox;
7708 compose->ruler_hbox = ruler_hbox;
7709 compose->ruler = ruler;
7710 compose->scrolledwin = scrolledwin;
7711 compose->text = text;
7713 compose->focused_editable = NULL;
7715 compose->popupmenu = popupmenu;
7717 compose->tmpl_menu = tmpl_menu;
7719 compose->mode = mode;
7720 compose->rmode = mode;
7722 compose->targetinfo = NULL;
7723 compose->replyinfo = NULL;
7724 compose->fwdinfo = NULL;
7726 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7727 g_str_equal, (GDestroyNotify) g_free, NULL);
7729 compose->replyto = NULL;
7731 compose->bcc = NULL;
7732 compose->followup_to = NULL;
7734 compose->ml_post = NULL;
7736 compose->inreplyto = NULL;
7737 compose->references = NULL;
7738 compose->msgid = NULL;
7739 compose->boundary = NULL;
7741 compose->autowrap = prefs_common.autowrap;
7742 compose->autoindent = prefs_common.auto_indent;
7743 compose->use_signing = FALSE;
7744 compose->use_encryption = FALSE;
7745 compose->privacy_system = NULL;
7747 compose->modified = FALSE;
7749 compose->return_receipt = FALSE;
7751 compose->to_list = NULL;
7752 compose->newsgroup_list = NULL;
7754 compose->undostruct = undostruct;
7756 compose->sig_str = NULL;
7758 compose->exteditor_file = NULL;
7759 compose->exteditor_pid = -1;
7760 compose->exteditor_tag = -1;
7761 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7764 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7765 if (mode != COMPOSE_REDIRECT) {
7766 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7767 strcmp(prefs_common.dictionary, "")) {
7768 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7769 prefs_common.alt_dictionary,
7770 conv_get_locale_charset_str(),
7771 prefs_common.misspelled_col,
7772 prefs_common.check_while_typing,
7773 prefs_common.recheck_when_changing_dict,
7774 prefs_common.use_alternate,
7775 prefs_common.use_both_dicts,
7776 GTK_TEXT_VIEW(text),
7777 GTK_WINDOW(compose->window),
7778 compose_dict_changed,
7779 compose_spell_menu_changed,
7782 alertpanel_error(_("Spell checker could not "
7784 gtkaspell_checkers_strerror());
7785 gtkaspell_checkers_reset_error();
7787 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7791 compose->gtkaspell = gtkaspell;
7792 compose_spell_menu_changed(compose);
7793 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7796 compose_select_account(compose, account, TRUE);
7798 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7799 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7801 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7802 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7804 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7805 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7807 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7808 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7810 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7811 if (account->protocol != A_NNTP)
7812 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7813 prefs_common_translated_header_name("To:"));
7815 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7816 prefs_common_translated_header_name("Newsgroups:"));
7818 #ifndef USE_NEW_ADDRBOOK
7819 addressbook_set_target_compose(compose);
7821 if (mode != COMPOSE_REDIRECT)
7822 compose_set_template_menu(compose);
7824 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7827 compose_list = g_list_append(compose_list, compose);
7829 if (!prefs_common.show_ruler)
7830 gtk_widget_hide(ruler_hbox);
7832 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7835 compose->priority = PRIORITY_NORMAL;
7836 compose_update_priority_menu_item(compose);
7838 compose_set_out_encoding(compose);
7841 compose_update_actions_menu(compose);
7843 /* Privacy Systems menu */
7844 compose_update_privacy_systems_menu(compose);
7846 activate_privacy_system(compose, account, TRUE);
7847 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7849 gtk_widget_realize(window);
7851 gtk_widget_show(window);
7853 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7854 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7861 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7866 GtkWidget *optmenubox;
7869 GtkWidget *from_name = NULL;
7870 #if !(GTK_CHECK_VERSION(2,12,0))
7871 GtkTooltips *tips = compose->tooltips;
7874 gint num = 0, def_menu = 0;
7876 accounts = account_get_list();
7877 cm_return_val_if_fail(accounts != NULL, NULL);
7879 optmenubox = gtk_event_box_new();
7880 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7881 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7883 hbox = gtk_hbox_new(FALSE, 6);
7884 from_name = gtk_entry_new();
7886 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7887 G_CALLBACK(compose_grab_focus_cb), compose);
7889 for (; accounts != NULL; accounts = accounts->next, num++) {
7890 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7891 gchar *name, *from = NULL;
7893 if (ac == compose->account) def_menu = num;
7895 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7898 if (ac == compose->account) {
7899 if (ac->name && *ac->name) {
7901 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7902 from = g_strdup_printf("%s <%s>",
7904 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7906 from = g_strdup_printf("%s",
7908 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7911 COMBOBOX_ADD(menu, name, ac->account_id);
7916 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7918 g_signal_connect(G_OBJECT(optmenu), "changed",
7919 G_CALLBACK(account_activated),
7921 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7922 G_CALLBACK(compose_entry_popup_extend),
7925 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7926 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7928 CLAWS_SET_TIP(optmenubox,
7929 _("Account to use for this email"));
7930 CLAWS_SET_TIP(from_name,
7931 _("Sender address to be used"));
7933 compose->account_combo = optmenu;
7934 compose->from_name = from_name;
7939 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7941 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7942 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7943 Compose *compose = (Compose *) data;
7945 compose->priority = value;
7949 static void compose_reply_change_mode(Compose *compose,
7952 gboolean was_modified = compose->modified;
7954 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7956 cm_return_if_fail(compose->replyinfo != NULL);
7958 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7960 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7962 if (action == COMPOSE_REPLY_TO_ALL)
7964 if (action == COMPOSE_REPLY_TO_SENDER)
7966 if (action == COMPOSE_REPLY_TO_LIST)
7969 compose_remove_header_entries(compose);
7970 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7971 if (compose->account->set_autocc && compose->account->auto_cc)
7972 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7974 if (compose->account->set_autobcc && compose->account->auto_bcc)
7975 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7977 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7978 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7979 compose_show_first_last_header(compose, TRUE);
7980 compose->modified = was_modified;
7981 compose_set_title(compose);
7984 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7986 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7987 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7988 Compose *compose = (Compose *) data;
7991 compose_reply_change_mode(compose, value);
7994 static void compose_update_priority_menu_item(Compose * compose)
7996 GtkWidget *menuitem = NULL;
7997 switch (compose->priority) {
7998 case PRIORITY_HIGHEST:
7999 menuitem = gtk_ui_manager_get_widget
8000 (compose->ui_manager, "/Menu/Options/Priority/Highest");
8003 menuitem = gtk_ui_manager_get_widget
8004 (compose->ui_manager, "/Menu/Options/Priority/High");
8006 case PRIORITY_NORMAL:
8007 menuitem = gtk_ui_manager_get_widget
8008 (compose->ui_manager, "/Menu/Options/Priority/Normal");
8011 menuitem = gtk_ui_manager_get_widget
8012 (compose->ui_manager, "/Menu/Options/Priority/Low");
8014 case PRIORITY_LOWEST:
8015 menuitem = gtk_ui_manager_get_widget
8016 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
8019 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8022 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
8024 Compose *compose = (Compose *) data;
8026 gboolean can_sign = FALSE, can_encrypt = FALSE;
8028 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
8030 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
8033 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
8034 g_free(compose->privacy_system);
8035 compose->privacy_system = NULL;
8036 if (systemid != NULL) {
8037 compose->privacy_system = g_strdup(systemid);
8039 can_sign = privacy_system_can_sign(systemid);
8040 can_encrypt = privacy_system_can_encrypt(systemid);
8043 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
8045 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8046 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8049 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
8051 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8052 GtkWidget *menuitem = NULL;
8053 GList *children, *amenu;
8054 gboolean can_sign = FALSE, can_encrypt = FALSE;
8055 gboolean found = FALSE;
8057 if (compose->privacy_system != NULL) {
8059 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8060 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
8061 cm_return_if_fail(menuitem != NULL);
8063 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
8066 while (amenu != NULL) {
8067 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
8068 if (systemid != NULL) {
8069 if (strcmp(systemid, compose->privacy_system) == 0 &&
8070 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8071 menuitem = GTK_WIDGET(amenu->data);
8073 can_sign = privacy_system_can_sign(systemid);
8074 can_encrypt = privacy_system_can_encrypt(systemid);
8078 } else if (strlen(compose->privacy_system) == 0 &&
8079 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8080 menuitem = GTK_WIDGET(amenu->data);
8083 can_encrypt = FALSE;
8088 amenu = amenu->next;
8090 g_list_free(children);
8091 if (menuitem != NULL)
8092 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8094 if (warn && !found && strlen(compose->privacy_system)) {
8095 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8096 "will not be able to sign or encrypt this message."),
8097 compose->privacy_system);
8101 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8102 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8105 static void compose_set_out_encoding(Compose *compose)
8107 CharSet out_encoding;
8108 const gchar *branch = NULL;
8109 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
8111 switch(out_encoding) {
8112 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8113 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
8114 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
8115 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
8116 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
8117 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
8118 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
8119 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
8120 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
8121 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
8122 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
8123 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
8124 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
8125 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
8126 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
8127 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
8128 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
8129 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
8130 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
8131 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
8132 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
8133 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
8134 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
8135 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
8136 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
8137 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
8138 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
8139 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
8140 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
8141 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
8142 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
8143 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
8144 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8146 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8149 static void compose_set_template_menu(Compose *compose)
8151 GSList *tmpl_list, *cur;
8155 tmpl_list = template_get_config();
8157 menu = gtk_menu_new();
8159 gtk_menu_set_accel_group (GTK_MENU (menu),
8160 gtk_ui_manager_get_accel_group(compose->ui_manager));
8161 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8162 Template *tmpl = (Template *)cur->data;
8163 gchar *accel_path = NULL;
8164 item = gtk_menu_item_new_with_label(tmpl->name);
8165 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8166 g_signal_connect(G_OBJECT(item), "activate",
8167 G_CALLBACK(compose_template_activate_cb),
8169 g_object_set_data(G_OBJECT(item), "template", tmpl);
8170 gtk_widget_show(item);
8171 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8172 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8176 gtk_widget_show(menu);
8177 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8180 void compose_update_actions_menu(Compose *compose)
8182 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8185 static void compose_update_privacy_systems_menu(Compose *compose)
8187 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8188 GSList *systems, *cur;
8190 GtkWidget *system_none;
8192 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8193 GtkWidget *privacy_menu = gtk_menu_new();
8195 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8196 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8198 g_signal_connect(G_OBJECT(system_none), "activate",
8199 G_CALLBACK(compose_set_privacy_system_cb), compose);
8201 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8202 gtk_widget_show(system_none);
8204 systems = privacy_get_system_ids();
8205 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8206 gchar *systemid = cur->data;
8208 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8209 widget = gtk_radio_menu_item_new_with_label(group,
8210 privacy_system_get_name(systemid));
8211 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8212 g_strdup(systemid), g_free);
8213 g_signal_connect(G_OBJECT(widget), "activate",
8214 G_CALLBACK(compose_set_privacy_system_cb), compose);
8216 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8217 gtk_widget_show(widget);
8220 g_slist_free(systems);
8221 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8222 gtk_widget_show_all(privacy_menu);
8223 gtk_widget_show_all(privacy_menuitem);
8226 void compose_reflect_prefs_all(void)
8231 for (cur = compose_list; cur != NULL; cur = cur->next) {
8232 compose = (Compose *)cur->data;
8233 compose_set_template_menu(compose);
8237 void compose_reflect_prefs_pixmap_theme(void)
8242 for (cur = compose_list; cur != NULL; cur = cur->next) {
8243 compose = (Compose *)cur->data;
8244 toolbar_update(TOOLBAR_COMPOSE, compose);
8248 static const gchar *compose_quote_char_from_context(Compose *compose)
8250 const gchar *qmark = NULL;
8252 cm_return_val_if_fail(compose != NULL, NULL);
8254 switch (compose->mode) {
8255 /* use forward-specific quote char */
8256 case COMPOSE_FORWARD:
8257 case COMPOSE_FORWARD_AS_ATTACH:
8258 case COMPOSE_FORWARD_INLINE:
8259 if (compose->folder && compose->folder->prefs &&
8260 compose->folder->prefs->forward_with_format)
8261 qmark = compose->folder->prefs->forward_quotemark;
8262 else if (compose->account->forward_with_format)
8263 qmark = compose->account->forward_quotemark;
8265 qmark = prefs_common.fw_quotemark;
8268 /* use reply-specific quote char in all other modes */
8270 if (compose->folder && compose->folder->prefs &&
8271 compose->folder->prefs->reply_with_format)
8272 qmark = compose->folder->prefs->reply_quotemark;
8273 else if (compose->account->reply_with_format)
8274 qmark = compose->account->reply_quotemark;
8276 qmark = prefs_common.quotemark;
8280 if (qmark == NULL || *qmark == '\0')
8286 static void compose_template_apply(Compose *compose, Template *tmpl,
8290 GtkTextBuffer *buffer;
8294 gchar *parsed_str = NULL;
8295 gint cursor_pos = 0;
8296 const gchar *err_msg = _("The body of the template has an error at line %d.");
8299 /* process the body */
8301 text = GTK_TEXT_VIEW(compose->text);
8302 buffer = gtk_text_view_get_buffer(text);
8305 qmark = compose_quote_char_from_context(compose);
8307 if (compose->replyinfo != NULL) {
8310 gtk_text_buffer_set_text(buffer, "", -1);
8311 mark = gtk_text_buffer_get_insert(buffer);
8312 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8314 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8315 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8317 } else if (compose->fwdinfo != NULL) {
8320 gtk_text_buffer_set_text(buffer, "", -1);
8321 mark = gtk_text_buffer_get_insert(buffer);
8322 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8324 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8325 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8328 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8330 GtkTextIter start, end;
8333 gtk_text_buffer_get_start_iter(buffer, &start);
8334 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8335 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8337 /* clear the buffer now */
8339 gtk_text_buffer_set_text(buffer, "", -1);
8341 parsed_str = compose_quote_fmt(compose, dummyinfo,
8342 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8343 procmsg_msginfo_free( dummyinfo );
8349 gtk_text_buffer_set_text(buffer, "", -1);
8350 mark = gtk_text_buffer_get_insert(buffer);
8351 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8354 if (replace && parsed_str && compose->account->auto_sig)
8355 compose_insert_sig(compose, FALSE);
8357 if (replace && parsed_str) {
8358 gtk_text_buffer_get_start_iter(buffer, &iter);
8359 gtk_text_buffer_place_cursor(buffer, &iter);
8363 cursor_pos = quote_fmt_get_cursor_pos();
8364 compose->set_cursor_pos = cursor_pos;
8365 if (cursor_pos == -1)
8367 gtk_text_buffer_get_start_iter(buffer, &iter);
8368 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8369 gtk_text_buffer_place_cursor(buffer, &iter);
8372 /* process the other fields */
8374 compose_template_apply_fields(compose, tmpl);
8375 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8376 quote_fmt_reset_vartable();
8377 compose_changed_cb(NULL, compose);
8380 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8381 gtkaspell_highlight_all(compose->gtkaspell);
8385 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8387 MsgInfo* dummyinfo = NULL;
8388 MsgInfo *msginfo = NULL;
8391 if (compose->replyinfo != NULL)
8392 msginfo = compose->replyinfo;
8393 else if (compose->fwdinfo != NULL)
8394 msginfo = compose->fwdinfo;
8396 dummyinfo = compose_msginfo_new_from_compose(compose);
8397 msginfo = dummyinfo;
8400 if (tmpl->from && *tmpl->from != '\0') {
8402 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8403 compose->gtkaspell);
8405 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8407 quote_fmt_scan_string(tmpl->from);
8410 buf = quote_fmt_get_buffer();
8412 alertpanel_error(_("Template From format error."));
8414 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8418 if (tmpl->to && *tmpl->to != '\0') {
8420 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8421 compose->gtkaspell);
8423 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8425 quote_fmt_scan_string(tmpl->to);
8428 buf = quote_fmt_get_buffer();
8430 alertpanel_error(_("Template To format error."));
8432 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8436 if (tmpl->cc && *tmpl->cc != '\0') {
8438 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8439 compose->gtkaspell);
8441 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8443 quote_fmt_scan_string(tmpl->cc);
8446 buf = quote_fmt_get_buffer();
8448 alertpanel_error(_("Template Cc format error."));
8450 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8454 if (tmpl->bcc && *tmpl->bcc != '\0') {
8456 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8457 compose->gtkaspell);
8459 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8461 quote_fmt_scan_string(tmpl->bcc);
8464 buf = quote_fmt_get_buffer();
8466 alertpanel_error(_("Template Bcc format error."));
8468 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8472 /* process the subject */
8473 if (tmpl->subject && *tmpl->subject != '\0') {
8475 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8476 compose->gtkaspell);
8478 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8480 quote_fmt_scan_string(tmpl->subject);
8483 buf = quote_fmt_get_buffer();
8485 alertpanel_error(_("Template subject format error."));
8487 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8491 procmsg_msginfo_free( dummyinfo );
8494 static void compose_destroy(Compose *compose)
8496 GtkAllocation allocation;
8497 GtkTextBuffer *buffer;
8498 GtkClipboard *clipboard;
8500 compose_list = g_list_remove(compose_list, compose);
8502 if (compose->updating) {
8503 debug_print("danger, not destroying anything now\n");
8504 compose->deferred_destroy = TRUE;
8507 /* NOTE: address_completion_end() does nothing with the window
8508 * however this may change. */
8509 address_completion_end(compose->window);
8511 slist_free_strings(compose->to_list);
8512 g_slist_free(compose->to_list);
8513 slist_free_strings(compose->newsgroup_list);
8514 g_slist_free(compose->newsgroup_list);
8515 slist_free_strings(compose->header_list);
8516 g_slist_free(compose->header_list);
8518 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8520 g_hash_table_destroy(compose->email_hashtable);
8522 procmsg_msginfo_free(compose->targetinfo);
8523 procmsg_msginfo_free(compose->replyinfo);
8524 procmsg_msginfo_free(compose->fwdinfo);
8526 g_free(compose->replyto);
8527 g_free(compose->cc);
8528 g_free(compose->bcc);
8529 g_free(compose->newsgroups);
8530 g_free(compose->followup_to);
8532 g_free(compose->ml_post);
8534 g_free(compose->inreplyto);
8535 g_free(compose->references);
8536 g_free(compose->msgid);
8537 g_free(compose->boundary);
8539 g_free(compose->redirect_filename);
8540 if (compose->undostruct)
8541 undo_destroy(compose->undostruct);
8543 g_free(compose->sig_str);
8545 g_free(compose->exteditor_file);
8547 g_free(compose->orig_charset);
8549 g_free(compose->privacy_system);
8551 #ifndef USE_NEW_ADDRBOOK
8552 if (addressbook_get_target_compose() == compose)
8553 addressbook_set_target_compose(NULL);
8556 if (compose->gtkaspell) {
8557 gtkaspell_delete(compose->gtkaspell);
8558 compose->gtkaspell = NULL;
8562 if (!compose->batch) {
8563 gtk_widget_get_allocation(compose->window, &allocation);
8564 prefs_common.compose_width = allocation.width;
8565 prefs_common.compose_height = allocation.height;
8568 if (!gtk_widget_get_parent(compose->paned))
8569 gtk_widget_destroy(compose->paned);
8570 gtk_widget_destroy(compose->popupmenu);
8572 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8573 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8574 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8576 gtk_widget_destroy(compose->window);
8577 toolbar_destroy(compose->toolbar);
8578 g_free(compose->toolbar);
8579 g_mutex_free(compose->mutex);
8583 static void compose_attach_info_free(AttachInfo *ainfo)
8585 g_free(ainfo->file);
8586 g_free(ainfo->content_type);
8587 g_free(ainfo->name);
8588 g_free(ainfo->charset);
8592 static void compose_attach_update_label(Compose *compose)
8597 GtkTreeModel *model;
8602 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8603 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8604 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8608 while(gtk_tree_model_iter_next(model, &iter))
8611 text = g_strdup_printf("(%d)", i);
8612 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8616 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8618 Compose *compose = (Compose *)data;
8619 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8620 GtkTreeSelection *selection;
8622 GtkTreeModel *model;
8624 selection = gtk_tree_view_get_selection(tree_view);
8625 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8630 for (cur = sel; cur != NULL; cur = cur->next) {
8631 GtkTreePath *path = cur->data;
8632 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8635 gtk_tree_path_free(path);
8638 for (cur = sel; cur != NULL; cur = cur->next) {
8639 GtkTreeRowReference *ref = cur->data;
8640 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8643 if (gtk_tree_model_get_iter(model, &iter, path))
8644 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8646 gtk_tree_path_free(path);
8647 gtk_tree_row_reference_free(ref);
8651 compose_attach_update_label(compose);
8654 static struct _AttachProperty
8657 GtkWidget *mimetype_entry;
8658 GtkWidget *encoding_optmenu;
8659 GtkWidget *path_entry;
8660 GtkWidget *filename_entry;
8662 GtkWidget *cancel_btn;
8665 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8667 gtk_tree_path_free((GtkTreePath *)ptr);
8670 static void compose_attach_property(GtkAction *action, gpointer data)
8672 Compose *compose = (Compose *)data;
8673 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8675 GtkComboBox *optmenu;
8676 GtkTreeSelection *selection;
8678 GtkTreeModel *model;
8681 static gboolean cancelled;
8683 /* only if one selected */
8684 selection = gtk_tree_view_get_selection(tree_view);
8685 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8688 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8692 path = (GtkTreePath *) sel->data;
8693 gtk_tree_model_get_iter(model, &iter, path);
8694 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8697 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8703 if (!attach_prop.window)
8704 compose_attach_property_create(&cancelled);
8705 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8706 gtk_widget_grab_focus(attach_prop.ok_btn);
8707 gtk_widget_show(attach_prop.window);
8708 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8710 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8711 if (ainfo->encoding == ENC_UNKNOWN)
8712 combobox_select_by_data(optmenu, ENC_BASE64);
8714 combobox_select_by_data(optmenu, ainfo->encoding);
8716 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8717 ainfo->content_type ? ainfo->content_type : "");
8718 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8719 ainfo->file ? ainfo->file : "");
8720 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8721 ainfo->name ? ainfo->name : "");
8724 const gchar *entry_text;
8726 gchar *cnttype = NULL;
8733 gtk_widget_hide(attach_prop.window);
8734 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8739 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8740 if (*entry_text != '\0') {
8743 text = g_strstrip(g_strdup(entry_text));
8744 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8745 cnttype = g_strdup(text);
8748 alertpanel_error(_("Invalid MIME type."));
8754 ainfo->encoding = combobox_get_active_data(optmenu);
8756 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8757 if (*entry_text != '\0') {
8758 if (is_file_exist(entry_text) &&
8759 (size = get_file_size(entry_text)) > 0)
8760 file = g_strdup(entry_text);
8763 (_("File doesn't exist or is empty."));
8769 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8770 if (*entry_text != '\0') {
8771 g_free(ainfo->name);
8772 ainfo->name = g_strdup(entry_text);
8776 g_free(ainfo->content_type);
8777 ainfo->content_type = cnttype;
8780 g_free(ainfo->file);
8784 ainfo->size = (goffset)size;
8786 /* update tree store */
8787 text = to_human_readable(ainfo->size);
8788 gtk_tree_model_get_iter(model, &iter, path);
8789 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8790 COL_MIMETYPE, ainfo->content_type,
8792 COL_NAME, ainfo->name,
8793 COL_CHARSET, ainfo->charset,
8799 gtk_tree_path_free(path);
8802 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8804 label = gtk_label_new(str); \
8805 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8806 GTK_FILL, 0, 0, 0); \
8807 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8809 entry = gtk_entry_new(); \
8810 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8811 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8814 static void compose_attach_property_create(gboolean *cancelled)
8820 GtkWidget *mimetype_entry;
8823 GtkListStore *optmenu_menu;
8824 GtkWidget *path_entry;
8825 GtkWidget *filename_entry;
8828 GtkWidget *cancel_btn;
8829 GList *mime_type_list, *strlist;
8832 debug_print("Creating attach_property window...\n");
8834 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8835 gtk_widget_set_size_request(window, 480, -1);
8836 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8837 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8838 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8839 g_signal_connect(G_OBJECT(window), "delete_event",
8840 G_CALLBACK(attach_property_delete_event),
8842 g_signal_connect(G_OBJECT(window), "key_press_event",
8843 G_CALLBACK(attach_property_key_pressed),
8846 vbox = gtk_vbox_new(FALSE, 8);
8847 gtk_container_add(GTK_CONTAINER(window), vbox);
8849 table = gtk_table_new(4, 2, FALSE);
8850 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8851 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8852 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8854 label = gtk_label_new(_("MIME type"));
8855 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8857 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8858 mimetype_entry = gtk_combo_box_entry_new_text();
8859 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8860 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8862 /* stuff with list */
8863 mime_type_list = procmime_get_mime_type_list();
8865 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8866 MimeType *type = (MimeType *) mime_type_list->data;
8869 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8871 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8874 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8875 (GCompareFunc)strcmp2);
8878 for (mime_type_list = strlist; mime_type_list != NULL;
8879 mime_type_list = mime_type_list->next) {
8880 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8881 g_free(mime_type_list->data);
8883 g_list_free(strlist);
8884 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8885 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8887 label = gtk_label_new(_("Encoding"));
8888 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8890 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8892 hbox = gtk_hbox_new(FALSE, 0);
8893 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8894 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8896 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8897 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8899 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8900 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8901 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8902 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8903 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8905 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8907 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8908 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8910 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8911 &ok_btn, GTK_STOCK_OK,
8913 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8914 gtk_widget_grab_default(ok_btn);
8916 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8917 G_CALLBACK(attach_property_ok),
8919 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8920 G_CALLBACK(attach_property_cancel),
8923 gtk_widget_show_all(vbox);
8925 attach_prop.window = window;
8926 attach_prop.mimetype_entry = mimetype_entry;
8927 attach_prop.encoding_optmenu = optmenu;
8928 attach_prop.path_entry = path_entry;
8929 attach_prop.filename_entry = filename_entry;
8930 attach_prop.ok_btn = ok_btn;
8931 attach_prop.cancel_btn = cancel_btn;
8934 #undef SET_LABEL_AND_ENTRY
8936 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8942 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8948 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8949 gboolean *cancelled)
8957 static gboolean attach_property_key_pressed(GtkWidget *widget,
8959 gboolean *cancelled)
8961 if (event && event->keyval == GDK_KEY_Escape) {
8965 if (event && event->keyval == GDK_KEY_Return) {
8973 static void compose_exec_ext_editor(Compose *compose)
8980 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8981 G_DIR_SEPARATOR, compose);
8983 if (pipe(pipe_fds) < 0) {
8989 if ((pid = fork()) < 0) {
8996 /* close the write side of the pipe */
8999 compose->exteditor_file = g_strdup(tmp);
9000 compose->exteditor_pid = pid;
9002 compose_set_ext_editor_sensitive(compose, FALSE);
9005 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
9007 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
9009 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
9013 } else { /* process-monitoring process */
9019 /* close the read side of the pipe */
9022 if (compose_write_body_to_file(compose, tmp) < 0) {
9023 fd_write_all(pipe_fds[1], "2\n", 2);
9027 pid_ed = compose_exec_ext_editor_real(tmp);
9029 fd_write_all(pipe_fds[1], "1\n", 2);
9033 /* wait until editor is terminated */
9034 waitpid(pid_ed, NULL, 0);
9036 fd_write_all(pipe_fds[1], "0\n", 2);
9043 #endif /* G_OS_UNIX */
9047 static gint compose_exec_ext_editor_real(const gchar *file)
9054 cm_return_val_if_fail(file != NULL, -1);
9056 if ((pid = fork()) < 0) {
9061 if (pid != 0) return pid;
9063 /* grandchild process */
9065 if (setpgid(0, getppid()))
9068 if (prefs_common_get_ext_editor_cmd() &&
9069 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
9070 *(p + 1) == 's' && !strchr(p + 2, '%')) {
9071 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
9073 if (prefs_common_get_ext_editor_cmd())
9074 g_warning("External editor command-line is invalid: '%s'\n",
9075 prefs_common_get_ext_editor_cmd());
9076 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
9079 cmdline = strsplit_with_quote(buf, " ", 1024);
9080 execvp(cmdline[0], cmdline);
9083 g_strfreev(cmdline);
9088 static gboolean compose_ext_editor_kill(Compose *compose)
9090 pid_t pgid = compose->exteditor_pid * -1;
9093 ret = kill(pgid, 0);
9095 if (ret == 0 || (ret == -1 && EPERM == errno)) {
9099 msg = g_strdup_printf
9100 (_("The external editor is still working.\n"
9101 "Force terminating the process?\n"
9102 "process group id: %d"), -pgid);
9103 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
9104 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
9108 if (val == G_ALERTALTERNATE) {
9109 g_source_remove(compose->exteditor_tag);
9110 g_io_channel_shutdown(compose->exteditor_ch,
9112 g_io_channel_unref(compose->exteditor_ch);
9114 if (kill(pgid, SIGTERM) < 0) perror("kill");
9115 waitpid(compose->exteditor_pid, NULL, 0);
9117 g_warning("Terminated process group id: %d", -pgid);
9118 g_warning("Temporary file: %s",
9119 compose->exteditor_file);
9121 compose_set_ext_editor_sensitive(compose, TRUE);
9123 g_free(compose->exteditor_file);
9124 compose->exteditor_file = NULL;
9125 compose->exteditor_pid = -1;
9126 compose->exteditor_ch = NULL;
9127 compose->exteditor_tag = -1;
9135 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
9139 Compose *compose = (Compose *)data;
9142 debug_print("Compose: input from monitoring process\n");
9144 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
9146 g_io_channel_shutdown(source, FALSE, NULL);
9147 g_io_channel_unref(source);
9149 waitpid(compose->exteditor_pid, NULL, 0);
9151 if (buf[0] == '0') { /* success */
9152 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9153 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9155 gtk_text_buffer_set_text(buffer, "", -1);
9156 compose_insert_file(compose, compose->exteditor_file);
9157 compose_changed_cb(NULL, compose);
9158 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9160 if (claws_unlink(compose->exteditor_file) < 0)
9161 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9162 } else if (buf[0] == '1') { /* failed */
9163 g_warning("Couldn't exec external editor\n");
9164 if (claws_unlink(compose->exteditor_file) < 0)
9165 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9166 } else if (buf[0] == '2') {
9167 g_warning("Couldn't write to file\n");
9168 } else if (buf[0] == '3') {
9169 g_warning("Pipe read failed\n");
9172 compose_set_ext_editor_sensitive(compose, TRUE);
9174 g_free(compose->exteditor_file);
9175 compose->exteditor_file = NULL;
9176 compose->exteditor_pid = -1;
9177 compose->exteditor_ch = NULL;
9178 compose->exteditor_tag = -1;
9183 static void compose_set_ext_editor_sensitive(Compose *compose,
9186 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9187 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9188 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9189 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9190 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9191 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9192 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9194 gtk_widget_set_sensitive(compose->text, sensitive);
9195 if (compose->toolbar->send_btn)
9196 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
9197 if (compose->toolbar->sendl_btn)
9198 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
9199 if (compose->toolbar->draft_btn)
9200 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
9201 if (compose->toolbar->insert_btn)
9202 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
9203 if (compose->toolbar->sig_btn)
9204 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
9205 if (compose->toolbar->exteditor_btn)
9206 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9207 if (compose->toolbar->linewrap_current_btn)
9208 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9209 if (compose->toolbar->linewrap_all_btn)
9210 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9212 #endif /* G_OS_UNIX */
9215 * compose_undo_state_changed:
9217 * Change the sensivity of the menuentries undo and redo
9219 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9220 gint redo_state, gpointer data)
9222 Compose *compose = (Compose *)data;
9224 switch (undo_state) {
9225 case UNDO_STATE_TRUE:
9226 if (!undostruct->undo_state) {
9227 undostruct->undo_state = TRUE;
9228 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9231 case UNDO_STATE_FALSE:
9232 if (undostruct->undo_state) {
9233 undostruct->undo_state = FALSE;
9234 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9237 case UNDO_STATE_UNCHANGED:
9239 case UNDO_STATE_REFRESH:
9240 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9243 g_warning("Undo state not recognized");
9247 switch (redo_state) {
9248 case UNDO_STATE_TRUE:
9249 if (!undostruct->redo_state) {
9250 undostruct->redo_state = TRUE;
9251 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9254 case UNDO_STATE_FALSE:
9255 if (undostruct->redo_state) {
9256 undostruct->redo_state = FALSE;
9257 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9260 case UNDO_STATE_UNCHANGED:
9262 case UNDO_STATE_REFRESH:
9263 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9266 g_warning("Redo state not recognized");
9271 /* callback functions */
9273 static void compose_notebook_size_alloc(GtkNotebook *notebook,
9274 GtkAllocation *allocation,
9277 prefs_common.compose_notebook_height = allocation->height;
9280 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9281 * includes "non-client" (windows-izm) in calculation, so this calculation
9282 * may not be accurate.
9284 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9285 GtkAllocation *allocation,
9286 GtkSHRuler *shruler)
9288 if (prefs_common.show_ruler) {
9289 gint char_width = 0, char_height = 0;
9290 gint line_width_in_chars;
9292 gtkut_get_font_size(GTK_WIDGET(widget),
9293 &char_width, &char_height);
9294 line_width_in_chars =
9295 (allocation->width - allocation->x) / char_width;
9297 /* got the maximum */
9298 gtk_shruler_set_range(GTK_SHRULER(shruler),
9299 0.0, line_width_in_chars, 0);
9308 ComposePrefType type;
9309 gboolean entry_marked;
9312 static void account_activated(GtkComboBox *optmenu, gpointer data)
9314 Compose *compose = (Compose *)data;
9317 gchar *folderidentifier;
9318 gint account_id = 0;
9321 GSList *list, *saved_list = NULL;
9322 HeaderEntryState *state;
9323 GtkRcStyle *style = NULL;
9324 static GdkColor yellow;
9325 static gboolean color_set = FALSE;
9327 /* Get ID of active account in the combo box */
9328 menu = gtk_combo_box_get_model(optmenu);
9329 gtk_combo_box_get_active_iter(optmenu, &iter);
9330 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9332 ac = account_find_from_id(account_id);
9333 cm_return_if_fail(ac != NULL);
9335 if (ac != compose->account) {
9336 compose_select_account(compose, ac, FALSE);
9338 for (list = compose->header_list; list; list = list->next) {
9339 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9341 if (hentry->type == PREF_ACCOUNT || !list->next) {
9342 compose_destroy_headerentry(compose, hentry);
9346 state = g_malloc0(sizeof(HeaderEntryState));
9347 state->header = gtk_editable_get_chars(GTK_EDITABLE(
9348 gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9349 state->entry = gtk_editable_get_chars(
9350 GTK_EDITABLE(hentry->entry), 0, -1);
9351 state->type = hentry->type;
9354 gdk_color_parse("#f5f6be", &yellow);
9355 color_set = gdk_colormap_alloc_color(
9356 gdk_colormap_get_system(),
9357 &yellow, FALSE, TRUE);
9360 style = gtk_widget_get_modifier_style(hentry->entry);
9361 state->entry_marked = gdk_color_equal(&yellow,
9362 &style->base[GTK_STATE_NORMAL]);
9364 saved_list = g_slist_append(saved_list, state);
9365 compose_destroy_headerentry(compose, hentry);
9368 compose->header_last = NULL;
9369 g_slist_free(compose->header_list);
9370 compose->header_list = NULL;
9371 compose->header_nextrow = 1;
9372 compose_create_header_entry(compose);
9374 if (ac->set_autocc && ac->auto_cc)
9375 compose_entry_append(compose, ac->auto_cc,
9376 COMPOSE_CC, PREF_ACCOUNT);
9378 if (ac->set_autobcc && ac->auto_bcc)
9379 compose_entry_append(compose, ac->auto_bcc,
9380 COMPOSE_BCC, PREF_ACCOUNT);
9382 if (ac->set_autoreplyto && ac->auto_replyto)
9383 compose_entry_append(compose, ac->auto_replyto,
9384 COMPOSE_REPLYTO, PREF_ACCOUNT);
9386 for (list = saved_list; list; list = list->next) {
9387 state = (HeaderEntryState *) list->data;
9389 compose_add_header_entry(compose, state->header,
9390 state->entry, state->type);
9391 if (state->entry_marked)
9392 compose_entry_mark_default_to(compose, state->entry);
9394 g_free(state->header);
9395 g_free(state->entry);
9398 g_slist_free(saved_list);
9400 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9401 (ac->protocol == A_NNTP) ?
9402 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9405 /* Set message save folder */
9406 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9407 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9409 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9410 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9412 compose_set_save_to(compose, NULL);
9413 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9414 folderidentifier = folder_item_get_identifier(account_get_special_folder
9415 (compose->account, F_OUTBOX));
9416 compose_set_save_to(compose, folderidentifier);
9417 g_free(folderidentifier);
9421 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9422 GtkTreeViewColumn *column, Compose *compose)
9424 compose_attach_property(NULL, compose);
9427 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9430 Compose *compose = (Compose *)data;
9431 GtkTreeSelection *attach_selection;
9432 gint attach_nr_selected;
9434 if (!event) return FALSE;
9436 if (event->button == 3) {
9437 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9438 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9440 if (attach_nr_selected > 0)
9442 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9443 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9445 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9446 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9449 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9450 NULL, NULL, event->button, event->time);
9457 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9460 Compose *compose = (Compose *)data;
9462 if (!event) return FALSE;
9464 switch (event->keyval) {
9465 case GDK_KEY_Delete:
9466 compose_attach_remove_selected(NULL, compose);
9472 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9474 toolbar_comp_set_sensitive(compose, allow);
9475 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9476 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9478 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9480 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9481 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9482 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9484 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9488 static void compose_send_cb(GtkAction *action, gpointer data)
9490 Compose *compose = (Compose *)data;
9492 if (prefs_common.work_offline &&
9493 !inc_offline_should_override(TRUE,
9494 _("Claws Mail needs network access in order "
9495 "to send this email.")))
9498 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9499 g_source_remove(compose->draft_timeout_tag);
9500 compose->draft_timeout_tag = -1;
9503 compose_send(compose);
9506 static void compose_send_later_cb(GtkAction *action, gpointer data)
9508 Compose *compose = (Compose *)data;
9512 compose_allow_user_actions(compose, FALSE);
9513 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9514 compose_allow_user_actions(compose, TRUE);
9518 compose_close(compose);
9519 } else if (val == -1) {
9520 alertpanel_error(_("Could not queue message."));
9521 } else if (val == -2) {
9522 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9523 } else if (val == -3) {
9524 if (privacy_peek_error())
9525 alertpanel_error(_("Could not queue message for sending:\n\n"
9526 "Signature failed: %s"), privacy_get_error());
9527 } else if (val == -4) {
9528 alertpanel_error(_("Could not queue message for sending:\n\n"
9529 "Charset conversion failed."));
9530 } else if (val == -5) {
9531 alertpanel_error(_("Could not queue message for sending:\n\n"
9532 "Couldn't get recipient encryption key."));
9533 } else if (val == -6) {
9536 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9539 #define DRAFTED_AT_EXIT "drafted_at_exit"
9540 static void compose_register_draft(MsgInfo *info)
9542 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9543 DRAFTED_AT_EXIT, NULL);
9544 FILE *fp = g_fopen(filepath, "ab");
9547 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9555 gboolean compose_draft (gpointer data, guint action)
9557 Compose *compose = (Compose *)data;
9562 MsgFlags flag = {0, 0};
9563 static gboolean lock = FALSE;
9564 MsgInfo *newmsginfo;
9566 gboolean target_locked = FALSE;
9567 gboolean err = FALSE;
9569 if (lock) return FALSE;
9571 if (compose->sending)
9574 draft = account_get_special_folder(compose->account, F_DRAFT);
9575 cm_return_val_if_fail(draft != NULL, FALSE);
9577 if (!g_mutex_trylock(compose->mutex)) {
9578 /* we don't want to lock the mutex once it's available,
9579 * because as the only other part of compose.c locking
9580 * it is compose_close - which means once unlocked,
9581 * the compose struct will be freed */
9582 debug_print("couldn't lock mutex, probably sending\n");
9588 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9589 G_DIR_SEPARATOR, compose);
9590 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9591 FILE_OP_ERROR(tmp, "fopen");
9595 /* chmod for security */
9596 if (change_file_mode_rw(fp, tmp) < 0) {
9597 FILE_OP_ERROR(tmp, "chmod");
9598 g_warning("can't change file mode\n");
9601 /* Save draft infos */
9602 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9603 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9605 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9606 gchar *savefolderid;
9608 savefolderid = compose_get_save_to(compose);
9609 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9610 g_free(savefolderid);
9612 if (compose->return_receipt) {
9613 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9615 if (compose->privacy_system) {
9616 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9617 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9618 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9621 /* Message-ID of message replying to */
9622 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9625 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9626 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9629 /* Message-ID of message forwarding to */
9630 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9633 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9634 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9638 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9639 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9641 sheaders = compose_get_manual_headers_info(compose);
9642 err |= (fprintf(fp, "X-Claws-Manual-Headers:%s\n", sheaders) < 0);
9645 /* end of headers */
9646 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9653 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9657 if (fclose(fp) == EOF) {
9661 if (compose->targetinfo) {
9662 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9663 flag.perm_flags = target_locked?MSG_LOCKED:0;
9665 flag.tmp_flags = MSG_DRAFT;
9667 folder_item_scan(draft);
9668 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9669 MsgInfo *tmpinfo = NULL;
9670 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9671 if (compose->msgid) {
9672 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9675 msgnum = tmpinfo->msgnum;
9676 procmsg_msginfo_free(tmpinfo);
9677 debug_print("got draft msgnum %d from scanning\n", msgnum);
9679 debug_print("didn't get draft msgnum after scanning\n");
9682 debug_print("got draft msgnum %d from adding\n", msgnum);
9688 if (action != COMPOSE_AUTO_SAVE) {
9689 if (action != COMPOSE_DRAFT_FOR_EXIT)
9690 alertpanel_error(_("Could not save draft."));
9693 gtkut_window_popup(compose->window);
9694 val = alertpanel_full(_("Could not save draft"),
9695 _("Could not save draft.\n"
9696 "Do you want to cancel exit or discard this email?"),
9697 _("_Cancel exit"), _("_Discard email"), NULL,
9698 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9699 if (val == G_ALERTALTERNATE) {
9701 g_mutex_unlock(compose->mutex); /* must be done before closing */
9702 compose_close(compose);
9706 g_mutex_unlock(compose->mutex); /* must be done before closing */
9715 if (compose->mode == COMPOSE_REEDIT) {
9716 compose_remove_reedit_target(compose, TRUE);
9719 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9722 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9724 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9726 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9727 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9728 procmsg_msginfo_set_flags(newmsginfo, 0,
9729 MSG_HAS_ATTACHMENT);
9731 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9732 compose_register_draft(newmsginfo);
9734 procmsg_msginfo_free(newmsginfo);
9737 folder_item_scan(draft);
9739 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9741 g_mutex_unlock(compose->mutex); /* must be done before closing */
9742 compose_close(compose);
9748 path = folder_item_fetch_msg(draft, msgnum);
9750 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9753 if (g_stat(path, &s) < 0) {
9754 FILE_OP_ERROR(path, "stat");
9760 procmsg_msginfo_free(compose->targetinfo);
9761 compose->targetinfo = procmsg_msginfo_new();
9762 compose->targetinfo->msgnum = msgnum;
9763 compose->targetinfo->size = (goffset)s.st_size;
9764 compose->targetinfo->mtime = s.st_mtime;
9765 compose->targetinfo->folder = draft;
9767 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9768 compose->mode = COMPOSE_REEDIT;
9770 if (action == COMPOSE_AUTO_SAVE) {
9771 compose->autosaved_draft = compose->targetinfo;
9773 compose->modified = FALSE;
9774 compose_set_title(compose);
9778 g_mutex_unlock(compose->mutex);
9782 void compose_clear_exit_drafts(void)
9784 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9785 DRAFTED_AT_EXIT, NULL);
9786 if (is_file_exist(filepath))
9787 claws_unlink(filepath);
9792 void compose_reopen_exit_drafts(void)
9794 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9795 DRAFTED_AT_EXIT, NULL);
9796 FILE *fp = g_fopen(filepath, "rb");
9800 while (fgets(buf, sizeof(buf), fp)) {
9801 gchar **parts = g_strsplit(buf, "\t", 2);
9802 const gchar *folder = parts[0];
9803 int msgnum = parts[1] ? atoi(parts[1]):-1;
9805 if (folder && *folder && msgnum > -1) {
9806 FolderItem *item = folder_find_item_from_identifier(folder);
9807 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9809 compose_reedit(info, FALSE);
9816 compose_clear_exit_drafts();
9819 static void compose_save_cb(GtkAction *action, gpointer data)
9821 Compose *compose = (Compose *)data;
9822 compose_draft(compose, COMPOSE_KEEP_EDITING);
9823 compose->rmode = COMPOSE_REEDIT;
9826 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9828 if (compose && file_list) {
9831 for ( tmp = file_list; tmp; tmp = tmp->next) {
9832 gchar *file = (gchar *) tmp->data;
9833 gchar *utf8_filename = conv_filename_to_utf8(file);
9834 compose_attach_append(compose, file, utf8_filename, NULL, NULL);
9835 compose_changed_cb(NULL, compose);
9840 g_free(utf8_filename);
9845 static void compose_attach_cb(GtkAction *action, gpointer data)
9847 Compose *compose = (Compose *)data;
9850 if (compose->redirect_filename != NULL)
9853 file_list = filesel_select_multiple_files_open(_("Select file"));
9856 compose_attach_from_list(compose, file_list, TRUE);
9857 g_list_free(file_list);
9861 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9863 Compose *compose = (Compose *)data;
9865 gint files_inserted = 0;
9867 file_list = filesel_select_multiple_files_open(_("Select file"));
9872 for ( tmp = file_list; tmp; tmp = tmp->next) {
9873 gchar *file = (gchar *) tmp->data;
9874 gchar *filedup = g_strdup(file);
9875 gchar *shortfile = g_path_get_basename(filedup);
9876 ComposeInsertResult res;
9877 /* insert the file if the file is short or if the user confirmed that
9878 he/she wants to insert the large file */
9879 res = compose_insert_file(compose, file);
9880 if (res == COMPOSE_INSERT_READ_ERROR) {
9881 alertpanel_error(_("File '%s' could not be read."), shortfile);
9882 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9883 alertpanel_error(_("File '%s' contained invalid characters\n"
9884 "for the current encoding, insertion may be incorrect."),
9886 } else if (res == COMPOSE_INSERT_SUCCESS)
9893 g_list_free(file_list);
9897 if (files_inserted > 0 && compose->gtkaspell &&
9898 compose->gtkaspell->check_while_typing)
9899 gtkaspell_highlight_all(compose->gtkaspell);
9903 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9905 Compose *compose = (Compose *)data;
9907 compose_insert_sig(compose, FALSE);
9910 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9914 Compose *compose = (Compose *)data;
9916 gtkut_widget_get_uposition(widget, &x, &y);
9917 if (!compose->batch) {
9918 prefs_common.compose_x = x;
9919 prefs_common.compose_y = y;
9921 if (compose->sending || compose->updating)
9923 compose_close_cb(NULL, compose);
9927 void compose_close_toolbar(Compose *compose)
9929 compose_close_cb(NULL, compose);
9932 static void compose_close_cb(GtkAction *action, gpointer data)
9934 Compose *compose = (Compose *)data;
9938 if (compose->exteditor_tag != -1) {
9939 if (!compose_ext_editor_kill(compose))
9944 if (compose->modified) {
9945 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9946 if (!g_mutex_trylock(compose->mutex)) {
9947 /* we don't want to lock the mutex once it's available,
9948 * because as the only other part of compose.c locking
9949 * it is compose_close - which means once unlocked,
9950 * the compose struct will be freed */
9951 debug_print("couldn't lock mutex, probably sending\n");
9955 val = alertpanel(_("Discard message"),
9956 _("This message has been modified. Discard it?"),
9957 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9959 val = alertpanel(_("Save changes"),
9960 _("This message has been modified. Save the latest changes?"),
9961 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9963 g_mutex_unlock(compose->mutex);
9965 case G_ALERTDEFAULT:
9966 if (prefs_common.autosave && !reedit)
9967 compose_remove_draft(compose);
9969 case G_ALERTALTERNATE:
9970 compose_draft(data, COMPOSE_QUIT_EDITING);
9977 compose_close(compose);
9980 static void compose_print_cb(GtkAction *action, gpointer data)
9982 Compose *compose = (Compose *) data;
9984 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9985 if (compose->targetinfo)
9986 messageview_print(compose->targetinfo, FALSE, -1, -1, 0);
9989 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9991 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9992 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9993 Compose *compose = (Compose *) data;
9996 compose->out_encoding = (CharSet)value;
9999 static void compose_address_cb(GtkAction *action, gpointer data)
10001 Compose *compose = (Compose *)data;
10003 #ifndef USE_NEW_ADDRBOOK
10004 addressbook_open(compose);
10006 GError* error = NULL;
10007 addressbook_connect_signals(compose);
10008 addressbook_dbus_open(TRUE, &error);
10010 g_warning("%s", error->message);
10011 g_error_free(error);
10016 static void about_show_cb(GtkAction *action, gpointer data)
10021 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
10023 Compose *compose = (Compose *)data;
10028 tmpl = g_object_get_data(G_OBJECT(widget), "template");
10029 cm_return_if_fail(tmpl != NULL);
10031 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
10033 val = alertpanel(_("Apply template"), msg,
10034 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
10037 if (val == G_ALERTDEFAULT)
10038 compose_template_apply(compose, tmpl, TRUE);
10039 else if (val == G_ALERTALTERNATE)
10040 compose_template_apply(compose, tmpl, FALSE);
10043 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
10045 Compose *compose = (Compose *)data;
10047 compose_exec_ext_editor(compose);
10050 static void compose_undo_cb(GtkAction *action, gpointer data)
10052 Compose *compose = (Compose *)data;
10053 gboolean prev_autowrap = compose->autowrap;
10055 compose->autowrap = FALSE;
10056 undo_undo(compose->undostruct);
10057 compose->autowrap = prev_autowrap;
10060 static void compose_redo_cb(GtkAction *action, gpointer data)
10062 Compose *compose = (Compose *)data;
10063 gboolean prev_autowrap = compose->autowrap;
10065 compose->autowrap = FALSE;
10066 undo_redo(compose->undostruct);
10067 compose->autowrap = prev_autowrap;
10070 static void entry_cut_clipboard(GtkWidget *entry)
10072 if (GTK_IS_EDITABLE(entry))
10073 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
10074 else if (GTK_IS_TEXT_VIEW(entry))
10075 gtk_text_buffer_cut_clipboard(
10076 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10077 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
10081 static void entry_copy_clipboard(GtkWidget *entry)
10083 if (GTK_IS_EDITABLE(entry))
10084 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
10085 else if (GTK_IS_TEXT_VIEW(entry))
10086 gtk_text_buffer_copy_clipboard(
10087 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10088 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
10091 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
10092 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
10094 if (GTK_IS_TEXT_VIEW(entry)) {
10095 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10096 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
10097 GtkTextIter start_iter, end_iter;
10099 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
10101 if (contents == NULL)
10104 /* we shouldn't delete the selection when middle-click-pasting, or we
10105 * can't mid-click-paste our own selection */
10106 if (clip != GDK_SELECTION_PRIMARY) {
10107 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
10108 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
10111 if (insert_place == NULL) {
10112 /* if insert_place isn't specified, insert at the cursor.
10113 * used for Ctrl-V pasting */
10114 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10115 start = gtk_text_iter_get_offset(&start_iter);
10116 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
10118 /* if insert_place is specified, paste here.
10119 * used for mid-click-pasting */
10120 start = gtk_text_iter_get_offset(insert_place);
10121 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
10122 if (prefs_common.primary_paste_unselects)
10123 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
10127 /* paste unwrapped: mark the paste so it's not wrapped later */
10128 end = start + strlen(contents);
10129 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
10130 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
10131 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
10132 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
10133 /* rewrap paragraph now (after a mid-click-paste) */
10134 mark_start = gtk_text_buffer_get_insert(buffer);
10135 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10136 gtk_text_iter_backward_char(&start_iter);
10137 compose_beautify_paragraph(compose, &start_iter, TRUE);
10139 } else if (GTK_IS_EDITABLE(entry))
10140 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
10142 compose->modified = TRUE;
10145 static void entry_allsel(GtkWidget *entry)
10147 if (GTK_IS_EDITABLE(entry))
10148 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
10149 else if (GTK_IS_TEXT_VIEW(entry)) {
10150 GtkTextIter startiter, enditer;
10151 GtkTextBuffer *textbuf;
10153 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10154 gtk_text_buffer_get_start_iter(textbuf, &startiter);
10155 gtk_text_buffer_get_end_iter(textbuf, &enditer);
10157 gtk_text_buffer_move_mark_by_name(textbuf,
10158 "selection_bound", &startiter);
10159 gtk_text_buffer_move_mark_by_name(textbuf,
10160 "insert", &enditer);
10164 static void compose_cut_cb(GtkAction *action, gpointer data)
10166 Compose *compose = (Compose *)data;
10167 if (compose->focused_editable
10168 #ifndef GENERIC_UMPC
10169 && gtk_widget_has_focus(compose->focused_editable)
10172 entry_cut_clipboard(compose->focused_editable);
10175 static void compose_copy_cb(GtkAction *action, gpointer data)
10177 Compose *compose = (Compose *)data;
10178 if (compose->focused_editable
10179 #ifndef GENERIC_UMPC
10180 && gtk_widget_has_focus(compose->focused_editable)
10183 entry_copy_clipboard(compose->focused_editable);
10186 static void compose_paste_cb(GtkAction *action, gpointer data)
10188 Compose *compose = (Compose *)data;
10189 gint prev_autowrap;
10190 GtkTextBuffer *buffer;
10192 if (compose->focused_editable &&
10193 #ifndef GENERIC_UMPC
10194 gtk_widget_has_focus(compose->focused_editable)
10197 entry_paste_clipboard(compose, compose->focused_editable,
10198 prefs_common.linewrap_pastes,
10199 GDK_SELECTION_CLIPBOARD, NULL);
10204 #ifndef GENERIC_UMPC
10205 gtk_widget_has_focus(compose->text) &&
10207 compose->gtkaspell &&
10208 compose->gtkaspell->check_while_typing)
10209 gtkaspell_highlight_all(compose->gtkaspell);
10213 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10215 Compose *compose = (Compose *)data;
10216 gint wrap_quote = prefs_common.linewrap_quote;
10217 if (compose->focused_editable
10218 #ifndef GENERIC_UMPC
10219 && gtk_widget_has_focus(compose->focused_editable)
10222 /* let text_insert() (called directly or at a later time
10223 * after the gtk_editable_paste_clipboard) know that
10224 * text is to be inserted as a quotation. implemented
10225 * by using a simple refcount... */
10226 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10227 G_OBJECT(compose->focused_editable),
10228 "paste_as_quotation"));
10229 g_object_set_data(G_OBJECT(compose->focused_editable),
10230 "paste_as_quotation",
10231 GINT_TO_POINTER(paste_as_quotation + 1));
10232 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10233 entry_paste_clipboard(compose, compose->focused_editable,
10234 prefs_common.linewrap_pastes,
10235 GDK_SELECTION_CLIPBOARD, NULL);
10236 prefs_common.linewrap_quote = wrap_quote;
10240 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10242 Compose *compose = (Compose *)data;
10243 gint prev_autowrap;
10244 GtkTextBuffer *buffer;
10246 if (compose->focused_editable
10247 #ifndef GENERIC_UMPC
10248 && gtk_widget_has_focus(compose->focused_editable)
10251 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10252 GDK_SELECTION_CLIPBOARD, NULL);
10257 #ifndef GENERIC_UMPC
10258 gtk_widget_has_focus(compose->text) &&
10260 compose->gtkaspell &&
10261 compose->gtkaspell->check_while_typing)
10262 gtkaspell_highlight_all(compose->gtkaspell);
10266 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10268 Compose *compose = (Compose *)data;
10269 gint prev_autowrap;
10270 GtkTextBuffer *buffer;
10272 if (compose->focused_editable
10273 #ifndef GENERIC_UMPC
10274 && gtk_widget_has_focus(compose->focused_editable)
10277 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10278 GDK_SELECTION_CLIPBOARD, NULL);
10283 #ifndef GENERIC_UMPC
10284 gtk_widget_has_focus(compose->text) &&
10286 compose->gtkaspell &&
10287 compose->gtkaspell->check_while_typing)
10288 gtkaspell_highlight_all(compose->gtkaspell);
10292 static void compose_allsel_cb(GtkAction *action, gpointer data)
10294 Compose *compose = (Compose *)data;
10295 if (compose->focused_editable
10296 #ifndef GENERIC_UMPC
10297 && gtk_widget_has_focus(compose->focused_editable)
10300 entry_allsel(compose->focused_editable);
10303 static void textview_move_beginning_of_line (GtkTextView *text)
10305 GtkTextBuffer *buffer;
10309 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10311 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10312 mark = gtk_text_buffer_get_insert(buffer);
10313 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10314 gtk_text_iter_set_line_offset(&ins, 0);
10315 gtk_text_buffer_place_cursor(buffer, &ins);
10318 static void textview_move_forward_character (GtkTextView *text)
10320 GtkTextBuffer *buffer;
10324 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10326 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10327 mark = gtk_text_buffer_get_insert(buffer);
10328 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10329 if (gtk_text_iter_forward_cursor_position(&ins))
10330 gtk_text_buffer_place_cursor(buffer, &ins);
10333 static void textview_move_backward_character (GtkTextView *text)
10335 GtkTextBuffer *buffer;
10339 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10341 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10342 mark = gtk_text_buffer_get_insert(buffer);
10343 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10344 if (gtk_text_iter_backward_cursor_position(&ins))
10345 gtk_text_buffer_place_cursor(buffer, &ins);
10348 static void textview_move_forward_word (GtkTextView *text)
10350 GtkTextBuffer *buffer;
10355 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10357 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10358 mark = gtk_text_buffer_get_insert(buffer);
10359 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10360 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10361 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10362 gtk_text_iter_backward_word_start(&ins);
10363 gtk_text_buffer_place_cursor(buffer, &ins);
10367 static void textview_move_backward_word (GtkTextView *text)
10369 GtkTextBuffer *buffer;
10374 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10376 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10377 mark = gtk_text_buffer_get_insert(buffer);
10378 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10379 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10380 if (gtk_text_iter_backward_word_starts(&ins, 1))
10381 gtk_text_buffer_place_cursor(buffer, &ins);
10384 static void textview_move_end_of_line (GtkTextView *text)
10386 GtkTextBuffer *buffer;
10390 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10392 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10393 mark = gtk_text_buffer_get_insert(buffer);
10394 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10395 if (gtk_text_iter_forward_to_line_end(&ins))
10396 gtk_text_buffer_place_cursor(buffer, &ins);
10399 static void textview_move_next_line (GtkTextView *text)
10401 GtkTextBuffer *buffer;
10406 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10408 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10409 mark = gtk_text_buffer_get_insert(buffer);
10410 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10411 offset = gtk_text_iter_get_line_offset(&ins);
10412 if (gtk_text_iter_forward_line(&ins)) {
10413 gtk_text_iter_set_line_offset(&ins, offset);
10414 gtk_text_buffer_place_cursor(buffer, &ins);
10418 static void textview_move_previous_line (GtkTextView *text)
10420 GtkTextBuffer *buffer;
10425 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10427 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10428 mark = gtk_text_buffer_get_insert(buffer);
10429 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10430 offset = gtk_text_iter_get_line_offset(&ins);
10431 if (gtk_text_iter_backward_line(&ins)) {
10432 gtk_text_iter_set_line_offset(&ins, offset);
10433 gtk_text_buffer_place_cursor(buffer, &ins);
10437 static void textview_delete_forward_character (GtkTextView *text)
10439 GtkTextBuffer *buffer;
10441 GtkTextIter ins, end_iter;
10443 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10445 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10446 mark = gtk_text_buffer_get_insert(buffer);
10447 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10449 if (gtk_text_iter_forward_char(&end_iter)) {
10450 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10454 static void textview_delete_backward_character (GtkTextView *text)
10456 GtkTextBuffer *buffer;
10458 GtkTextIter ins, end_iter;
10460 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10462 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10463 mark = gtk_text_buffer_get_insert(buffer);
10464 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10466 if (gtk_text_iter_backward_char(&end_iter)) {
10467 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10471 static void textview_delete_forward_word (GtkTextView *text)
10473 GtkTextBuffer *buffer;
10475 GtkTextIter ins, end_iter;
10477 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10479 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10480 mark = gtk_text_buffer_get_insert(buffer);
10481 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10483 if (gtk_text_iter_forward_word_end(&end_iter)) {
10484 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10488 static void textview_delete_backward_word (GtkTextView *text)
10490 GtkTextBuffer *buffer;
10492 GtkTextIter ins, end_iter;
10494 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10496 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10497 mark = gtk_text_buffer_get_insert(buffer);
10498 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10500 if (gtk_text_iter_backward_word_start(&end_iter)) {
10501 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10505 static void textview_delete_line (GtkTextView *text)
10507 GtkTextBuffer *buffer;
10509 GtkTextIter ins, start_iter, end_iter;
10511 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10513 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10514 mark = gtk_text_buffer_get_insert(buffer);
10515 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10518 gtk_text_iter_set_line_offset(&start_iter, 0);
10521 if (gtk_text_iter_ends_line(&end_iter)){
10522 if (!gtk_text_iter_forward_char(&end_iter))
10523 gtk_text_iter_backward_char(&start_iter);
10526 gtk_text_iter_forward_to_line_end(&end_iter);
10527 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10530 static void textview_delete_to_line_end (GtkTextView *text)
10532 GtkTextBuffer *buffer;
10534 GtkTextIter ins, end_iter;
10536 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10538 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10539 mark = gtk_text_buffer_get_insert(buffer);
10540 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10542 if (gtk_text_iter_ends_line(&end_iter))
10543 gtk_text_iter_forward_char(&end_iter);
10545 gtk_text_iter_forward_to_line_end(&end_iter);
10546 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10549 #define DO_ACTION(name, act) { \
10550 if(!strcmp(name, a_name)) { \
10554 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10556 const gchar *a_name = gtk_action_get_name(action);
10557 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10558 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10559 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10560 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10561 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10562 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10563 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10564 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10565 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10566 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10567 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10568 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10569 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10570 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10574 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10576 Compose *compose = (Compose *)data;
10577 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10578 ComposeCallAdvancedAction action = -1;
10580 action = compose_call_advanced_action_from_path(gaction);
10583 void (*do_action) (GtkTextView *text);
10584 } action_table[] = {
10585 {textview_move_beginning_of_line},
10586 {textview_move_forward_character},
10587 {textview_move_backward_character},
10588 {textview_move_forward_word},
10589 {textview_move_backward_word},
10590 {textview_move_end_of_line},
10591 {textview_move_next_line},
10592 {textview_move_previous_line},
10593 {textview_delete_forward_character},
10594 {textview_delete_backward_character},
10595 {textview_delete_forward_word},
10596 {textview_delete_backward_word},
10597 {textview_delete_line},
10598 {textview_delete_to_line_end}
10601 if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
10603 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10604 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10605 if (action_table[action].do_action)
10606 action_table[action].do_action(text);
10608 g_warning("Not implemented yet.");
10612 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10614 GtkAllocation allocation;
10618 if (GTK_IS_EDITABLE(widget)) {
10619 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10620 gtk_editable_set_position(GTK_EDITABLE(widget),
10623 if ((parent = gtk_widget_get_parent(widget))
10624 && (parent = gtk_widget_get_parent(parent))
10625 && (parent = gtk_widget_get_parent(parent))) {
10626 if (GTK_IS_SCROLLED_WINDOW(parent)) {
10627 gtk_widget_get_allocation(widget, &allocation);
10628 gint y = allocation.y;
10629 gint height = allocation.height;
10630 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10631 (GTK_SCROLLED_WINDOW(parent));
10633 gfloat value = gtk_adjustment_get_value(shown);
10634 gfloat upper = gtk_adjustment_get_upper(shown);
10635 gfloat page_size = gtk_adjustment_get_page_size(shown);
10636 if (y < (int)value) {
10637 gtk_adjustment_set_value(shown, y - 1);
10639 if ((y + height) > ((int)value + (int)page_size)) {
10640 if ((y - height - 1) < ((int)upper - (int)page_size)) {
10641 gtk_adjustment_set_value(shown,
10642 y + height - (int)page_size - 1);
10644 gtk_adjustment_set_value(shown,
10645 (int)upper - (int)page_size - 1);
10652 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10653 compose->focused_editable = widget;
10655 #ifdef GENERIC_UMPC
10656 if (GTK_IS_TEXT_VIEW(widget)
10657 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10658 g_object_ref(compose->notebook);
10659 g_object_ref(compose->edit_vbox);
10660 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10661 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10662 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10663 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10664 g_object_unref(compose->notebook);
10665 g_object_unref(compose->edit_vbox);
10666 g_signal_handlers_block_by_func(G_OBJECT(widget),
10667 G_CALLBACK(compose_grab_focus_cb),
10669 gtk_widget_grab_focus(widget);
10670 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10671 G_CALLBACK(compose_grab_focus_cb),
10673 } else if (!GTK_IS_TEXT_VIEW(widget)
10674 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10675 g_object_ref(compose->notebook);
10676 g_object_ref(compose->edit_vbox);
10677 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10678 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10679 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10680 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10681 g_object_unref(compose->notebook);
10682 g_object_unref(compose->edit_vbox);
10683 g_signal_handlers_block_by_func(G_OBJECT(widget),
10684 G_CALLBACK(compose_grab_focus_cb),
10686 gtk_widget_grab_focus(widget);
10687 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10688 G_CALLBACK(compose_grab_focus_cb),
10694 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10696 compose->modified = TRUE;
10697 // compose_beautify_paragraph(compose, NULL, TRUE);
10698 #ifndef GENERIC_UMPC
10699 compose_set_title(compose);
10703 static void compose_wrap_cb(GtkAction *action, gpointer data)
10705 Compose *compose = (Compose *)data;
10706 compose_beautify_paragraph(compose, NULL, TRUE);
10709 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10711 Compose *compose = (Compose *)data;
10712 compose_wrap_all_full(compose, TRUE);
10715 static void compose_find_cb(GtkAction *action, gpointer data)
10717 Compose *compose = (Compose *)data;
10719 message_search_compose(compose);
10722 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10725 Compose *compose = (Compose *)data;
10726 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10727 if (compose->autowrap)
10728 compose_wrap_all_full(compose, TRUE);
10729 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10732 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10735 Compose *compose = (Compose *)data;
10736 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10739 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10741 Compose *compose = (Compose *)data;
10743 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10746 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10748 Compose *compose = (Compose *)data;
10750 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10753 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10755 g_free(compose->privacy_system);
10757 compose->privacy_system = g_strdup(account->default_privacy_system);
10758 compose_update_privacy_system_menu_item(compose, warn);
10761 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10763 Compose *compose = (Compose *)data;
10765 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10766 gtk_widget_show(compose->ruler_hbox);
10767 prefs_common.show_ruler = TRUE;
10769 gtk_widget_hide(compose->ruler_hbox);
10770 gtk_widget_queue_resize(compose->edit_vbox);
10771 prefs_common.show_ruler = FALSE;
10775 static void compose_attach_drag_received_cb (GtkWidget *widget,
10776 GdkDragContext *context,
10779 GtkSelectionData *data,
10782 gpointer user_data)
10784 Compose *compose = (Compose *)user_data;
10788 type = gtk_selection_data_get_data_type(data);
10789 if (((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
10791 || (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND"))
10793 ) && gtk_drag_get_source_widget(context) !=
10794 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10795 list = uri_list_extract_filenames(
10796 (const gchar *)gtk_selection_data_get_data(data));
10797 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10798 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10799 compose_attach_append
10800 (compose, (const gchar *)tmp->data,
10801 utf8_filename, NULL, NULL);
10802 g_free(utf8_filename);
10804 if (list) compose_changed_cb(NULL, compose);
10805 list_free_strings(list);
10807 } else if (gtk_drag_get_source_widget(context)
10808 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10809 /* comes from our summaryview */
10810 SummaryView * summaryview = NULL;
10811 GSList * list = NULL, *cur = NULL;
10813 if (mainwindow_get_mainwindow())
10814 summaryview = mainwindow_get_mainwindow()->summaryview;
10817 list = summary_get_selected_msg_list(summaryview);
10819 for (cur = list; cur; cur = cur->next) {
10820 MsgInfo *msginfo = (MsgInfo *)cur->data;
10821 gchar *file = NULL;
10823 file = procmsg_get_message_file_full(msginfo,
10826 compose_attach_append(compose, (const gchar *)file,
10827 (const gchar *)file, "message/rfc822", NULL);
10831 g_slist_free(list);
10835 static gboolean compose_drag_drop(GtkWidget *widget,
10836 GdkDragContext *drag_context,
10838 guint time, gpointer user_data)
10840 /* not handling this signal makes compose_insert_drag_received_cb
10845 static gboolean completion_set_focus_to_subject
10846 (GtkWidget *widget,
10847 GdkEventKey *event,
10850 cm_return_val_if_fail(compose != NULL, FALSE);
10852 /* make backtab move to subject field */
10853 if(event->keyval == GDK_KEY_ISO_Left_Tab) {
10854 gtk_widget_grab_focus(compose->subject_entry);
10860 static void compose_insert_drag_received_cb (GtkWidget *widget,
10861 GdkDragContext *drag_context,
10864 GtkSelectionData *data,
10867 gpointer user_data)
10869 Compose *compose = (Compose *)user_data;
10873 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10875 type = gtk_selection_data_get_data_type(data);
10877 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
10879 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND")) {
10881 AlertValue val = G_ALERTDEFAULT;
10882 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
10884 list = uri_list_extract_filenames(ddata);
10885 if (list == NULL && strstr(ddata, "://")) {
10886 /* Assume a list of no files, and data has ://, is a remote link */
10887 gchar *tmpdata = g_strstrip(g_strdup(ddata));
10888 gchar *tmpfile = get_tmp_file();
10889 str_write_to_file(tmpdata, tmpfile);
10891 compose_insert_file(compose, tmpfile);
10892 claws_unlink(tmpfile);
10894 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10895 compose_beautify_paragraph(compose, NULL, TRUE);
10898 switch (prefs_common.compose_dnd_mode) {
10899 case COMPOSE_DND_ASK:
10900 val = alertpanel_full(_("Insert or attach?"),
10901 _("Do you want to insert the contents of the file(s) "
10902 "into the message body, or attach it to the email?"),
10903 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10904 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10906 case COMPOSE_DND_INSERT:
10907 val = G_ALERTALTERNATE;
10909 case COMPOSE_DND_ATTACH:
10910 val = G_ALERTOTHER;
10913 /* unexpected case */
10914 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10917 if (val & G_ALERTDISABLE) {
10918 val &= ~G_ALERTDISABLE;
10919 /* remember what action to perform by default, only if we don't click Cancel */
10920 if (val == G_ALERTALTERNATE)
10921 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10922 else if (val == G_ALERTOTHER)
10923 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10926 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10927 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10928 list_free_strings(list);
10931 } else if (val == G_ALERTOTHER) {
10932 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10933 list_free_strings(list);
10938 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10939 compose_insert_file(compose, (const gchar *)tmp->data);
10941 list_free_strings(list);
10943 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10948 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10951 static void compose_header_drag_received_cb (GtkWidget *widget,
10952 GdkDragContext *drag_context,
10955 GtkSelectionData *data,
10958 gpointer user_data)
10960 GtkEditable *entry = (GtkEditable *)user_data;
10961 const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
10963 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10966 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10967 gchar *decoded=g_new(gchar, strlen(email));
10970 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
10971 gtk_editable_delete_text(entry, 0, -1);
10972 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10973 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10977 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10980 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10982 Compose *compose = (Compose *)data;
10984 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10985 compose->return_receipt = TRUE;
10987 compose->return_receipt = FALSE;
10990 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10992 Compose *compose = (Compose *)data;
10994 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10995 compose->remove_references = TRUE;
10997 compose->remove_references = FALSE;
11000 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
11001 ComposeHeaderEntry *headerentry)
11003 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
11007 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
11008 GdkEventKey *event,
11009 ComposeHeaderEntry *headerentry)
11011 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
11012 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
11013 !(event->state & GDK_MODIFIER_MASK) &&
11014 (event->keyval == GDK_KEY_BackSpace) &&
11015 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
11016 gtk_container_remove
11017 (GTK_CONTAINER(headerentry->compose->header_table),
11018 headerentry->combo);
11019 gtk_container_remove
11020 (GTK_CONTAINER(headerentry->compose->header_table),
11021 headerentry->entry);
11022 headerentry->compose->header_list =
11023 g_slist_remove(headerentry->compose->header_list,
11025 g_free(headerentry);
11026 } else if (event->keyval == GDK_KEY_Tab) {
11027 if (headerentry->compose->header_last == headerentry) {
11028 /* Override default next focus, and give it to subject_entry
11029 * instead of notebook tabs
11031 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
11032 gtk_widget_grab_focus(headerentry->compose->subject_entry);
11039 static gboolean scroll_postpone(gpointer data)
11041 Compose *compose = (Compose *)data;
11043 cm_return_val_if_fail(!compose->batch, FALSE);
11045 GTK_EVENTS_FLUSH();
11046 compose_show_first_last_header(compose, FALSE);
11050 static void compose_headerentry_changed_cb(GtkWidget *entry,
11051 ComposeHeaderEntry *headerentry)
11053 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
11054 compose_create_header_entry(headerentry->compose);
11055 g_signal_handlers_disconnect_matched
11056 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
11057 0, 0, NULL, NULL, headerentry);
11059 if (!headerentry->compose->batch)
11060 g_timeout_add(0, scroll_postpone, headerentry->compose);
11064 static gboolean compose_defer_auto_save_draft(Compose *compose)
11066 compose->draft_timeout_tag = -1;
11067 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
11071 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
11073 GtkAdjustment *vadj;
11075 cm_return_if_fail(compose);
11076 cm_return_if_fail(!compose->batch);
11077 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
11078 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
11079 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11080 gtk_widget_get_parent(compose->header_table)));
11081 gtk_adjustment_set_value(vadj, (show_first ?
11082 gtk_adjustment_get_lower(vadj) :
11083 (gtk_adjustment_get_upper(vadj) -
11084 gtk_adjustment_get_page_size(vadj))));
11085 gtk_adjustment_changed(vadj);
11088 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
11089 const gchar *text, gint len, Compose *compose)
11091 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
11092 (G_OBJECT(compose->text), "paste_as_quotation"));
11095 cm_return_if_fail(text != NULL);
11097 g_signal_handlers_block_by_func(G_OBJECT(buffer),
11098 G_CALLBACK(text_inserted),
11100 if (paste_as_quotation) {
11102 const gchar *qmark;
11104 GtkTextIter start_iter;
11107 len = strlen(text);
11109 new_text = g_strndup(text, len);
11111 qmark = compose_quote_char_from_context(compose);
11113 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11114 gtk_text_buffer_place_cursor(buffer, iter);
11116 pos = gtk_text_iter_get_offset(iter);
11118 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
11119 _("Quote format error at line %d."));
11120 quote_fmt_reset_vartable();
11122 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
11123 GINT_TO_POINTER(paste_as_quotation - 1));
11125 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11126 gtk_text_buffer_place_cursor(buffer, iter);
11127 gtk_text_buffer_delete_mark(buffer, mark);
11129 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
11130 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
11131 compose_beautify_paragraph(compose, &start_iter, FALSE);
11132 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
11133 gtk_text_buffer_delete_mark(buffer, mark);
11135 if (strcmp(text, "\n") || compose->automatic_break
11136 || gtk_text_iter_starts_line(iter)) {
11137 GtkTextIter before_ins;
11138 gtk_text_buffer_insert(buffer, iter, text, len);
11139 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
11140 before_ins = *iter;
11141 gtk_text_iter_backward_chars(&before_ins, len);
11142 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
11145 /* check if the preceding is just whitespace or quote */
11146 GtkTextIter start_line;
11147 gchar *tmp = NULL, *quote = NULL;
11148 gint quote_len = 0, is_normal = 0;
11149 start_line = *iter;
11150 gtk_text_iter_set_line_offset(&start_line, 0);
11151 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
11154 if (*tmp == '\0') {
11157 quote = compose_get_quote_str(buffer, &start_line, "e_len);
11165 gtk_text_buffer_insert(buffer, iter, text, len);
11167 gtk_text_buffer_insert_with_tags_by_name(buffer,
11168 iter, text, len, "no_join", NULL);
11173 if (!paste_as_quotation) {
11174 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11175 compose_beautify_paragraph(compose, iter, FALSE);
11176 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11177 gtk_text_buffer_delete_mark(buffer, mark);
11180 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
11181 G_CALLBACK(text_inserted),
11183 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
11185 if (prefs_common.autosave &&
11186 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
11187 compose->draft_timeout_tag != -2 /* disabled while loading */)
11188 compose->draft_timeout_tag = g_timeout_add
11189 (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
11193 static void compose_check_all(GtkAction *action, gpointer data)
11195 Compose *compose = (Compose *)data;
11196 if (!compose->gtkaspell)
11199 if (gtk_widget_has_focus(compose->subject_entry))
11200 claws_spell_entry_check_all(
11201 CLAWS_SPELL_ENTRY(compose->subject_entry));
11203 gtkaspell_check_all(compose->gtkaspell);
11206 static void compose_highlight_all(GtkAction *action, gpointer data)
11208 Compose *compose = (Compose *)data;
11209 if (compose->gtkaspell) {
11210 claws_spell_entry_recheck_all(
11211 CLAWS_SPELL_ENTRY(compose->subject_entry));
11212 gtkaspell_highlight_all(compose->gtkaspell);
11216 static void compose_check_backwards(GtkAction *action, gpointer data)
11218 Compose *compose = (Compose *)data;
11219 if (!compose->gtkaspell) {
11220 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11224 if (gtk_widget_has_focus(compose->subject_entry))
11225 claws_spell_entry_check_backwards(
11226 CLAWS_SPELL_ENTRY(compose->subject_entry));
11228 gtkaspell_check_backwards(compose->gtkaspell);
11231 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11233 Compose *compose = (Compose *)data;
11234 if (!compose->gtkaspell) {
11235 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11239 if (gtk_widget_has_focus(compose->subject_entry))
11240 claws_spell_entry_check_forwards_go(
11241 CLAWS_SPELL_ENTRY(compose->subject_entry));
11243 gtkaspell_check_forwards_go(compose->gtkaspell);
11248 *\brief Guess originating forward account from MsgInfo and several
11249 * "common preference" settings. Return NULL if no guess.
11251 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11253 PrefsAccount *account = NULL;
11255 cm_return_val_if_fail(msginfo, NULL);
11256 cm_return_val_if_fail(msginfo->folder, NULL);
11257 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11259 if (msginfo->folder->prefs->enable_default_account)
11260 account = account_find_from_id(msginfo->folder->prefs->default_account);
11263 account = msginfo->folder->folder->account;
11265 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11267 Xstrdup_a(to, msginfo->to, return NULL);
11268 extract_address(to);
11269 account = account_find_from_address(to, FALSE);
11272 if (!account && prefs_common.forward_account_autosel) {
11273 gchar cc[BUFFSIZE];
11274 if (!procheader_get_header_from_msginfo
11275 (msginfo, cc,sizeof cc , "Cc:")) {
11276 gchar *buf = cc + strlen("Cc:");
11277 extract_address(buf);
11278 account = account_find_from_address(buf, FALSE);
11282 if (!account && prefs_common.forward_account_autosel) {
11283 gchar deliveredto[BUFFSIZE];
11284 if (!procheader_get_header_from_msginfo
11285 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
11286 gchar *buf = deliveredto + strlen("Delivered-To:");
11287 extract_address(buf);
11288 account = account_find_from_address(buf, FALSE);
11295 gboolean compose_close(Compose *compose)
11299 if (!g_mutex_trylock(compose->mutex)) {
11300 /* we have to wait for the (possibly deferred by auto-save)
11301 * drafting to be done, before destroying the compose under
11303 debug_print("waiting for drafting to finish...\n");
11304 compose_allow_user_actions(compose, FALSE);
11305 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11308 cm_return_val_if_fail(compose, FALSE);
11309 gtkut_widget_get_uposition(compose->window, &x, &y);
11310 if (!compose->batch) {
11311 prefs_common.compose_x = x;
11312 prefs_common.compose_y = y;
11314 g_mutex_unlock(compose->mutex);
11315 compose_destroy(compose);
11320 * Add entry field for each address in list.
11321 * \param compose E-Mail composition object.
11322 * \param listAddress List of (formatted) E-Mail addresses.
11324 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11327 node = listAddress;
11329 addr = ( gchar * ) node->data;
11330 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11331 node = g_list_next( node );
11335 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11336 guint action, gboolean opening_multiple)
11338 gchar *body = NULL;
11339 GSList *new_msglist = NULL;
11340 MsgInfo *tmp_msginfo = NULL;
11341 gboolean originally_enc = FALSE;
11342 gboolean originally_sig = FALSE;
11343 Compose *compose = NULL;
11344 gchar *s_system = NULL;
11346 cm_return_if_fail(msgview != NULL);
11348 cm_return_if_fail(msginfo_list != NULL);
11350 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11351 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11352 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11354 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11355 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11356 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11357 orig_msginfo, mimeinfo);
11358 if (tmp_msginfo != NULL) {
11359 new_msglist = g_slist_append(NULL, tmp_msginfo);
11361 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11362 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11363 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11365 tmp_msginfo->folder = orig_msginfo->folder;
11366 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11367 if (orig_msginfo->tags) {
11368 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11369 tmp_msginfo->folder->tags_dirty = TRUE;
11375 if (!opening_multiple)
11376 body = messageview_get_selection(msgview);
11379 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11380 procmsg_msginfo_free(tmp_msginfo);
11381 g_slist_free(new_msglist);
11383 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11385 if (compose && originally_enc) {
11386 compose_force_encryption(compose, compose->account, FALSE, s_system);
11389 if (compose && originally_sig && compose->account->default_sign_reply) {
11390 compose_force_signing(compose, compose->account, s_system);
11394 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11397 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11400 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11401 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11402 GSList *cur = msginfo_list;
11403 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11404 "messages. Opening the windows "
11405 "could take some time. Do you "
11406 "want to continue?"),
11407 g_slist_length(msginfo_list));
11408 if (g_slist_length(msginfo_list) > 9
11409 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11410 != G_ALERTALTERNATE) {
11415 /* We'll open multiple compose windows */
11416 /* let the WM place the next windows */
11417 compose_force_window_origin = FALSE;
11418 for (; cur; cur = cur->next) {
11420 tmplist.data = cur->data;
11421 tmplist.next = NULL;
11422 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11424 compose_force_window_origin = TRUE;
11426 /* forwarding multiple mails as attachments is done via a
11427 * single compose window */
11428 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11432 void compose_check_for_email_account(Compose *compose)
11434 PrefsAccount *ac = NULL, *curr = NULL;
11440 if (compose->account && compose->account->protocol == A_NNTP) {
11441 ac = account_get_cur_account();
11442 if (ac->protocol == A_NNTP) {
11443 list = account_get_list();
11445 for( ; list != NULL ; list = g_list_next(list)) {
11446 curr = (PrefsAccount *) list->data;
11447 if (curr->protocol != A_NNTP) {
11453 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11458 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo,
11459 const gchar *address)
11461 GSList *msginfo_list = NULL;
11462 gchar *body = messageview_get_selection(msgview);
11465 msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11467 compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11468 compose_check_for_email_account(compose);
11469 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11470 compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11471 compose_reply_set_subject(compose, msginfo);
11474 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11477 void compose_set_position(Compose *compose, gint pos)
11479 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11481 gtkut_text_view_set_position(text, pos);
11484 gboolean compose_search_string(Compose *compose,
11485 const gchar *str, gboolean case_sens)
11487 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11489 return gtkut_text_view_search_string(text, str, case_sens);
11492 gboolean compose_search_string_backward(Compose *compose,
11493 const gchar *str, gboolean case_sens)
11495 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11497 return gtkut_text_view_search_string_backward(text, str, case_sens);
11500 /* allocate a msginfo structure and populate its data from a compose data structure */
11501 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11503 MsgInfo *newmsginfo;
11505 gchar buf[BUFFSIZE];
11507 cm_return_val_if_fail( compose != NULL, NULL );
11509 newmsginfo = procmsg_msginfo_new();
11512 get_rfc822_date(buf, sizeof(buf));
11513 newmsginfo->date = g_strdup(buf);
11516 if (compose->from_name) {
11517 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11518 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11522 if (compose->subject_entry)
11523 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11525 /* to, cc, reply-to, newsgroups */
11526 for (list = compose->header_list; list; list = list->next) {
11527 gchar *header = gtk_editable_get_chars(
11529 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11530 gchar *entry = gtk_editable_get_chars(
11531 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11533 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11534 if ( newmsginfo->to == NULL ) {
11535 newmsginfo->to = g_strdup(entry);
11536 } else if (entry && *entry) {
11537 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11538 g_free(newmsginfo->to);
11539 newmsginfo->to = tmp;
11542 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11543 if ( newmsginfo->cc == NULL ) {
11544 newmsginfo->cc = g_strdup(entry);
11545 } else if (entry && *entry) {
11546 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11547 g_free(newmsginfo->cc);
11548 newmsginfo->cc = tmp;
11551 if ( strcasecmp(header,
11552 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11553 if ( newmsginfo->newsgroups == NULL ) {
11554 newmsginfo->newsgroups = g_strdup(entry);
11555 } else if (entry && *entry) {
11556 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11557 g_free(newmsginfo->newsgroups);
11558 newmsginfo->newsgroups = tmp;
11566 /* other data is unset */
11572 /* update compose's dictionaries from folder dict settings */
11573 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11574 FolderItem *folder_item)
11576 cm_return_if_fail(compose != NULL);
11578 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11579 FolderItemPrefs *prefs = folder_item->prefs;
11581 if (prefs->enable_default_dictionary)
11582 gtkaspell_change_dict(compose->gtkaspell,
11583 prefs->default_dictionary, FALSE);
11584 if (folder_item->prefs->enable_default_alt_dictionary)
11585 gtkaspell_change_alt_dict(compose->gtkaspell,
11586 prefs->default_alt_dictionary);
11587 if (prefs->enable_default_dictionary
11588 || prefs->enable_default_alt_dictionary)
11589 compose_spell_menu_changed(compose);