2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2011 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #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);
505 static void text_inserted (GtkTextBuffer *buffer,
510 static Compose *compose_generic_reply(MsgInfo *msginfo,
511 ComposeQuoteMode quote_mode,
515 gboolean followup_and_reply_to,
518 static void compose_headerentry_changed_cb (GtkWidget *entry,
519 ComposeHeaderEntry *headerentry);
520 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
522 ComposeHeaderEntry *headerentry);
523 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
524 ComposeHeaderEntry *headerentry);
526 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
528 static void compose_allow_user_actions (Compose *compose, gboolean allow);
530 static void compose_nothing_cb (GtkAction *action, gpointer data)
536 static void compose_check_all (GtkAction *action, gpointer data);
537 static void compose_highlight_all (GtkAction *action, gpointer data);
538 static void compose_check_backwards (GtkAction *action, gpointer data);
539 static void compose_check_forwards_go (GtkAction *action, gpointer data);
542 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
544 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
547 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
548 FolderItem *folder_item);
550 static void compose_attach_update_label(Compose *compose);
551 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
552 gboolean respect_default_to);
554 static GtkActionEntry compose_popup_entries[] =
556 {"Compose", NULL, "Compose" },
557 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
558 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
559 {"Compose/---", NULL, "---", NULL, NULL, NULL },
560 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
563 static GtkActionEntry compose_entries[] =
565 {"Menu", NULL, "Menu" },
567 {"Message", NULL, N_("_Message") },
568 {"Edit", NULL, N_("_Edit") },
570 {"Spelling", NULL, N_("_Spelling") },
572 {"Options", NULL, N_("_Options") },
573 {"Tools", NULL, N_("_Tools") },
574 {"Help", NULL, N_("_Help") },
576 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
577 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
578 {"Message/---", NULL, "---" },
580 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
581 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
582 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
583 /* {"Message/---", NULL, "---" }, */
584 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
585 /* {"Message/---", NULL, "---" }, */
586 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
587 /* {"Message/---", NULL, "---" }, */
588 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
591 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
592 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
593 {"Edit/---", NULL, "---" },
595 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
596 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
597 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
599 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
600 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
601 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
602 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
604 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
606 {"Edit/Advanced", NULL, N_("A_dvanced") },
607 {"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*/
608 {"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*/
609 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
610 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
611 {"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*/
612 {"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*/
613 {"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*/
614 {"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*/
615 {"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*/
616 {"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*/
617 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
618 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
619 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
620 {"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*/
622 /* {"Edit/---", NULL, "---" }, */
623 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
625 /* {"Edit/---", NULL, "---" }, */
626 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
627 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
628 /* {"Edit/---", NULL, "---" }, */
629 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
632 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
633 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
634 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
635 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
637 {"Spelling/---", NULL, "---" },
638 {"Spelling/Options", NULL, N_("_Options") },
643 {"Options/ReplyMode", NULL, N_("Reply _mode") },
644 {"Options/---", NULL, "---" },
645 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
646 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
648 /* {"Options/---", NULL, "---" }, */
650 {"Options/Priority", NULL, N_("_Priority") },
652 {"Options/Encoding", NULL, N_("Character _encoding") },
653 {"Options/Encoding/---", NULL, "---" },
654 #define ENC_ACTION(cs_char,c_char,string) \
655 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
657 {"Options/Encoding/Western", NULL, N_("Western European") },
658 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
659 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
660 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
661 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
662 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
663 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
664 {"Options/Encoding/Korean", NULL, N_("Korean") },
665 {"Options/Encoding/Thai", NULL, N_("Thai") },
668 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
670 {"Tools/Template", NULL, N_("_Template") },
671 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
672 {"Tools/Actions", NULL, N_("Actio_ns") },
673 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
676 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
679 static GtkToggleActionEntry compose_toggle_entries[] =
681 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
682 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
683 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
684 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
685 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
686 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
687 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
690 static GtkRadioActionEntry compose_radio_rm_entries[] =
692 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
693 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
694 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
695 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
698 static GtkRadioActionEntry compose_radio_prio_entries[] =
700 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
701 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
702 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
703 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
704 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
707 static GtkRadioActionEntry compose_radio_enc_entries[] =
709 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
729 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
730 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
731 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
732 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
733 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
734 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
735 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
736 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
737 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
738 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
739 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
740 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
743 static GtkTargetEntry compose_mime_types[] =
745 {"text/uri-list", 0, 0},
746 {"UTF8_STRING", 0, 0},
750 static gboolean compose_put_existing_to_front(MsgInfo *info)
752 GList *compose_list = compose_get_compose_list();
756 for (elem = compose_list; elem != NULL && elem->data != NULL;
758 Compose *c = (Compose*)elem->data;
760 if (!c->targetinfo || !c->targetinfo->msgid ||
764 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
765 gtkut_window_popup(c->window);
773 static GdkColor quote_color1 =
774 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
775 static GdkColor quote_color2 =
776 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
777 static GdkColor quote_color3 =
778 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
780 static GdkColor quote_bgcolor1 =
781 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
782 static GdkColor quote_bgcolor2 =
783 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
784 static GdkColor quote_bgcolor3 =
785 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
787 static GdkColor signature_color = {
794 static GdkColor uri_color = {
801 static void compose_create_tags(GtkTextView *text, Compose *compose)
803 GtkTextBuffer *buffer;
804 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
805 #if !GTK_CHECK_VERSION(2, 24, 0)
812 buffer = gtk_text_view_get_buffer(text);
814 if (prefs_common.enable_color) {
815 /* grab the quote colors, converting from an int to a GdkColor */
816 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
818 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
820 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
822 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
824 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
826 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
828 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
830 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
833 signature_color = quote_color1 = quote_color2 = quote_color3 =
834 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
837 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
838 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
839 "foreground-gdk", "e_color1,
840 "paragraph-background-gdk", "e_bgcolor1,
842 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
843 "foreground-gdk", "e_color2,
844 "paragraph-background-gdk", "e_bgcolor2,
846 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
847 "foreground-gdk", "e_color3,
848 "paragraph-background-gdk", "e_bgcolor3,
851 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
852 "foreground-gdk", "e_color1,
854 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
855 "foreground-gdk", "e_color2,
857 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
858 "foreground-gdk", "e_color3,
862 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
863 "foreground-gdk", &signature_color,
866 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
867 "foreground-gdk", &uri_color,
869 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
870 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
872 color[0] = quote_color1;
873 color[1] = quote_color2;
874 color[2] = quote_color3;
875 color[3] = quote_bgcolor1;
876 color[4] = quote_bgcolor2;
877 color[5] = quote_bgcolor3;
878 color[6] = signature_color;
879 color[7] = uri_color;
880 #if !GTK_CHECK_VERSION(2, 24, 0)
881 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
882 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
884 for (i = 0; i < 8; i++) {
885 if (success[i] == FALSE) {
888 g_warning("Compose: color allocation failed.\n");
889 style = gtk_widget_get_style(GTK_WIDGET(text));
890 quote_color1 = quote_color2 = quote_color3 =
891 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
892 signature_color = uri_color = black;
898 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
901 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
904 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
906 return compose_generic_new(account, mailto, item, NULL, NULL);
909 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
911 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
914 #define SCROLL_TO_CURSOR(compose) { \
915 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
916 gtk_text_view_get_buffer( \
917 GTK_TEXT_VIEW(compose->text))); \
918 gtk_text_view_scroll_mark_onscreen( \
919 GTK_TEXT_VIEW(compose->text), \
923 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
926 if (folderidentifier) {
927 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
928 prefs_common.compose_save_to_history = add_history(
929 prefs_common.compose_save_to_history, folderidentifier);
930 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
931 prefs_common.compose_save_to_history);
934 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
935 if (folderidentifier)
936 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
938 gtk_entry_set_text(GTK_ENTRY(entry), "");
941 static gchar *compose_get_save_to(Compose *compose)
944 gchar *result = NULL;
945 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
946 result = gtk_editable_get_chars(entry, 0, -1);
949 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
950 prefs_common.compose_save_to_history = add_history(
951 prefs_common.compose_save_to_history, result);
952 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
953 prefs_common.compose_save_to_history);
958 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
959 GList *attach_files, GList *listAddress )
962 GtkTextView *textview;
963 GtkTextBuffer *textbuf;
965 const gchar *subject_format = NULL;
966 const gchar *body_format = NULL;
967 gchar *mailto_from = NULL;
968 PrefsAccount *mailto_account = NULL;
969 MsgInfo* dummyinfo = NULL;
970 gint cursor_pos = -1;
971 MailField mfield = NO_FIELD_PRESENT;
975 /* check if mailto defines a from */
976 if (mailto && *mailto != '\0') {
977 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
978 /* mailto defines a from, check if we can get account prefs from it,
979 if not, the account prefs will be guessed using other ways, but we'll keep
982 mailto_account = account_find_from_address(mailto_from, TRUE);
984 account = mailto_account;
987 /* if no account prefs set from mailto, set if from folder prefs (if any) */
988 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
989 account = account_find_from_id(item->prefs->default_account);
991 /* if no account prefs set, fallback to the current one */
992 if (!account) account = cur_account;
993 cm_return_val_if_fail(account != NULL, NULL);
995 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
997 /* override from name if mailto asked for it */
999 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1000 g_free(mailto_from);
1002 /* override from name according to folder properties */
1003 if (item && item->prefs &&
1004 item->prefs->compose_with_format &&
1005 item->prefs->compose_override_from_format &&
1006 *item->prefs->compose_override_from_format != '\0') {
1011 dummyinfo = compose_msginfo_new_from_compose(compose);
1013 /* decode \-escape sequences in the internal representation of the quote format */
1014 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1015 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1018 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1019 compose->gtkaspell);
1021 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1023 quote_fmt_scan_string(tmp);
1026 buf = quote_fmt_get_buffer();
1028 alertpanel_error(_("New message From format error."));
1030 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1031 quote_fmt_reset_vartable();
1036 compose->replyinfo = NULL;
1037 compose->fwdinfo = NULL;
1039 textview = GTK_TEXT_VIEW(compose->text);
1040 textbuf = gtk_text_view_get_buffer(textview);
1041 compose_create_tags(textview, compose);
1043 undo_block(compose->undostruct);
1045 compose_set_dictionaries_from_folder_prefs(compose, item);
1048 if (account->auto_sig)
1049 compose_insert_sig(compose, FALSE);
1050 gtk_text_buffer_get_start_iter(textbuf, &iter);
1051 gtk_text_buffer_place_cursor(textbuf, &iter);
1053 if (account->protocol != A_NNTP) {
1054 if (mailto && *mailto != '\0') {
1055 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1058 compose_set_folder_prefs(compose, item, TRUE);
1060 if (item && item->ret_rcpt) {
1061 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1064 if (mailto && *mailto != '\0') {
1065 if (!strchr(mailto, '@'))
1066 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1068 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1069 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1070 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1071 mfield = TO_FIELD_PRESENT;
1074 * CLAWS: just don't allow return receipt request, even if the user
1075 * may want to send an email. simple but foolproof.
1077 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1079 compose_add_field_list( compose, listAddress );
1081 if (item && item->prefs && item->prefs->compose_with_format) {
1082 subject_format = item->prefs->compose_subject_format;
1083 body_format = item->prefs->compose_body_format;
1084 } else if (account->compose_with_format) {
1085 subject_format = account->compose_subject_format;
1086 body_format = account->compose_body_format;
1087 } else if (prefs_common.compose_with_format) {
1088 subject_format = prefs_common.compose_subject_format;
1089 body_format = prefs_common.compose_body_format;
1092 if (subject_format || body_format) {
1095 && *subject_format != '\0' )
1097 gchar *subject = NULL;
1102 dummyinfo = compose_msginfo_new_from_compose(compose);
1104 /* decode \-escape sequences in the internal representation of the quote format */
1105 tmp = g_malloc(strlen(subject_format)+1);
1106 pref_get_unescaped_pref(tmp, subject_format);
1108 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1110 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1111 compose->gtkaspell);
1113 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1115 quote_fmt_scan_string(tmp);
1118 buf = quote_fmt_get_buffer();
1120 alertpanel_error(_("New message subject format error."));
1122 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1123 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1124 quote_fmt_reset_vartable();
1128 mfield = SUBJECT_FIELD_PRESENT;
1132 && *body_format != '\0' )
1135 GtkTextBuffer *buffer;
1136 GtkTextIter start, end;
1140 dummyinfo = compose_msginfo_new_from_compose(compose);
1142 text = GTK_TEXT_VIEW(compose->text);
1143 buffer = gtk_text_view_get_buffer(text);
1144 gtk_text_buffer_get_start_iter(buffer, &start);
1145 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1146 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1148 compose_quote_fmt(compose, dummyinfo,
1150 NULL, tmp, FALSE, TRUE,
1151 _("The body of the \"New message\" template has an error at line %d."));
1152 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1153 quote_fmt_reset_vartable();
1157 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1158 gtkaspell_highlight_all(compose->gtkaspell);
1160 mfield = BODY_FIELD_PRESENT;
1164 procmsg_msginfo_free( dummyinfo );
1170 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1171 ainfo = (AttachInfo *) curr->data;
1172 compose_attach_append(compose, ainfo->file, ainfo->name,
1173 ainfo->content_type, ainfo->charset);
1177 compose_show_first_last_header(compose, TRUE);
1179 /* Set save folder */
1180 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1181 gchar *folderidentifier;
1183 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1184 folderidentifier = folder_item_get_identifier(item);
1185 compose_set_save_to(compose, folderidentifier);
1186 g_free(folderidentifier);
1189 /* Place cursor according to provided input (mfield) */
1191 case NO_FIELD_PRESENT:
1192 if (compose->header_last)
1193 gtk_widget_grab_focus(compose->header_last->entry);
1195 case TO_FIELD_PRESENT:
1196 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1198 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1201 gtk_widget_grab_focus(compose->subject_entry);
1203 case SUBJECT_FIELD_PRESENT:
1204 textview = GTK_TEXT_VIEW(compose->text);
1207 textbuf = gtk_text_view_get_buffer(textview);
1210 mark = gtk_text_buffer_get_insert(textbuf);
1211 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1212 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1214 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1215 * only defers where it comes to the variable body
1216 * is not null. If no body is present compose->text
1217 * will be null in which case you cannot place the
1218 * cursor inside the component so. An empty component
1219 * is therefore created before placing the cursor
1221 case BODY_FIELD_PRESENT:
1222 cursor_pos = quote_fmt_get_cursor_pos();
1223 if (cursor_pos == -1)
1224 gtk_widget_grab_focus(compose->header_last->entry);
1226 gtk_widget_grab_focus(compose->text);
1230 undo_unblock(compose->undostruct);
1232 if (prefs_common.auto_exteditor)
1233 compose_exec_ext_editor(compose);
1235 compose->draft_timeout_tag = -1;
1236 SCROLL_TO_CURSOR(compose);
1238 compose->modified = FALSE;
1239 compose_set_title(compose);
1241 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1246 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1247 gboolean override_pref, const gchar *system)
1249 const gchar *privacy = NULL;
1251 cm_return_if_fail(compose != NULL);
1252 cm_return_if_fail(account != NULL);
1254 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1259 else if (account->default_privacy_system
1260 && strlen(account->default_privacy_system)) {
1261 privacy = account->default_privacy_system;
1263 GSList *privacy_avail = privacy_get_system_ids();
1264 if (privacy_avail && g_slist_length(privacy_avail)) {
1265 privacy = (gchar *)(privacy_avail->data);
1268 if (privacy != NULL) {
1270 g_free(compose->privacy_system);
1271 compose->privacy_system = NULL;
1273 if (compose->privacy_system == NULL)
1274 compose->privacy_system = g_strdup(privacy);
1275 else if (*(compose->privacy_system) == '\0') {
1276 g_free(compose->privacy_system);
1277 compose->privacy_system = g_strdup(privacy);
1279 compose_update_privacy_system_menu_item(compose, FALSE);
1280 compose_use_encryption(compose, TRUE);
1284 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1286 const gchar *privacy = NULL;
1290 else if (account->default_privacy_system
1291 && strlen(account->default_privacy_system)) {
1292 privacy = account->default_privacy_system;
1294 GSList *privacy_avail = privacy_get_system_ids();
1295 if (privacy_avail && g_slist_length(privacy_avail)) {
1296 privacy = (gchar *)(privacy_avail->data);
1300 if (privacy != NULL) {
1302 g_free(compose->privacy_system);
1303 compose->privacy_system = NULL;
1305 if (compose->privacy_system == NULL)
1306 compose->privacy_system = g_strdup(privacy);
1307 compose_update_privacy_system_menu_item(compose, FALSE);
1308 compose_use_signing(compose, TRUE);
1312 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1316 Compose *compose = NULL;
1318 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1320 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1321 cm_return_val_if_fail(msginfo != NULL, NULL);
1323 list_len = g_slist_length(msginfo_list);
1327 case COMPOSE_REPLY_TO_ADDRESS:
1328 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1329 FALSE, prefs_common.default_reply_list, FALSE, body);
1331 case COMPOSE_REPLY_WITH_QUOTE:
1332 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1333 FALSE, prefs_common.default_reply_list, FALSE, body);
1335 case COMPOSE_REPLY_WITHOUT_QUOTE:
1336 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1337 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1339 case COMPOSE_REPLY_TO_SENDER:
1340 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1341 FALSE, FALSE, TRUE, body);
1343 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1344 compose = compose_followup_and_reply_to(msginfo,
1345 COMPOSE_QUOTE_CHECK,
1346 FALSE, FALSE, body);
1348 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1349 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1350 FALSE, FALSE, TRUE, body);
1352 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1353 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1354 FALSE, FALSE, TRUE, NULL);
1356 case COMPOSE_REPLY_TO_ALL:
1357 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1358 TRUE, FALSE, FALSE, body);
1360 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1361 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1362 TRUE, FALSE, FALSE, body);
1364 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1365 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1366 TRUE, FALSE, FALSE, NULL);
1368 case COMPOSE_REPLY_TO_LIST:
1369 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1370 FALSE, TRUE, FALSE, body);
1372 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1373 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1374 FALSE, TRUE, FALSE, body);
1376 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1377 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1378 FALSE, TRUE, FALSE, NULL);
1380 case COMPOSE_FORWARD:
1381 if (prefs_common.forward_as_attachment) {
1382 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1385 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1389 case COMPOSE_FORWARD_INLINE:
1390 /* check if we reply to more than one Message */
1391 if (list_len == 1) {
1392 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1395 /* more messages FALL THROUGH */
1396 case COMPOSE_FORWARD_AS_ATTACH:
1397 compose = compose_forward_multiple(NULL, msginfo_list);
1399 case COMPOSE_REDIRECT:
1400 compose = compose_redirect(NULL, msginfo, FALSE);
1403 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1406 if (compose == NULL) {
1407 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1411 compose->rmode = mode;
1412 switch (compose->rmode) {
1414 case COMPOSE_REPLY_WITH_QUOTE:
1415 case COMPOSE_REPLY_WITHOUT_QUOTE:
1416 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1417 debug_print("reply mode Normal\n");
1418 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1419 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1421 case COMPOSE_REPLY_TO_SENDER:
1422 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1423 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1424 debug_print("reply mode Sender\n");
1425 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1427 case COMPOSE_REPLY_TO_ALL:
1428 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1429 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1430 debug_print("reply mode All\n");
1431 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1433 case COMPOSE_REPLY_TO_LIST:
1434 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1435 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1436 debug_print("reply mode List\n");
1437 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1439 case COMPOSE_REPLY_TO_ADDRESS:
1440 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1448 static Compose *compose_reply(MsgInfo *msginfo,
1449 ComposeQuoteMode quote_mode,
1455 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1456 to_sender, FALSE, body);
1459 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1460 ComposeQuoteMode quote_mode,
1465 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1466 to_sender, TRUE, body);
1469 static void compose_extract_original_charset(Compose *compose)
1471 MsgInfo *info = NULL;
1472 if (compose->replyinfo) {
1473 info = compose->replyinfo;
1474 } else if (compose->fwdinfo) {
1475 info = compose->fwdinfo;
1476 } else if (compose->targetinfo) {
1477 info = compose->targetinfo;
1480 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1481 MimeInfo *partinfo = mimeinfo;
1482 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1483 partinfo = procmime_mimeinfo_next(partinfo);
1485 compose->orig_charset =
1486 g_strdup(procmime_mimeinfo_get_parameter(
1487 partinfo, "charset"));
1489 procmime_mimeinfo_free_all(mimeinfo);
1493 #define SIGNAL_BLOCK(buffer) { \
1494 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1495 G_CALLBACK(compose_changed_cb), \
1497 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1498 G_CALLBACK(text_inserted), \
1502 #define SIGNAL_UNBLOCK(buffer) { \
1503 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1504 G_CALLBACK(compose_changed_cb), \
1506 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1507 G_CALLBACK(text_inserted), \
1511 static Compose *compose_generic_reply(MsgInfo *msginfo,
1512 ComposeQuoteMode quote_mode,
1513 gboolean to_all, gboolean to_ml,
1515 gboolean followup_and_reply_to,
1519 PrefsAccount *account = NULL;
1520 GtkTextView *textview;
1521 GtkTextBuffer *textbuf;
1522 gboolean quote = FALSE;
1523 const gchar *qmark = NULL;
1524 const gchar *body_fmt = NULL;
1525 gchar *s_system = NULL;
1527 cm_return_val_if_fail(msginfo != NULL, NULL);
1528 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1530 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1532 cm_return_val_if_fail(account != NULL, NULL);
1534 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1536 compose->updating = TRUE;
1538 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1539 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1541 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1542 if (!compose->replyinfo)
1543 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1545 compose_extract_original_charset(compose);
1547 if (msginfo->folder && msginfo->folder->ret_rcpt)
1548 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1550 /* Set save folder */
1551 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1552 gchar *folderidentifier;
1554 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1555 folderidentifier = folder_item_get_identifier(msginfo->folder);
1556 compose_set_save_to(compose, folderidentifier);
1557 g_free(folderidentifier);
1560 if (compose_parse_header(compose, msginfo) < 0) {
1561 compose->updating = FALSE;
1562 compose_destroy(compose);
1566 /* override from name according to folder properties */
1567 if (msginfo->folder && msginfo->folder->prefs &&
1568 msginfo->folder->prefs->reply_with_format &&
1569 msginfo->folder->prefs->reply_override_from_format &&
1570 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1575 /* decode \-escape sequences in the internal representation of the quote format */
1576 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1577 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1580 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1581 compose->gtkaspell);
1583 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1585 quote_fmt_scan_string(tmp);
1588 buf = quote_fmt_get_buffer();
1590 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1592 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1593 quote_fmt_reset_vartable();
1598 textview = (GTK_TEXT_VIEW(compose->text));
1599 textbuf = gtk_text_view_get_buffer(textview);
1600 compose_create_tags(textview, compose);
1602 undo_block(compose->undostruct);
1604 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1605 gtkaspell_block_check(compose->gtkaspell);
1608 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1609 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1610 /* use the reply format of folder (if enabled), or the account's one
1611 (if enabled) or fallback to the global reply format, which is always
1612 enabled (even if empty), and use the relevant quotemark */
1614 if (msginfo->folder && msginfo->folder->prefs &&
1615 msginfo->folder->prefs->reply_with_format) {
1616 qmark = msginfo->folder->prefs->reply_quotemark;
1617 body_fmt = msginfo->folder->prefs->reply_body_format;
1619 } else if (account->reply_with_format) {
1620 qmark = account->reply_quotemark;
1621 body_fmt = account->reply_body_format;
1624 qmark = prefs_common.quotemark;
1625 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1626 body_fmt = gettext(prefs_common.quotefmt);
1633 /* empty quotemark is not allowed */
1634 if (qmark == NULL || *qmark == '\0')
1636 compose_quote_fmt(compose, compose->replyinfo,
1637 body_fmt, qmark, body, FALSE, TRUE,
1638 _("The body of the \"Reply\" template has an error at line %d."));
1639 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1640 quote_fmt_reset_vartable();
1643 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1644 compose_force_encryption(compose, account, FALSE, s_system);
1647 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1648 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1649 compose_force_signing(compose, account, s_system);
1653 SIGNAL_BLOCK(textbuf);
1655 if (account->auto_sig)
1656 compose_insert_sig(compose, FALSE);
1658 compose_wrap_all(compose);
1661 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1662 gtkaspell_highlight_all(compose->gtkaspell);
1663 gtkaspell_unblock_check(compose->gtkaspell);
1665 SIGNAL_UNBLOCK(textbuf);
1667 gtk_widget_grab_focus(compose->text);
1669 undo_unblock(compose->undostruct);
1671 if (prefs_common.auto_exteditor)
1672 compose_exec_ext_editor(compose);
1674 compose->modified = FALSE;
1675 compose_set_title(compose);
1677 compose->updating = FALSE;
1678 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1679 SCROLL_TO_CURSOR(compose);
1681 if (compose->deferred_destroy) {
1682 compose_destroy(compose);
1690 #define INSERT_FW_HEADER(var, hdr) \
1691 if (msginfo->var && *msginfo->var) { \
1692 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1693 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1694 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1697 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1698 gboolean as_attach, const gchar *body,
1699 gboolean no_extedit,
1703 GtkTextView *textview;
1704 GtkTextBuffer *textbuf;
1705 gint cursor_pos = -1;
1708 cm_return_val_if_fail(msginfo != NULL, NULL);
1709 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1712 !(account = compose_guess_forward_account_from_msginfo
1714 account = cur_account;
1716 if (!prefs_common.forward_as_attachment)
1717 mode = COMPOSE_FORWARD_INLINE;
1719 mode = COMPOSE_FORWARD;
1720 compose = compose_create(account, msginfo->folder, mode, batch);
1722 compose->updating = TRUE;
1723 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1724 if (!compose->fwdinfo)
1725 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1727 compose_extract_original_charset(compose);
1729 if (msginfo->subject && *msginfo->subject) {
1730 gchar *buf, *buf2, *p;
1732 buf = p = g_strdup(msginfo->subject);
1733 p += subject_get_prefix_length(p);
1734 memmove(buf, p, strlen(p) + 1);
1736 buf2 = g_strdup_printf("Fw: %s", buf);
1737 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1743 /* override from name according to folder properties */
1744 if (msginfo->folder && msginfo->folder->prefs &&
1745 msginfo->folder->prefs->forward_with_format &&
1746 msginfo->folder->prefs->forward_override_from_format &&
1747 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1751 MsgInfo *full_msginfo = NULL;
1754 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1756 full_msginfo = procmsg_msginfo_copy(msginfo);
1758 /* decode \-escape sequences in the internal representation of the quote format */
1759 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1760 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1763 gtkaspell_block_check(compose->gtkaspell);
1764 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1765 compose->gtkaspell);
1767 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1769 quote_fmt_scan_string(tmp);
1772 buf = quote_fmt_get_buffer();
1774 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1776 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1777 quote_fmt_reset_vartable();
1780 procmsg_msginfo_free(full_msginfo);
1783 textview = GTK_TEXT_VIEW(compose->text);
1784 textbuf = gtk_text_view_get_buffer(textview);
1785 compose_create_tags(textview, compose);
1787 undo_block(compose->undostruct);
1791 msgfile = procmsg_get_message_file(msginfo);
1792 if (!is_file_exist(msgfile))
1793 g_warning("%s: file not exist\n", msgfile);
1795 compose_attach_append(compose, msgfile, msgfile,
1796 "message/rfc822", NULL);
1800 const gchar *qmark = NULL;
1801 const gchar *body_fmt = NULL;
1802 MsgInfo *full_msginfo;
1804 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1805 body_fmt = gettext(prefs_common.fw_quotefmt);
1809 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1811 full_msginfo = procmsg_msginfo_copy(msginfo);
1813 /* use the forward format of folder (if enabled), or the account's one
1814 (if enabled) or fallback to the global forward format, which is always
1815 enabled (even if empty), and use the relevant quotemark */
1816 if (msginfo->folder && msginfo->folder->prefs &&
1817 msginfo->folder->prefs->forward_with_format) {
1818 qmark = msginfo->folder->prefs->forward_quotemark;
1819 body_fmt = msginfo->folder->prefs->forward_body_format;
1821 } else if (account->forward_with_format) {
1822 qmark = account->forward_quotemark;
1823 body_fmt = account->forward_body_format;
1826 qmark = prefs_common.fw_quotemark;
1827 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1828 body_fmt = gettext(prefs_common.fw_quotefmt);
1833 /* empty quotemark is not allowed */
1834 if (qmark == NULL || *qmark == '\0')
1837 compose_quote_fmt(compose, full_msginfo,
1838 body_fmt, qmark, body, FALSE, TRUE,
1839 _("The body of the \"Forward\" template has an error at line %d."));
1840 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1841 quote_fmt_reset_vartable();
1842 compose_attach_parts(compose, msginfo);
1844 procmsg_msginfo_free(full_msginfo);
1847 SIGNAL_BLOCK(textbuf);
1849 if (account->auto_sig)
1850 compose_insert_sig(compose, FALSE);
1852 compose_wrap_all(compose);
1855 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1856 gtkaspell_highlight_all(compose->gtkaspell);
1857 gtkaspell_unblock_check(compose->gtkaspell);
1859 SIGNAL_UNBLOCK(textbuf);
1861 cursor_pos = quote_fmt_get_cursor_pos();
1862 if (cursor_pos == -1)
1863 gtk_widget_grab_focus(compose->header_last->entry);
1865 gtk_widget_grab_focus(compose->text);
1867 if (!no_extedit && prefs_common.auto_exteditor)
1868 compose_exec_ext_editor(compose);
1871 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1872 gchar *folderidentifier;
1874 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1875 folderidentifier = folder_item_get_identifier(msginfo->folder);
1876 compose_set_save_to(compose, folderidentifier);
1877 g_free(folderidentifier);
1880 undo_unblock(compose->undostruct);
1882 compose->modified = FALSE;
1883 compose_set_title(compose);
1885 compose->updating = FALSE;
1886 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1887 SCROLL_TO_CURSOR(compose);
1889 if (compose->deferred_destroy) {
1890 compose_destroy(compose);
1894 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1899 #undef INSERT_FW_HEADER
1901 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1904 GtkTextView *textview;
1905 GtkTextBuffer *textbuf;
1909 gboolean single_mail = TRUE;
1911 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1913 if (g_slist_length(msginfo_list) > 1)
1914 single_mail = FALSE;
1916 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1917 if (((MsgInfo *)msginfo->data)->folder == NULL)
1920 /* guess account from first selected message */
1922 !(account = compose_guess_forward_account_from_msginfo
1923 (msginfo_list->data)))
1924 account = cur_account;
1926 cm_return_val_if_fail(account != NULL, NULL);
1928 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1929 if (msginfo->data) {
1930 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1931 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1935 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1936 g_warning("no msginfo_list");
1940 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1942 compose->updating = TRUE;
1944 /* override from name according to folder properties */
1945 if (msginfo_list->data) {
1946 MsgInfo *msginfo = msginfo_list->data;
1948 if (msginfo->folder && msginfo->folder->prefs &&
1949 msginfo->folder->prefs->forward_with_format &&
1950 msginfo->folder->prefs->forward_override_from_format &&
1951 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1956 /* decode \-escape sequences in the internal representation of the quote format */
1957 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1958 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1961 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1962 compose->gtkaspell);
1964 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1966 quote_fmt_scan_string(tmp);
1969 buf = quote_fmt_get_buffer();
1971 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1973 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1974 quote_fmt_reset_vartable();
1980 textview = GTK_TEXT_VIEW(compose->text);
1981 textbuf = gtk_text_view_get_buffer(textview);
1982 compose_create_tags(textview, compose);
1984 undo_block(compose->undostruct);
1985 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1986 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1988 if (!is_file_exist(msgfile))
1989 g_warning("%s: file not exist\n", msgfile);
1991 compose_attach_append(compose, msgfile, msgfile,
1992 "message/rfc822", NULL);
1997 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1998 if (info->subject && *info->subject) {
1999 gchar *buf, *buf2, *p;
2001 buf = p = g_strdup(info->subject);
2002 p += subject_get_prefix_length(p);
2003 memmove(buf, p, strlen(p) + 1);
2005 buf2 = g_strdup_printf("Fw: %s", buf);
2006 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2012 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2013 _("Fw: multiple emails"));
2016 SIGNAL_BLOCK(textbuf);
2018 if (account->auto_sig)
2019 compose_insert_sig(compose, FALSE);
2021 compose_wrap_all(compose);
2023 SIGNAL_UNBLOCK(textbuf);
2025 gtk_text_buffer_get_start_iter(textbuf, &iter);
2026 gtk_text_buffer_place_cursor(textbuf, &iter);
2028 gtk_widget_grab_focus(compose->header_last->entry);
2029 undo_unblock(compose->undostruct);
2030 compose->modified = FALSE;
2031 compose_set_title(compose);
2033 compose->updating = FALSE;
2034 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2035 SCROLL_TO_CURSOR(compose);
2037 if (compose->deferred_destroy) {
2038 compose_destroy(compose);
2042 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2047 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2049 GtkTextIter start = *iter;
2050 GtkTextIter end_iter;
2051 int start_pos = gtk_text_iter_get_offset(&start);
2053 if (!compose->account->sig_sep)
2056 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2057 start_pos+strlen(compose->account->sig_sep));
2059 /* check sig separator */
2060 str = gtk_text_iter_get_text(&start, &end_iter);
2061 if (!strcmp(str, compose->account->sig_sep)) {
2063 /* check end of line (\n) */
2064 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2065 start_pos+strlen(compose->account->sig_sep));
2066 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2067 start_pos+strlen(compose->account->sig_sep)+1);
2068 tmp = gtk_text_iter_get_text(&start, &end_iter);
2069 if (!strcmp(tmp,"\n")) {
2081 static void compose_colorize_signature(Compose *compose)
2083 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2085 GtkTextIter end_iter;
2086 gtk_text_buffer_get_start_iter(buffer, &iter);
2087 while (gtk_text_iter_forward_line(&iter))
2088 if (compose_is_sig_separator(compose, buffer, &iter)) {
2089 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2090 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2094 #define BLOCK_WRAP() { \
2095 prev_autowrap = compose->autowrap; \
2096 buffer = gtk_text_view_get_buffer( \
2097 GTK_TEXT_VIEW(compose->text)); \
2098 compose->autowrap = FALSE; \
2100 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2101 G_CALLBACK(compose_changed_cb), \
2103 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2104 G_CALLBACK(text_inserted), \
2107 #define UNBLOCK_WRAP() { \
2108 compose->autowrap = prev_autowrap; \
2109 if (compose->autowrap) { \
2110 gint old = compose->draft_timeout_tag; \
2111 compose->draft_timeout_tag = -2; \
2112 compose_wrap_all(compose); \
2113 compose->draft_timeout_tag = old; \
2116 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2117 G_CALLBACK(compose_changed_cb), \
2119 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2120 G_CALLBACK(text_inserted), \
2124 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2126 Compose *compose = NULL;
2127 PrefsAccount *account = NULL;
2128 GtkTextView *textview;
2129 GtkTextBuffer *textbuf;
2133 gchar buf[BUFFSIZE];
2134 gboolean use_signing = FALSE;
2135 gboolean use_encryption = FALSE;
2136 gchar *privacy_system = NULL;
2137 int priority = PRIORITY_NORMAL;
2138 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2139 gboolean autowrap = prefs_common.autowrap;
2140 gboolean autoindent = prefs_common.auto_indent;
2141 HeaderEntry *manual_headers = NULL;
2143 cm_return_val_if_fail(msginfo != NULL, NULL);
2144 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2146 if (compose_put_existing_to_front(msginfo)) {
2150 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2151 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2152 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2153 gchar queueheader_buf[BUFFSIZE];
2156 /* Select Account from queue headers */
2157 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2158 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2159 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2160 account = account_find_from_id(id);
2162 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2163 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2164 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2165 account = account_find_from_id(id);
2167 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2168 sizeof(queueheader_buf), "NAID:")) {
2169 id = atoi(&queueheader_buf[strlen("NAID:")]);
2170 account = account_find_from_id(id);
2172 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2173 sizeof(queueheader_buf), "MAID:")) {
2174 id = atoi(&queueheader_buf[strlen("MAID:")]);
2175 account = account_find_from_id(id);
2177 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2178 sizeof(queueheader_buf), "S:")) {
2179 account = account_find_from_address(queueheader_buf, FALSE);
2181 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2182 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2183 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2184 use_signing = param;
2187 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2188 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2189 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2190 use_signing = param;
2193 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2194 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2195 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2196 use_encryption = param;
2198 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2199 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2200 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2201 use_encryption = param;
2203 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2204 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2205 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2208 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2209 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2210 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2213 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2214 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2215 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2217 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2218 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2219 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2221 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2222 sizeof(queueheader_buf), "X-Priority: ")) {
2223 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2226 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2227 sizeof(queueheader_buf), "RMID:")) {
2228 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2229 if (tokens[0] && tokens[1] && tokens[2]) {
2230 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2231 if (orig_item != NULL) {
2232 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2237 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2238 sizeof(queueheader_buf), "FMID:")) {
2239 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2240 if (tokens[0] && tokens[1] && tokens[2]) {
2241 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2242 if (orig_item != NULL) {
2243 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2248 /* Get manual headers */
2249 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2250 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2251 if (*listmh != '\0') {
2252 debug_print("Got manual headers: %s\n", listmh);
2253 manual_headers = procheader_entries_from_str(listmh);
2258 account = msginfo->folder->folder->account;
2261 if (!account && prefs_common.reedit_account_autosel) {
2262 gchar from[BUFFSIZE];
2263 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2264 extract_address(from);
2265 account = account_find_from_address(from, FALSE);
2269 account = cur_account;
2271 cm_return_val_if_fail(account != NULL, NULL);
2273 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2275 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2276 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2277 compose->autowrap = autowrap;
2278 compose->replyinfo = replyinfo;
2279 compose->fwdinfo = fwdinfo;
2281 compose->updating = TRUE;
2282 compose->priority = priority;
2284 if (privacy_system != NULL) {
2285 compose->privacy_system = privacy_system;
2286 compose_use_signing(compose, use_signing);
2287 compose_use_encryption(compose, use_encryption);
2288 compose_update_privacy_system_menu_item(compose, FALSE);
2290 activate_privacy_system(compose, account, FALSE);
2293 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2295 compose_extract_original_charset(compose);
2297 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2298 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2299 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2300 gchar queueheader_buf[BUFFSIZE];
2302 /* Set message save folder */
2303 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2304 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2305 compose_set_save_to(compose, &queueheader_buf[4]);
2307 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2308 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2310 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2315 if (compose_parse_header(compose, msginfo) < 0) {
2316 compose->updating = FALSE;
2317 compose_destroy(compose);
2320 compose_reedit_set_entry(compose, msginfo);
2322 textview = GTK_TEXT_VIEW(compose->text);
2323 textbuf = gtk_text_view_get_buffer(textview);
2324 compose_create_tags(textview, compose);
2326 mark = gtk_text_buffer_get_insert(textbuf);
2327 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2329 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2330 G_CALLBACK(compose_changed_cb),
2333 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2334 fp = procmime_get_first_encrypted_text_content(msginfo);
2336 compose_force_encryption(compose, account, TRUE, NULL);
2339 fp = procmime_get_first_text_content(msginfo);
2342 g_warning("Can't get text part\n");
2346 gboolean prev_autowrap = compose->autowrap;
2347 GtkTextBuffer *buffer = textbuf;
2349 while (fgets(buf, sizeof(buf), fp) != NULL) {
2351 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2357 compose_attach_parts(compose, msginfo);
2359 compose_colorize_signature(compose);
2361 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2362 G_CALLBACK(compose_changed_cb),
2365 if (manual_headers != NULL) {
2366 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2367 procheader_entries_free(manual_headers);
2368 compose->updating = FALSE;
2369 compose_destroy(compose);
2372 procheader_entries_free(manual_headers);
2375 gtk_widget_grab_focus(compose->text);
2377 if (prefs_common.auto_exteditor) {
2378 compose_exec_ext_editor(compose);
2380 compose->modified = FALSE;
2381 compose_set_title(compose);
2383 compose->updating = FALSE;
2384 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2385 SCROLL_TO_CURSOR(compose);
2387 if (compose->deferred_destroy) {
2388 compose_destroy(compose);
2392 compose->sig_str = account_get_signature_str(compose->account);
2394 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2399 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2406 cm_return_val_if_fail(msginfo != NULL, NULL);
2409 account = account_get_reply_account(msginfo,
2410 prefs_common.reply_account_autosel);
2411 cm_return_val_if_fail(account != NULL, NULL);
2413 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2415 compose->updating = TRUE;
2417 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2418 compose->replyinfo = NULL;
2419 compose->fwdinfo = NULL;
2421 compose_show_first_last_header(compose, TRUE);
2423 gtk_widget_grab_focus(compose->header_last->entry);
2425 filename = procmsg_get_message_file(msginfo);
2427 if (filename == NULL) {
2428 compose->updating = FALSE;
2429 compose_destroy(compose);
2434 compose->redirect_filename = filename;
2436 /* Set save folder */
2437 item = msginfo->folder;
2438 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2439 gchar *folderidentifier;
2441 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2442 folderidentifier = folder_item_get_identifier(item);
2443 compose_set_save_to(compose, folderidentifier);
2444 g_free(folderidentifier);
2447 compose_attach_parts(compose, msginfo);
2449 if (msginfo->subject)
2450 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2452 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2454 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2455 _("The body of the \"Redirect\" template has an error at line %d."));
2456 quote_fmt_reset_vartable();
2457 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2459 compose_colorize_signature(compose);
2462 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2463 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2464 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2466 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2467 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2468 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2469 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2470 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2471 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2472 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2473 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2475 if (compose->toolbar->draft_btn)
2476 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2477 if (compose->toolbar->insert_btn)
2478 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2479 if (compose->toolbar->attach_btn)
2480 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2481 if (compose->toolbar->sig_btn)
2482 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2483 if (compose->toolbar->exteditor_btn)
2484 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2485 if (compose->toolbar->linewrap_current_btn)
2486 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2487 if (compose->toolbar->linewrap_all_btn)
2488 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2490 compose->modified = FALSE;
2491 compose_set_title(compose);
2492 compose->updating = FALSE;
2493 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2494 SCROLL_TO_CURSOR(compose);
2496 if (compose->deferred_destroy) {
2497 compose_destroy(compose);
2501 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2506 GList *compose_get_compose_list(void)
2508 return compose_list;
2511 void compose_entry_append(Compose *compose, const gchar *address,
2512 ComposeEntryType type, ComposePrefType pref_type)
2514 const gchar *header;
2516 gboolean in_quote = FALSE;
2517 if (!address || *address == '\0') return;
2524 header = N_("Bcc:");
2526 case COMPOSE_REPLYTO:
2527 header = N_("Reply-To:");
2529 case COMPOSE_NEWSGROUPS:
2530 header = N_("Newsgroups:");
2532 case COMPOSE_FOLLOWUPTO:
2533 header = N_( "Followup-To:");
2535 case COMPOSE_INREPLYTO:
2536 header = N_( "In-Reply-To:");
2543 header = prefs_common_translated_header_name(header);
2545 cur = begin = (gchar *)address;
2547 /* we separate the line by commas, but not if we're inside a quoted
2549 while (*cur != '\0') {
2551 in_quote = !in_quote;
2552 if (*cur == ',' && !in_quote) {
2553 gchar *tmp = g_strdup(begin);
2555 tmp[cur-begin]='\0';
2558 while (*tmp == ' ' || *tmp == '\t')
2560 compose_add_header_entry(compose, header, tmp, pref_type);
2567 gchar *tmp = g_strdup(begin);
2569 tmp[cur-begin]='\0';
2572 while (*tmp == ' ' || *tmp == '\t')
2574 compose_add_header_entry(compose, header, tmp, pref_type);
2579 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2581 #if !GTK_CHECK_VERSION(3, 0, 0)
2582 static GdkColor yellow;
2583 static GdkColor black;
2584 static gboolean yellow_initialised = FALSE;
2586 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2587 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2592 #if !GTK_CHECK_VERSION(3, 0, 0)
2593 if (!yellow_initialised) {
2594 gdk_color_parse("#f5f6be", &yellow);
2595 gdk_color_parse("#000000", &black);
2596 yellow_initialised = gdk_colormap_alloc_color(
2597 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2598 yellow_initialised &= gdk_colormap_alloc_color(
2599 gdk_colormap_get_system(), &black, FALSE, TRUE);
2603 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2604 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2605 if (gtk_entry_get_text(entry) &&
2606 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2607 #if !GTK_CHECK_VERSION(3, 0, 0)
2608 if (yellow_initialised) {
2610 gtk_widget_modify_base(
2611 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2612 GTK_STATE_NORMAL, &yellow);
2613 gtk_widget_modify_text(
2614 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2615 GTK_STATE_NORMAL, &black);
2616 #if !GTK_CHECK_VERSION(3, 0, 0)
2623 void compose_toolbar_cb(gint action, gpointer data)
2625 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2626 Compose *compose = (Compose*)toolbar_item->parent;
2628 cm_return_if_fail(compose != NULL);
2632 compose_send_cb(NULL, compose);
2635 compose_send_later_cb(NULL, compose);
2638 compose_draft(compose, COMPOSE_QUIT_EDITING);
2641 compose_insert_file_cb(NULL, compose);
2644 compose_attach_cb(NULL, compose);
2647 compose_insert_sig(compose, FALSE);
2650 compose_ext_editor_cb(NULL, compose);
2652 case A_LINEWRAP_CURRENT:
2653 compose_beautify_paragraph(compose, NULL, TRUE);
2655 case A_LINEWRAP_ALL:
2656 compose_wrap_all_full(compose, TRUE);
2659 compose_address_cb(NULL, compose);
2662 case A_CHECK_SPELLING:
2663 compose_check_all(NULL, compose);
2671 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2676 gchar *subject = NULL;
2680 gchar **attach = NULL;
2681 gchar *inreplyto = NULL;
2682 MailField mfield = NO_FIELD_PRESENT;
2684 /* get mailto parts but skip from */
2685 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2688 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2689 mfield = TO_FIELD_PRESENT;
2692 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2694 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2696 if (!g_utf8_validate (subject, -1, NULL)) {
2697 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2698 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2701 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2703 mfield = SUBJECT_FIELD_PRESENT;
2706 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2707 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2710 gboolean prev_autowrap = compose->autowrap;
2712 compose->autowrap = FALSE;
2714 mark = gtk_text_buffer_get_insert(buffer);
2715 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2717 if (!g_utf8_validate (body, -1, NULL)) {
2718 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2719 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2722 gtk_text_buffer_insert(buffer, &iter, body, -1);
2724 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2726 compose->autowrap = prev_autowrap;
2727 if (compose->autowrap)
2728 compose_wrap_all(compose);
2729 mfield = BODY_FIELD_PRESENT;
2733 gint i = 0, att = 0;
2734 gchar *warn_files = NULL;
2735 while (attach[i] != NULL) {
2736 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2737 if (utf8_filename) {
2738 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2739 gchar *tmp = g_strdup_printf("%s%s\n",
2740 warn_files?warn_files:"",
2746 g_free(utf8_filename);
2748 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2753 alertpanel_notice(ngettext(
2754 "The following file has been attached: \n%s",
2755 "The following files have been attached: \n%s", att), warn_files);
2760 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2773 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2775 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2776 {"Cc:", NULL, TRUE},
2777 {"References:", NULL, FALSE},
2778 {"Bcc:", NULL, TRUE},
2779 {"Newsgroups:", NULL, TRUE},
2780 {"Followup-To:", NULL, TRUE},
2781 {"List-Post:", NULL, FALSE},
2782 {"X-Priority:", NULL, FALSE},
2783 {NULL, NULL, FALSE}};
2799 cm_return_val_if_fail(msginfo != NULL, -1);
2801 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2802 procheader_get_header_fields(fp, hentry);
2805 if (hentry[H_REPLY_TO].body != NULL) {
2806 if (hentry[H_REPLY_TO].body[0] != '\0') {
2808 conv_unmime_header(hentry[H_REPLY_TO].body,
2811 g_free(hentry[H_REPLY_TO].body);
2812 hentry[H_REPLY_TO].body = NULL;
2814 if (hentry[H_CC].body != NULL) {
2815 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2816 g_free(hentry[H_CC].body);
2817 hentry[H_CC].body = NULL;
2819 if (hentry[H_REFERENCES].body != NULL) {
2820 if (compose->mode == COMPOSE_REEDIT)
2821 compose->references = hentry[H_REFERENCES].body;
2823 compose->references = compose_parse_references
2824 (hentry[H_REFERENCES].body, msginfo->msgid);
2825 g_free(hentry[H_REFERENCES].body);
2827 hentry[H_REFERENCES].body = NULL;
2829 if (hentry[H_BCC].body != NULL) {
2830 if (compose->mode == COMPOSE_REEDIT)
2832 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2833 g_free(hentry[H_BCC].body);
2834 hentry[H_BCC].body = NULL;
2836 if (hentry[H_NEWSGROUPS].body != NULL) {
2837 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2838 hentry[H_NEWSGROUPS].body = NULL;
2840 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2841 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2842 compose->followup_to =
2843 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2846 g_free(hentry[H_FOLLOWUP_TO].body);
2847 hentry[H_FOLLOWUP_TO].body = NULL;
2849 if (hentry[H_LIST_POST].body != NULL) {
2850 gchar *to = NULL, *start = NULL;
2852 extract_address(hentry[H_LIST_POST].body);
2853 if (hentry[H_LIST_POST].body[0] != '\0') {
2854 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2856 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2857 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2860 g_free(compose->ml_post);
2861 compose->ml_post = to;
2864 g_free(hentry[H_LIST_POST].body);
2865 hentry[H_LIST_POST].body = NULL;
2868 /* CLAWS - X-Priority */
2869 if (compose->mode == COMPOSE_REEDIT)
2870 if (hentry[H_X_PRIORITY].body != NULL) {
2873 priority = atoi(hentry[H_X_PRIORITY].body);
2874 g_free(hentry[H_X_PRIORITY].body);
2876 hentry[H_X_PRIORITY].body = NULL;
2878 if (priority < PRIORITY_HIGHEST ||
2879 priority > PRIORITY_LOWEST)
2880 priority = PRIORITY_NORMAL;
2882 compose->priority = priority;
2885 if (compose->mode == COMPOSE_REEDIT) {
2886 if (msginfo->inreplyto && *msginfo->inreplyto)
2887 compose->inreplyto = g_strdup(msginfo->inreplyto);
2891 if (msginfo->msgid && *msginfo->msgid)
2892 compose->inreplyto = g_strdup(msginfo->msgid);
2894 if (!compose->references) {
2895 if (msginfo->msgid && *msginfo->msgid) {
2896 if (msginfo->inreplyto && *msginfo->inreplyto)
2897 compose->references =
2898 g_strdup_printf("<%s>\n\t<%s>",
2902 compose->references =
2903 g_strconcat("<", msginfo->msgid, ">",
2905 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2906 compose->references =
2907 g_strconcat("<", msginfo->inreplyto, ">",
2915 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2920 cm_return_val_if_fail(msginfo != NULL, -1);
2922 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2923 procheader_get_header_fields(fp, entries);
2927 while (he != NULL && he->name != NULL) {
2929 GtkListStore *model = NULL;
2931 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
2932 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
2933 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
2934 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
2935 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
2942 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2944 GSList *ref_id_list, *cur;
2948 ref_id_list = references_list_append(NULL, ref);
2949 if (!ref_id_list) return NULL;
2950 if (msgid && *msgid)
2951 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2956 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2957 /* "<" + Message-ID + ">" + CR+LF+TAB */
2958 len += strlen((gchar *)cur->data) + 5;
2960 if (len > MAX_REFERENCES_LEN) {
2961 /* remove second message-ID */
2962 if (ref_id_list && ref_id_list->next &&
2963 ref_id_list->next->next) {
2964 g_free(ref_id_list->next->data);
2965 ref_id_list = g_slist_remove
2966 (ref_id_list, ref_id_list->next->data);
2968 slist_free_strings(ref_id_list);
2969 g_slist_free(ref_id_list);
2976 new_ref = g_string_new("");
2977 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2978 if (new_ref->len > 0)
2979 g_string_append(new_ref, "\n\t");
2980 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2983 slist_free_strings(ref_id_list);
2984 g_slist_free(ref_id_list);
2986 new_ref_str = new_ref->str;
2987 g_string_free(new_ref, FALSE);
2992 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2993 const gchar *fmt, const gchar *qmark,
2994 const gchar *body, gboolean rewrap,
2995 gboolean need_unescape,
2996 const gchar *err_msg)
2998 MsgInfo* dummyinfo = NULL;
2999 gchar *quote_str = NULL;
3001 gboolean prev_autowrap;
3002 const gchar *trimmed_body = body;
3003 gint cursor_pos = -1;
3004 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3005 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3010 SIGNAL_BLOCK(buffer);
3013 dummyinfo = compose_msginfo_new_from_compose(compose);
3014 msginfo = dummyinfo;
3017 if (qmark != NULL) {
3019 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3020 compose->gtkaspell);
3022 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3024 quote_fmt_scan_string(qmark);
3027 buf = quote_fmt_get_buffer();
3029 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3031 Xstrdup_a(quote_str, buf, goto error)
3034 if (fmt && *fmt != '\0') {
3037 while (*trimmed_body == '\n')
3041 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3042 compose->gtkaspell);
3044 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3046 if (need_unescape) {
3049 /* decode \-escape sequences in the internal representation of the quote format */
3050 tmp = g_malloc(strlen(fmt)+1);
3051 pref_get_unescaped_pref(tmp, fmt);
3052 quote_fmt_scan_string(tmp);
3056 quote_fmt_scan_string(fmt);
3060 buf = quote_fmt_get_buffer();
3062 gint line = quote_fmt_get_line();
3063 alertpanel_error(err_msg, line);
3069 prev_autowrap = compose->autowrap;
3070 compose->autowrap = FALSE;
3072 mark = gtk_text_buffer_get_insert(buffer);
3073 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3074 if (g_utf8_validate(buf, -1, NULL)) {
3075 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3077 gchar *tmpout = NULL;
3078 tmpout = conv_codeset_strdup
3079 (buf, conv_get_locale_charset_str_no_utf8(),
3081 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3083 tmpout = g_malloc(strlen(buf)*2+1);
3084 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3086 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3090 cursor_pos = quote_fmt_get_cursor_pos();
3091 if (cursor_pos == -1)
3092 cursor_pos = gtk_text_iter_get_offset(&iter);
3093 compose->set_cursor_pos = cursor_pos;
3095 gtk_text_buffer_get_start_iter(buffer, &iter);
3096 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3097 gtk_text_buffer_place_cursor(buffer, &iter);
3099 compose->autowrap = prev_autowrap;
3100 if (compose->autowrap && rewrap)
3101 compose_wrap_all(compose);
3108 SIGNAL_UNBLOCK(buffer);
3110 procmsg_msginfo_free( dummyinfo );
3115 /* if ml_post is of type addr@host and from is of type
3116 * addr-anything@host, return TRUE
3118 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3120 gchar *left_ml = NULL;
3121 gchar *right_ml = NULL;
3122 gchar *left_from = NULL;
3123 gchar *right_from = NULL;
3124 gboolean result = FALSE;
3126 if (!ml_post || !from)
3129 left_ml = g_strdup(ml_post);
3130 if (strstr(left_ml, "@")) {
3131 right_ml = strstr(left_ml, "@")+1;
3132 *(strstr(left_ml, "@")) = '\0';
3135 left_from = g_strdup(from);
3136 if (strstr(left_from, "@")) {
3137 right_from = strstr(left_from, "@")+1;
3138 *(strstr(left_from, "@")) = '\0';
3141 if (left_ml && left_from && right_ml && right_from
3142 && !strncmp(left_from, left_ml, strlen(left_ml))
3143 && !strcmp(right_from, right_ml)) {
3152 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3153 gboolean respect_default_to)
3157 if (!folder || !folder->prefs)
3160 if (respect_default_to && folder->prefs->enable_default_to) {
3161 compose_entry_append(compose, folder->prefs->default_to,
3162 COMPOSE_TO, PREF_FOLDER);
3163 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3165 if (folder->prefs->enable_default_cc)
3166 compose_entry_append(compose, folder->prefs->default_cc,
3167 COMPOSE_CC, PREF_FOLDER);
3168 if (folder->prefs->enable_default_bcc)
3169 compose_entry_append(compose, folder->prefs->default_bcc,
3170 COMPOSE_BCC, PREF_FOLDER);
3171 if (folder->prefs->enable_default_replyto)
3172 compose_entry_append(compose, folder->prefs->default_replyto,
3173 COMPOSE_REPLYTO, PREF_FOLDER);
3176 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3181 if (!compose || !msginfo)
3184 if (msginfo->subject && *msginfo->subject) {
3185 buf = p = g_strdup(msginfo->subject);
3186 p += subject_get_prefix_length(p);
3187 memmove(buf, p, strlen(p) + 1);
3189 buf2 = g_strdup_printf("Re: %s", buf);
3190 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3195 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3198 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3199 gboolean to_all, gboolean to_ml,
3201 gboolean followup_and_reply_to)
3203 GSList *cc_list = NULL;
3206 gchar *replyto = NULL;
3207 gchar *ac_email = NULL;
3209 gboolean reply_to_ml = FALSE;
3210 gboolean default_reply_to = FALSE;
3212 cm_return_if_fail(compose->account != NULL);
3213 cm_return_if_fail(msginfo != NULL);
3215 reply_to_ml = to_ml && compose->ml_post;
3217 default_reply_to = msginfo->folder &&
3218 msginfo->folder->prefs->enable_default_reply_to;
3220 if (compose->account->protocol != A_NNTP) {
3221 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3223 if (reply_to_ml && !default_reply_to) {
3225 gboolean is_subscr = is_subscription(compose->ml_post,
3228 /* normal answer to ml post with a reply-to */
3229 compose_entry_append(compose,
3231 COMPOSE_TO, PREF_ML);
3232 if (compose->replyto)
3233 compose_entry_append(compose,
3235 COMPOSE_CC, PREF_ML);
3237 /* answer to subscription confirmation */
3238 if (compose->replyto)
3239 compose_entry_append(compose,
3241 COMPOSE_TO, PREF_ML);
3242 else if (msginfo->from)
3243 compose_entry_append(compose,
3245 COMPOSE_TO, PREF_ML);
3248 else if (!(to_all || to_sender) && default_reply_to) {
3249 compose_entry_append(compose,
3250 msginfo->folder->prefs->default_reply_to,
3251 COMPOSE_TO, PREF_FOLDER);
3252 compose_entry_mark_default_to(compose,
3253 msginfo->folder->prefs->default_reply_to);
3258 Xstrdup_a(tmp1, msginfo->from, return);
3259 extract_address(tmp1);
3260 if (to_all || to_sender ||
3261 !account_find_from_address(tmp1, FALSE))
3262 compose_entry_append(compose,
3263 (compose->replyto && !to_sender)
3264 ? compose->replyto :
3265 msginfo->from ? msginfo->from : "",
3266 COMPOSE_TO, PREF_NONE);
3267 else if (!to_all && !to_sender) {
3268 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3269 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3270 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3271 if (compose->replyto) {
3272 compose_entry_append(compose,
3274 COMPOSE_TO, PREF_NONE);
3276 compose_entry_append(compose,
3277 msginfo->from ? msginfo->from : "",
3278 COMPOSE_TO, PREF_NONE);
3281 /* replying to own mail, use original recp */
3282 compose_entry_append(compose,
3283 msginfo->to ? msginfo->to : "",
3284 COMPOSE_TO, PREF_NONE);
3285 compose_entry_append(compose,
3286 msginfo->cc ? msginfo->cc : "",
3287 COMPOSE_CC, PREF_NONE);
3292 if (to_sender || (compose->followup_to &&
3293 !strncmp(compose->followup_to, "poster", 6)))
3294 compose_entry_append
3296 (compose->replyto ? compose->replyto :
3297 msginfo->from ? msginfo->from : ""),
3298 COMPOSE_TO, PREF_NONE);
3300 else if (followup_and_reply_to || to_all) {
3301 compose_entry_append
3303 (compose->replyto ? compose->replyto :
3304 msginfo->from ? msginfo->from : ""),
3305 COMPOSE_TO, PREF_NONE);
3307 compose_entry_append
3309 compose->followup_to ? compose->followup_to :
3310 compose->newsgroups ? compose->newsgroups : "",
3311 COMPOSE_NEWSGROUPS, PREF_NONE);
3314 compose_entry_append
3316 compose->followup_to ? compose->followup_to :
3317 compose->newsgroups ? compose->newsgroups : "",
3318 COMPOSE_NEWSGROUPS, PREF_NONE);
3320 compose_reply_set_subject(compose, msginfo);
3322 if (to_ml && compose->ml_post) return;
3323 if (!to_all || compose->account->protocol == A_NNTP) return;
3325 if (compose->replyto) {
3326 Xstrdup_a(replyto, compose->replyto, return);
3327 extract_address(replyto);
3329 if (msginfo->from) {
3330 Xstrdup_a(from, msginfo->from, return);
3331 extract_address(from);
3334 if (replyto && from)
3335 cc_list = address_list_append_with_comments(cc_list, from);
3336 if (to_all && msginfo->folder &&
3337 msginfo->folder->prefs->enable_default_reply_to)
3338 cc_list = address_list_append_with_comments(cc_list,
3339 msginfo->folder->prefs->default_reply_to);
3340 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3341 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3343 ac_email = g_utf8_strdown(compose->account->address, -1);
3346 for (cur = cc_list; cur != NULL; cur = cur->next) {
3347 gchar *addr = g_utf8_strdown(cur->data, -1);
3348 extract_address(addr);
3350 if (strcmp(ac_email, addr))
3351 compose_entry_append(compose, (gchar *)cur->data,
3352 COMPOSE_CC, PREF_NONE);
3354 debug_print("Cc address same as compose account's, ignoring\n");
3359 slist_free_strings(cc_list);
3360 g_slist_free(cc_list);
3366 #define SET_ENTRY(entry, str) \
3369 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3372 #define SET_ADDRESS(type, str) \
3375 compose_entry_append(compose, str, type, PREF_NONE); \
3378 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3380 cm_return_if_fail(msginfo != NULL);
3382 SET_ENTRY(subject_entry, msginfo->subject);
3383 SET_ENTRY(from_name, msginfo->from);
3384 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3385 SET_ADDRESS(COMPOSE_CC, compose->cc);
3386 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3387 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3388 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3389 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3391 compose_update_priority_menu_item(compose);
3392 compose_update_privacy_system_menu_item(compose, FALSE);
3393 compose_show_first_last_header(compose, TRUE);
3399 static void compose_insert_sig(Compose *compose, gboolean replace)
3401 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3402 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3404 GtkTextIter iter, iter_end;
3405 gint cur_pos, ins_pos;
3406 gboolean prev_autowrap;
3407 gboolean found = FALSE;
3408 gboolean exists = FALSE;
3410 cm_return_if_fail(compose->account != NULL);
3414 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3415 G_CALLBACK(compose_changed_cb),
3418 mark = gtk_text_buffer_get_insert(buffer);
3419 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3420 cur_pos = gtk_text_iter_get_offset (&iter);
3423 gtk_text_buffer_get_end_iter(buffer, &iter);
3425 exists = (compose->sig_str != NULL);
3428 GtkTextIter first_iter, start_iter, end_iter;
3430 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3432 if (!exists || compose->sig_str[0] == '\0')
3435 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3436 compose->signature_tag);
3439 /* include previous \n\n */
3440 gtk_text_iter_backward_chars(&first_iter, 1);
3441 start_iter = first_iter;
3442 end_iter = first_iter;
3444 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3445 compose->signature_tag);
3446 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3447 compose->signature_tag);
3449 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3455 g_free(compose->sig_str);
3456 compose->sig_str = account_get_signature_str(compose->account);
3458 cur_pos = gtk_text_iter_get_offset(&iter);
3460 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3461 g_free(compose->sig_str);
3462 compose->sig_str = NULL;
3464 if (compose->sig_inserted == FALSE)
3465 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3466 compose->sig_inserted = TRUE;
3468 cur_pos = gtk_text_iter_get_offset(&iter);
3469 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3471 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3472 gtk_text_iter_forward_chars(&iter, 1);
3473 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3474 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3476 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3477 cur_pos = gtk_text_buffer_get_char_count (buffer);
3480 /* put the cursor where it should be
3481 * either where the quote_fmt says, either where it was */
3482 if (compose->set_cursor_pos < 0)
3483 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3485 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3486 compose->set_cursor_pos);
3488 compose->set_cursor_pos = -1;
3489 gtk_text_buffer_place_cursor(buffer, &iter);
3490 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3491 G_CALLBACK(compose_changed_cb),
3497 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3500 GtkTextBuffer *buffer;
3503 const gchar *cur_encoding;
3504 gchar buf[BUFFSIZE];
3507 gboolean prev_autowrap;
3508 gboolean badtxt = FALSE;
3509 struct stat file_stat;
3512 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3514 /* get the size of the file we are about to insert */
3515 ret = g_stat(file, &file_stat);
3517 gchar *shortfile = g_path_get_basename(file);
3518 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3520 return COMPOSE_INSERT_NO_FILE;
3521 } else if (prefs_common.warn_large_insert == TRUE) {
3523 /* ask user for confirmation if the file is large */
3524 if (prefs_common.warn_large_insert_size < 0 ||
3525 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3529 msg = g_strdup_printf(_("You are about to insert a file of %s "
3530 "in the message body. Are you sure you want to do that?"),
3531 to_human_readable(file_stat.st_size));
3532 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3533 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3536 /* do we ask for confirmation next time? */
3537 if (aval & G_ALERTDISABLE) {
3538 /* no confirmation next time, disable feature in preferences */
3539 aval &= ~G_ALERTDISABLE;
3540 prefs_common.warn_large_insert = FALSE;
3543 /* abort file insertion if user canceled action */
3544 if (aval != G_ALERTALTERNATE) {
3545 return COMPOSE_INSERT_NO_FILE;
3551 if ((fp = g_fopen(file, "rb")) == NULL) {
3552 FILE_OP_ERROR(file, "fopen");
3553 return COMPOSE_INSERT_READ_ERROR;
3556 prev_autowrap = compose->autowrap;
3557 compose->autowrap = FALSE;
3559 text = GTK_TEXT_VIEW(compose->text);
3560 buffer = gtk_text_view_get_buffer(text);
3561 mark = gtk_text_buffer_get_insert(buffer);
3562 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3564 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3565 G_CALLBACK(text_inserted),
3568 cur_encoding = conv_get_locale_charset_str_no_utf8();
3570 while (fgets(buf, sizeof(buf), fp) != NULL) {
3573 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3574 str = g_strdup(buf);
3576 str = conv_codeset_strdup
3577 (buf, cur_encoding, CS_INTERNAL);
3580 /* strip <CR> if DOS/Windows file,
3581 replace <CR> with <LF> if Macintosh file. */
3584 if (len > 0 && str[len - 1] != '\n') {
3586 if (str[len] == '\r') str[len] = '\n';
3589 gtk_text_buffer_insert(buffer, &iter, str, -1);
3593 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3594 G_CALLBACK(text_inserted),
3596 compose->autowrap = prev_autowrap;
3597 if (compose->autowrap)
3598 compose_wrap_all(compose);
3603 return COMPOSE_INSERT_INVALID_CHARACTER;
3605 return COMPOSE_INSERT_SUCCESS;
3608 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3609 const gchar *filename,
3610 const gchar *content_type,
3611 const gchar *charset)
3619 GtkListStore *store;
3621 gboolean has_binary = FALSE;
3623 if (!is_file_exist(file)) {
3624 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3625 gboolean result = FALSE;
3626 if (file_from_uri && is_file_exist(file_from_uri)) {
3627 result = compose_attach_append(
3628 compose, file_from_uri,
3629 filename, content_type,
3632 g_free(file_from_uri);
3635 alertpanel_error("File %s doesn't exist\n", filename);
3638 if ((size = get_file_size(file)) < 0) {
3639 alertpanel_error("Can't get file size of %s\n", filename);
3643 alertpanel_error(_("File %s is empty."), filename);
3646 if ((fp = g_fopen(file, "rb")) == NULL) {
3647 alertpanel_error(_("Can't read %s."), filename);
3652 ainfo = g_new0(AttachInfo, 1);
3653 auto_ainfo = g_auto_pointer_new_with_free
3654 (ainfo, (GFreeFunc) compose_attach_info_free);
3655 ainfo->file = g_strdup(file);
3658 ainfo->content_type = g_strdup(content_type);
3659 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3661 MsgFlags flags = {0, 0};
3663 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3664 ainfo->encoding = ENC_7BIT;
3666 ainfo->encoding = ENC_8BIT;
3668 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3669 if (msginfo && msginfo->subject)
3670 name = g_strdup(msginfo->subject);
3672 name = g_path_get_basename(filename ? filename : file);
3674 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3676 procmsg_msginfo_free(msginfo);
3678 if (!g_ascii_strncasecmp(content_type, "text/", 5)) {
3679 ainfo->charset = g_strdup(charset);
3680 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3682 ainfo->encoding = ENC_BASE64;
3684 name = g_path_get_basename(filename ? filename : file);
3685 ainfo->name = g_strdup(name);
3689 ainfo->content_type = procmime_get_mime_type(file);
3690 if (!ainfo->content_type) {
3691 ainfo->content_type =
3692 g_strdup("application/octet-stream");
3693 ainfo->encoding = ENC_BASE64;
3694 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text/", 5))
3696 procmime_get_encoding_for_text_file(file, &has_binary);
3698 ainfo->encoding = ENC_BASE64;
3699 name = g_path_get_basename(filename ? filename : file);
3700 ainfo->name = g_strdup(name);
3704 if (ainfo->name != NULL
3705 && !strcmp(ainfo->name, ".")) {
3706 g_free(ainfo->name);
3710 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3711 g_free(ainfo->content_type);
3712 ainfo->content_type = g_strdup("application/octet-stream");
3713 g_free(ainfo->charset);
3714 ainfo->charset = NULL;
3717 ainfo->size = (goffset)size;
3718 size_text = to_human_readable((goffset)size);
3720 store = GTK_LIST_STORE(gtk_tree_view_get_model
3721 (GTK_TREE_VIEW(compose->attach_clist)));
3723 gtk_list_store_append(store, &iter);
3724 gtk_list_store_set(store, &iter,
3725 COL_MIMETYPE, ainfo->content_type,
3726 COL_SIZE, size_text,
3727 COL_NAME, ainfo->name,
3728 COL_CHARSET, ainfo->charset,
3730 COL_AUTODATA, auto_ainfo,
3733 g_auto_pointer_free(auto_ainfo);
3734 compose_attach_update_label(compose);
3738 static void compose_use_signing(Compose *compose, gboolean use_signing)
3740 compose->use_signing = use_signing;
3741 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3744 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3746 compose->use_encryption = use_encryption;
3747 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3750 #define NEXT_PART_NOT_CHILD(info) \
3752 node = info->node; \
3753 while (node->children) \
3754 node = g_node_last_child(node); \
3755 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3758 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3762 MimeInfo *firsttext = NULL;
3763 MimeInfo *encrypted = NULL;
3766 const gchar *partname = NULL;
3768 mimeinfo = procmime_scan_message(msginfo);
3769 if (!mimeinfo) return;
3771 if (mimeinfo->node->children == NULL) {
3772 procmime_mimeinfo_free_all(mimeinfo);
3776 /* find first content part */
3777 child = (MimeInfo *) mimeinfo->node->children->data;
3778 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3779 child = (MimeInfo *)child->node->children->data;
3782 if (child->type == MIMETYPE_TEXT) {
3784 debug_print("First text part found\n");
3785 } else if (compose->mode == COMPOSE_REEDIT &&
3786 child->type == MIMETYPE_APPLICATION &&
3787 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3788 encrypted = (MimeInfo *)child->node->parent->data;
3791 child = (MimeInfo *) mimeinfo->node->children->data;
3792 while (child != NULL) {
3795 if (child == encrypted) {
3796 /* skip this part of tree */
3797 NEXT_PART_NOT_CHILD(child);
3801 if (child->type == MIMETYPE_MULTIPART) {
3802 /* get the actual content */
3803 child = procmime_mimeinfo_next(child);
3807 if (child == firsttext) {
3808 child = procmime_mimeinfo_next(child);
3812 outfile = procmime_get_tmp_file_name(child);
3813 if ((err = procmime_get_part(outfile, child)) < 0)
3814 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3816 gchar *content_type;
3818 content_type = procmime_get_content_type_str(child->type, child->subtype);
3820 /* if we meet a pgp signature, we don't attach it, but
3821 * we force signing. */
3822 if ((strcmp(content_type, "application/pgp-signature") &&
3823 strcmp(content_type, "application/pkcs7-signature") &&
3824 strcmp(content_type, "application/x-pkcs7-signature"))
3825 || compose->mode == COMPOSE_REDIRECT) {
3826 partname = procmime_mimeinfo_get_parameter(child, "filename");
3827 if (partname == NULL)
3828 partname = procmime_mimeinfo_get_parameter(child, "name");
3829 if (partname == NULL)
3831 compose_attach_append(compose, outfile,
3832 partname, content_type,
3833 procmime_mimeinfo_get_parameter(child, "charset"));
3835 compose_force_signing(compose, compose->account, NULL);
3837 g_free(content_type);
3840 NEXT_PART_NOT_CHILD(child);
3842 procmime_mimeinfo_free_all(mimeinfo);
3845 #undef NEXT_PART_NOT_CHILD
3850 WAIT_FOR_INDENT_CHAR,
3851 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3854 /* return indent length, we allow:
3855 indent characters followed by indent characters or spaces/tabs,
3856 alphabets and numbers immediately followed by indent characters,
3857 and the repeating sequences of the above
3858 If quote ends with multiple spaces, only the first one is included. */
3859 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3860 const GtkTextIter *start, gint *len)
3862 GtkTextIter iter = *start;
3866 IndentState state = WAIT_FOR_INDENT_CHAR;
3869 gint alnum_count = 0;
3870 gint space_count = 0;
3873 if (prefs_common.quote_chars == NULL) {
3877 while (!gtk_text_iter_ends_line(&iter)) {
3878 wc = gtk_text_iter_get_char(&iter);
3879 if (g_unichar_iswide(wc))
3881 clen = g_unichar_to_utf8(wc, ch);
3885 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3886 is_space = g_unichar_isspace(wc);
3888 if (state == WAIT_FOR_INDENT_CHAR) {
3889 if (!is_indent && !g_unichar_isalnum(wc))
3892 quote_len += alnum_count + space_count + 1;
3893 alnum_count = space_count = 0;
3894 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3897 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3898 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3902 else if (is_indent) {
3903 quote_len += alnum_count + space_count + 1;
3904 alnum_count = space_count = 0;
3907 state = WAIT_FOR_INDENT_CHAR;
3911 gtk_text_iter_forward_char(&iter);
3914 if (quote_len > 0 && space_count > 0)
3920 if (quote_len > 0) {
3922 gtk_text_iter_forward_chars(&iter, quote_len);
3923 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3929 /* return >0 if the line is itemized */
3930 static int compose_itemized_length(GtkTextBuffer *buffer,
3931 const GtkTextIter *start)
3933 GtkTextIter iter = *start;
3938 if (gtk_text_iter_ends_line(&iter))
3943 wc = gtk_text_iter_get_char(&iter);
3944 if (!g_unichar_isspace(wc))
3946 gtk_text_iter_forward_char(&iter);
3947 if (gtk_text_iter_ends_line(&iter))
3951 clen = g_unichar_to_utf8(wc, ch);
3955 if (!strchr("*-+", ch[0]))
3958 gtk_text_iter_forward_char(&iter);
3959 if (gtk_text_iter_ends_line(&iter))
3961 wc = gtk_text_iter_get_char(&iter);
3962 if (g_unichar_isspace(wc)) {
3968 /* return the string at the start of the itemization */
3969 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3970 const GtkTextIter *start)
3972 GtkTextIter iter = *start;
3975 GString *item_chars = g_string_new("");
3978 if (gtk_text_iter_ends_line(&iter))
3983 wc = gtk_text_iter_get_char(&iter);
3984 if (!g_unichar_isspace(wc))
3986 gtk_text_iter_forward_char(&iter);
3987 if (gtk_text_iter_ends_line(&iter))
3989 g_string_append_unichar(item_chars, wc);
3992 str = item_chars->str;
3993 g_string_free(item_chars, FALSE);
3997 /* return the number of spaces at a line's start */
3998 static int compose_left_offset_length(GtkTextBuffer *buffer,
3999 const GtkTextIter *start)
4001 GtkTextIter iter = *start;
4004 if (gtk_text_iter_ends_line(&iter))
4008 wc = gtk_text_iter_get_char(&iter);
4009 if (!g_unichar_isspace(wc))
4012 gtk_text_iter_forward_char(&iter);
4013 if (gtk_text_iter_ends_line(&iter))
4017 gtk_text_iter_forward_char(&iter);
4018 if (gtk_text_iter_ends_line(&iter))
4023 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
4024 const GtkTextIter *start,
4025 GtkTextIter *break_pos,
4029 GtkTextIter iter = *start, line_end = *start;
4030 PangoLogAttr *attrs;
4037 gboolean can_break = FALSE;
4038 gboolean do_break = FALSE;
4039 gboolean was_white = FALSE;
4040 gboolean prev_dont_break = FALSE;
4042 gtk_text_iter_forward_to_line_end(&line_end);
4043 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
4044 len = g_utf8_strlen(str, -1);
4048 g_warning("compose_get_line_break_pos: len = 0!\n");
4052 /* g_print("breaking line: %d: %s (len = %d)\n",
4053 gtk_text_iter_get_line(&iter), str, len); */
4055 attrs = g_new(PangoLogAttr, len + 1);
4057 pango_default_break(str, -1, NULL, attrs, len + 1);
4061 /* skip quote and leading spaces */
4062 for (i = 0; *p != '\0' && i < len; i++) {
4065 wc = g_utf8_get_char(p);
4066 if (i >= quote_len && !g_unichar_isspace(wc))
4068 if (g_unichar_iswide(wc))
4070 else if (*p == '\t')
4074 p = g_utf8_next_char(p);
4077 for (; *p != '\0' && i < len; i++) {
4078 PangoLogAttr *attr = attrs + i;
4082 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
4085 was_white = attr->is_white;
4087 /* don't wrap URI */
4088 if ((uri_len = get_uri_len(p)) > 0) {
4090 if (pos > 0 && col > max_col) {
4100 wc = g_utf8_get_char(p);
4101 if (g_unichar_iswide(wc)) {
4103 if (prev_dont_break && can_break && attr->is_line_break)
4105 } else if (*p == '\t')
4109 if (pos > 0 && col > max_col) {
4114 if (*p == '-' || *p == '/')
4115 prev_dont_break = TRUE;
4117 prev_dont_break = FALSE;
4119 p = g_utf8_next_char(p);
4123 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4128 *break_pos = *start;
4129 gtk_text_iter_set_line_offset(break_pos, pos);
4134 static gboolean compose_join_next_line(Compose *compose,
4135 GtkTextBuffer *buffer,
4137 const gchar *quote_str)
4139 GtkTextIter iter_ = *iter, cur, prev, next, end;
4140 PangoLogAttr attrs[3];
4142 gchar *next_quote_str;
4145 gboolean keep_cursor = FALSE;
4147 if (!gtk_text_iter_forward_line(&iter_) ||
4148 gtk_text_iter_ends_line(&iter_)) {
4151 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4153 if ((quote_str || next_quote_str) &&
4154 strcmp2(quote_str, next_quote_str) != 0) {
4155 g_free(next_quote_str);
4158 g_free(next_quote_str);
4161 if (quote_len > 0) {
4162 gtk_text_iter_forward_chars(&end, quote_len);
4163 if (gtk_text_iter_ends_line(&end)) {
4168 /* don't join itemized lines */
4169 if (compose_itemized_length(buffer, &end) > 0) {
4173 /* don't join signature separator */
4174 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4177 /* delete quote str */
4179 gtk_text_buffer_delete(buffer, &iter_, &end);
4181 /* don't join line breaks put by the user */
4183 gtk_text_iter_backward_char(&cur);
4184 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4185 gtk_text_iter_forward_char(&cur);
4189 gtk_text_iter_forward_char(&cur);
4190 /* delete linebreak and extra spaces */
4191 while (gtk_text_iter_backward_char(&cur)) {
4192 wc1 = gtk_text_iter_get_char(&cur);
4193 if (!g_unichar_isspace(wc1))
4198 while (!gtk_text_iter_ends_line(&cur)) {
4199 wc1 = gtk_text_iter_get_char(&cur);
4200 if (!g_unichar_isspace(wc1))
4202 gtk_text_iter_forward_char(&cur);
4205 if (!gtk_text_iter_equal(&prev, &next)) {
4208 mark = gtk_text_buffer_get_insert(buffer);
4209 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4210 if (gtk_text_iter_equal(&prev, &cur))
4212 gtk_text_buffer_delete(buffer, &prev, &next);
4216 /* insert space if required */
4217 gtk_text_iter_backward_char(&prev);
4218 wc1 = gtk_text_iter_get_char(&prev);
4219 wc2 = gtk_text_iter_get_char(&next);
4220 gtk_text_iter_forward_char(&next);
4221 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4222 pango_default_break(str, -1, NULL, attrs, 3);
4223 if (!attrs[1].is_line_break ||
4224 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4225 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4227 gtk_text_iter_backward_char(&iter_);
4228 gtk_text_buffer_place_cursor(buffer, &iter_);
4237 #define ADD_TXT_POS(bp_, ep_, pti_) \
4238 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4239 last = last->next; \
4240 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4241 last->next = NULL; \
4243 g_warning("alloc error scanning URIs\n"); \
4246 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4248 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4249 GtkTextBuffer *buffer;
4250 GtkTextIter iter, break_pos, end_of_line;
4251 gchar *quote_str = NULL;
4253 gboolean wrap_quote = prefs_common.linewrap_quote;
4254 gboolean prev_autowrap = compose->autowrap;
4255 gint startq_offset = -1, noq_offset = -1;
4256 gint uri_start = -1, uri_stop = -1;
4257 gint nouri_start = -1, nouri_stop = -1;
4258 gint num_blocks = 0;
4259 gint quotelevel = -1;
4260 gboolean modified = force;
4261 gboolean removed = FALSE;
4262 gboolean modified_before_remove = FALSE;
4264 gboolean start = TRUE;
4265 gint itemized_len = 0, rem_item_len = 0;
4266 gchar *itemized_chars = NULL;
4267 gboolean item_continuation = FALSE;
4272 if (compose->draft_timeout_tag == -2) {
4276 compose->autowrap = FALSE;
4278 buffer = gtk_text_view_get_buffer(text);
4279 undo_wrapping(compose->undostruct, TRUE);
4284 mark = gtk_text_buffer_get_insert(buffer);
4285 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4289 if (compose->draft_timeout_tag == -2) {
4290 if (gtk_text_iter_ends_line(&iter)) {
4291 while (gtk_text_iter_ends_line(&iter) &&
4292 gtk_text_iter_forward_line(&iter))
4295 while (gtk_text_iter_backward_line(&iter)) {
4296 if (gtk_text_iter_ends_line(&iter)) {
4297 gtk_text_iter_forward_line(&iter);
4303 /* move to line start */
4304 gtk_text_iter_set_line_offset(&iter, 0);
4307 itemized_len = compose_itemized_length(buffer, &iter);
4309 if (!itemized_len) {
4310 itemized_len = compose_left_offset_length(buffer, &iter);
4311 item_continuation = TRUE;
4315 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4317 /* go until paragraph end (empty line) */
4318 while (start || !gtk_text_iter_ends_line(&iter)) {
4319 gchar *scanpos = NULL;
4320 /* parse table - in order of priority */
4322 const gchar *needle; /* token */
4324 /* token search function */
4325 gchar *(*search) (const gchar *haystack,
4326 const gchar *needle);
4327 /* part parsing function */
4328 gboolean (*parse) (const gchar *start,
4329 const gchar *scanpos,
4333 /* part to URI function */
4334 gchar *(*build_uri) (const gchar *bp,
4338 static struct table parser[] = {
4339 {"http://", strcasestr, get_uri_part, make_uri_string},
4340 {"https://", strcasestr, get_uri_part, make_uri_string},
4341 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4342 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4343 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4344 {"www.", strcasestr, get_uri_part, make_http_string},
4345 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4346 {"@", strcasestr, get_email_part, make_email_string}
4348 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4349 gint last_index = PARSE_ELEMS;
4351 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4355 if (!prev_autowrap && num_blocks == 0) {
4357 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4358 G_CALLBACK(text_inserted),
4361 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4364 uri_start = uri_stop = -1;
4366 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4369 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4370 if (startq_offset == -1)
4371 startq_offset = gtk_text_iter_get_offset(&iter);
4372 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4373 if (quotelevel > 2) {
4374 /* recycle colors */
4375 if (prefs_common.recycle_quote_colors)
4384 if (startq_offset == -1)
4385 noq_offset = gtk_text_iter_get_offset(&iter);
4389 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4392 if (gtk_text_iter_ends_line(&iter)) {
4394 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4395 prefs_common.linewrap_len,
4397 GtkTextIter prev, next, cur;
4398 if (prev_autowrap != FALSE || force) {
4399 compose->automatic_break = TRUE;
4401 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4402 compose->automatic_break = FALSE;
4403 if (itemized_len && compose->autoindent) {
4404 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4405 if (!item_continuation)
4406 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4408 } else if (quote_str && wrap_quote) {
4409 compose->automatic_break = TRUE;
4411 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4412 compose->automatic_break = FALSE;
4413 if (itemized_len && compose->autoindent) {
4414 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4415 if (!item_continuation)
4416 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4420 /* remove trailing spaces */
4422 rem_item_len = itemized_len;
4423 while (compose->autoindent && rem_item_len-- > 0)
4424 gtk_text_iter_backward_char(&cur);
4425 gtk_text_iter_backward_char(&cur);
4428 while (!gtk_text_iter_starts_line(&cur)) {
4431 gtk_text_iter_backward_char(&cur);
4432 wc = gtk_text_iter_get_char(&cur);
4433 if (!g_unichar_isspace(wc))
4437 if (!gtk_text_iter_equal(&prev, &next)) {
4438 gtk_text_buffer_delete(buffer, &prev, &next);
4440 gtk_text_iter_forward_char(&break_pos);
4444 gtk_text_buffer_insert(buffer, &break_pos,
4448 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4450 /* move iter to current line start */
4451 gtk_text_iter_set_line_offset(&iter, 0);
4458 /* move iter to next line start */
4464 if (!prev_autowrap && num_blocks > 0) {
4466 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4467 G_CALLBACK(text_inserted),
4471 while (!gtk_text_iter_ends_line(&end_of_line)) {
4472 gtk_text_iter_forward_char(&end_of_line);
4474 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4476 nouri_start = gtk_text_iter_get_offset(&iter);
4477 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4479 walk_pos = gtk_text_iter_get_offset(&iter);
4480 /* FIXME: this looks phony. scanning for anything in the parse table */
4481 for (n = 0; n < PARSE_ELEMS; n++) {
4484 tmp = parser[n].search(walk, parser[n].needle);
4486 if (scanpos == NULL || tmp < scanpos) {
4495 /* check if URI can be parsed */
4496 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4497 (const gchar **)&ep, FALSE)
4498 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4502 strlen(parser[last_index].needle);
4505 uri_start = walk_pos + (bp - o_walk);
4506 uri_stop = walk_pos + (ep - o_walk);
4510 gtk_text_iter_forward_line(&iter);
4513 if (startq_offset != -1) {
4514 GtkTextIter startquote, endquote;
4515 gtk_text_buffer_get_iter_at_offset(
4516 buffer, &startquote, startq_offset);
4519 switch (quotelevel) {
4521 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4522 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4523 gtk_text_buffer_apply_tag_by_name(
4524 buffer, "quote0", &startquote, &endquote);
4525 gtk_text_buffer_remove_tag_by_name(
4526 buffer, "quote1", &startquote, &endquote);
4527 gtk_text_buffer_remove_tag_by_name(
4528 buffer, "quote2", &startquote, &endquote);
4533 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4534 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4535 gtk_text_buffer_apply_tag_by_name(
4536 buffer, "quote1", &startquote, &endquote);
4537 gtk_text_buffer_remove_tag_by_name(
4538 buffer, "quote0", &startquote, &endquote);
4539 gtk_text_buffer_remove_tag_by_name(
4540 buffer, "quote2", &startquote, &endquote);
4545 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4546 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4547 gtk_text_buffer_apply_tag_by_name(
4548 buffer, "quote2", &startquote, &endquote);
4549 gtk_text_buffer_remove_tag_by_name(
4550 buffer, "quote0", &startquote, &endquote);
4551 gtk_text_buffer_remove_tag_by_name(
4552 buffer, "quote1", &startquote, &endquote);
4558 } else if (noq_offset != -1) {
4559 GtkTextIter startnoquote, endnoquote;
4560 gtk_text_buffer_get_iter_at_offset(
4561 buffer, &startnoquote, noq_offset);
4564 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4565 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4566 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4567 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4568 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4569 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4570 gtk_text_buffer_remove_tag_by_name(
4571 buffer, "quote0", &startnoquote, &endnoquote);
4572 gtk_text_buffer_remove_tag_by_name(
4573 buffer, "quote1", &startnoquote, &endnoquote);
4574 gtk_text_buffer_remove_tag_by_name(
4575 buffer, "quote2", &startnoquote, &endnoquote);
4581 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4582 GtkTextIter nouri_start_iter, nouri_end_iter;
4583 gtk_text_buffer_get_iter_at_offset(
4584 buffer, &nouri_start_iter, nouri_start);
4585 gtk_text_buffer_get_iter_at_offset(
4586 buffer, &nouri_end_iter, nouri_stop);
4587 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4588 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4589 gtk_text_buffer_remove_tag_by_name(
4590 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4591 modified_before_remove = modified;
4596 if (uri_start >= 0 && uri_stop > 0) {
4597 GtkTextIter uri_start_iter, uri_end_iter, back;
4598 gtk_text_buffer_get_iter_at_offset(
4599 buffer, &uri_start_iter, uri_start);
4600 gtk_text_buffer_get_iter_at_offset(
4601 buffer, &uri_end_iter, uri_stop);
4602 back = uri_end_iter;
4603 gtk_text_iter_backward_char(&back);
4604 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4605 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4606 gtk_text_buffer_apply_tag_by_name(
4607 buffer, "link", &uri_start_iter, &uri_end_iter);
4609 if (removed && !modified_before_remove) {
4615 // debug_print("not modified, out after %d lines\n", lines);
4619 // debug_print("modified, out after %d lines\n", lines);
4621 g_free(itemized_chars);
4624 undo_wrapping(compose->undostruct, FALSE);
4625 compose->autowrap = prev_autowrap;
4630 void compose_action_cb(void *data)
4632 Compose *compose = (Compose *)data;
4633 compose_wrap_all(compose);
4636 static void compose_wrap_all(Compose *compose)
4638 compose_wrap_all_full(compose, FALSE);
4641 static void compose_wrap_all_full(Compose *compose, gboolean force)
4643 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4644 GtkTextBuffer *buffer;
4646 gboolean modified = TRUE;
4648 buffer = gtk_text_view_get_buffer(text);
4650 gtk_text_buffer_get_start_iter(buffer, &iter);
4651 while (!gtk_text_iter_is_end(&iter) && modified)
4652 modified = compose_beautify_paragraph(compose, &iter, force);
4656 static void compose_set_title(Compose *compose)
4662 edited = compose->modified ? _(" [Edited]") : "";
4664 subject = gtk_editable_get_chars(
4665 GTK_EDITABLE(compose->subject_entry), 0, -1);
4667 #ifndef GENERIC_UMPC
4668 if (subject && strlen(subject))
4669 str = g_strdup_printf(_("%s - Compose message%s"),
4672 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4674 str = g_strdup(_("Compose message"));
4677 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4683 * compose_current_mail_account:
4685 * Find a current mail account (the currently selected account, or the
4686 * default account, if a news account is currently selected). If a
4687 * mail account cannot be found, display an error message.
4689 * Return value: Mail account, or NULL if not found.
4691 static PrefsAccount *
4692 compose_current_mail_account(void)
4696 if (cur_account && cur_account->protocol != A_NNTP)
4699 ac = account_get_default();
4700 if (!ac || ac->protocol == A_NNTP) {
4701 alertpanel_error(_("Account for sending mail is not specified.\n"
4702 "Please select a mail account before sending."));
4709 #define QUOTE_IF_REQUIRED(out, str) \
4711 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4715 len = strlen(str) + 3; \
4716 if ((__tmp = alloca(len)) == NULL) { \
4717 g_warning("can't allocate memory\n"); \
4718 g_string_free(header, TRUE); \
4721 g_snprintf(__tmp, len, "\"%s\"", str); \
4726 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4727 g_warning("can't allocate memory\n"); \
4728 g_string_free(header, TRUE); \
4731 strcpy(__tmp, str); \
4737 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4739 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4743 len = strlen(str) + 3; \
4744 if ((__tmp = alloca(len)) == NULL) { \
4745 g_warning("can't allocate memory\n"); \
4748 g_snprintf(__tmp, len, "\"%s\"", str); \
4753 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4754 g_warning("can't allocate memory\n"); \
4757 strcpy(__tmp, str); \
4763 static void compose_select_account(Compose *compose, PrefsAccount *account,
4766 gchar *from = NULL, *header;
4767 ComposeHeaderEntry *header_entry;
4769 cm_return_if_fail(account != NULL);
4771 compose->account = account;
4772 if (account->name && *account->name) {
4774 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4775 from = g_strdup_printf("%s <%s>",
4776 buf, account->address);
4777 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4779 from = g_strdup_printf("<%s>",
4781 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4786 compose_set_title(compose);
4788 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4789 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4791 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4792 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4793 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4795 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4797 activate_privacy_system(compose, account, FALSE);
4799 if (!init && compose->mode != COMPOSE_REDIRECT) {
4800 undo_block(compose->undostruct);
4801 compose_insert_sig(compose, TRUE);
4802 undo_unblock(compose->undostruct);
4805 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4806 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4808 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4809 if (account->protocol == A_NNTP) {
4810 if (!strcmp(header, _("To:")))
4811 combobox_select_by_text(
4812 GTK_COMBO_BOX(header_entry->combo),
4815 if (!strcmp(header, _("Newsgroups:")))
4816 combobox_select_by_text(
4817 GTK_COMBO_BOX(header_entry->combo),
4825 /* use account's dict info if set */
4826 if (compose->gtkaspell) {
4827 if (account->enable_default_dictionary)
4828 gtkaspell_change_dict(compose->gtkaspell,
4829 account->default_dictionary, FALSE);
4830 if (account->enable_default_alt_dictionary)
4831 gtkaspell_change_alt_dict(compose->gtkaspell,
4832 account->default_alt_dictionary);
4833 if (account->enable_default_dictionary
4834 || account->enable_default_alt_dictionary)
4835 compose_spell_menu_changed(compose);
4840 gboolean compose_check_for_valid_recipient(Compose *compose) {
4841 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4842 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4843 gboolean recipient_found = FALSE;
4847 /* free to and newsgroup list */
4848 slist_free_strings(compose->to_list);
4849 g_slist_free(compose->to_list);
4850 compose->to_list = NULL;
4852 slist_free_strings(compose->newsgroup_list);
4853 g_slist_free(compose->newsgroup_list);
4854 compose->newsgroup_list = NULL;
4856 /* search header entries for to and newsgroup entries */
4857 for (list = compose->header_list; list; list = list->next) {
4860 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4861 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4864 if (entry[0] != '\0') {
4865 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4866 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4867 compose->to_list = address_list_append(compose->to_list, entry);
4868 recipient_found = TRUE;
4871 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4872 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4873 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4874 recipient_found = TRUE;
4881 return recipient_found;
4884 static gboolean compose_check_for_set_recipients(Compose *compose)
4886 if (compose->account->set_autocc && compose->account->auto_cc) {
4887 gboolean found_other = FALSE;
4889 /* search header entries for to and newsgroup entries */
4890 for (list = compose->header_list; list; list = list->next) {
4893 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4894 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4897 if (strcmp(entry, compose->account->auto_cc)
4898 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4908 if (compose->batch) {
4909 gtk_widget_show_all(compose->window);
4911 aval = alertpanel(_("Send"),
4912 _("The only recipient is the default CC address. Send anyway?"),
4913 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4914 if (aval != G_ALERTALTERNATE)
4918 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4919 gboolean found_other = FALSE;
4921 /* search header entries for to and newsgroup entries */
4922 for (list = compose->header_list; list; list = list->next) {
4925 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4926 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4929 if (strcmp(entry, compose->account->auto_bcc)
4930 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4940 if (compose->batch) {
4941 gtk_widget_show_all(compose->window);
4943 aval = alertpanel(_("Send"),
4944 _("The only recipient is the default BCC address. Send anyway?"),
4945 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4946 if (aval != G_ALERTALTERNATE)
4953 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4957 if (compose_check_for_valid_recipient(compose) == FALSE) {
4958 if (compose->batch) {
4959 gtk_widget_show_all(compose->window);
4961 alertpanel_error(_("Recipient is not specified."));
4965 if (compose_check_for_set_recipients(compose) == FALSE) {
4969 if (!compose->batch) {
4970 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4971 if (*str == '\0' && check_everything == TRUE &&
4972 compose->mode != COMPOSE_REDIRECT) {
4974 gchar *button_label;
4977 if (compose->sending)
4978 button_label = _("+_Send");
4980 button_label = _("+_Queue");
4981 message = g_strdup_printf(_("Subject is empty. %s"),
4982 compose->sending?_("Send it anyway?"):
4983 _("Queue it anyway?"));
4985 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4986 GTK_STOCK_CANCEL, button_label, NULL);
4988 if (aval != G_ALERTALTERNATE)
4993 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4999 gint compose_send(Compose *compose)
5002 FolderItem *folder = NULL;
5004 gchar *msgpath = NULL;
5005 gboolean discard_window = FALSE;
5006 gchar *errstr = NULL;
5007 gchar *tmsgid = NULL;
5008 MainWindow *mainwin = mainwindow_get_mainwindow();
5009 gboolean queued_removed = FALSE;
5011 if (prefs_common.send_dialog_invisible
5012 || compose->batch == TRUE)
5013 discard_window = TRUE;
5015 compose_allow_user_actions (compose, FALSE);
5016 compose->sending = TRUE;
5018 if (compose_check_entries(compose, TRUE) == FALSE) {
5019 if (compose->batch) {
5020 gtk_widget_show_all(compose->window);
5026 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
5029 if (compose->batch) {
5030 gtk_widget_show_all(compose->window);
5033 alertpanel_error(_("Could not queue message for sending:\n\n"
5034 "Charset conversion failed."));
5035 } else if (val == -5) {
5036 alertpanel_error(_("Could not queue message for sending:\n\n"
5037 "Couldn't get recipient encryption key."));
5038 } else if (val == -6) {
5040 } else if (val == -3) {
5041 if (privacy_peek_error())
5042 alertpanel_error(_("Could not queue message for sending:\n\n"
5043 "Signature failed: %s"), privacy_get_error());
5044 } else if (val == -2 && errno != 0) {
5045 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
5047 alertpanel_error(_("Could not queue message for sending."));
5052 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
5053 if (discard_window) {
5054 compose->sending = FALSE;
5055 compose_close(compose);
5056 /* No more compose access in the normal codepath
5057 * after this point! */
5062 alertpanel_error(_("The message was queued but could not be "
5063 "sent.\nUse \"Send queued messages\" from "
5064 "the main window to retry."));
5065 if (!discard_window) {
5072 if (msgpath == NULL) {
5073 msgpath = folder_item_fetch_msg(folder, msgnum);
5074 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5077 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5078 claws_unlink(msgpath);
5081 if (!discard_window) {
5083 if (!queued_removed)
5084 folder_item_remove_msg(folder, msgnum);
5085 folder_item_scan(folder);
5087 /* make sure we delete that */
5088 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5090 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5091 folder_item_remove_msg(folder, tmp->msgnum);
5092 procmsg_msginfo_free(tmp);
5099 if (!queued_removed)
5100 folder_item_remove_msg(folder, msgnum);
5101 folder_item_scan(folder);
5103 /* make sure we delete that */
5104 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5106 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5107 folder_item_remove_msg(folder, tmp->msgnum);
5108 procmsg_msginfo_free(tmp);
5111 if (!discard_window) {
5112 compose->sending = FALSE;
5113 compose_allow_user_actions (compose, TRUE);
5114 compose_close(compose);
5118 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5119 "the main window to retry."), errstr);
5122 alertpanel_error_log(_("The message was queued but could not be "
5123 "sent.\nUse \"Send queued messages\" from "
5124 "the main window to retry."));
5126 if (!discard_window) {
5135 toolbar_main_set_sensitive(mainwin);
5136 main_window_set_menu_sensitive(mainwin);
5142 compose_allow_user_actions (compose, TRUE);
5143 compose->sending = FALSE;
5144 compose->modified = TRUE;
5145 toolbar_main_set_sensitive(mainwin);
5146 main_window_set_menu_sensitive(mainwin);
5151 static gboolean compose_use_attach(Compose *compose)
5153 GtkTreeModel *model = gtk_tree_view_get_model
5154 (GTK_TREE_VIEW(compose->attach_clist));
5155 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5158 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5161 gchar buf[BUFFSIZE];
5163 gboolean first_to_address;
5164 gboolean first_cc_address;
5166 ComposeHeaderEntry *headerentry;
5167 const gchar *headerentryname;
5168 const gchar *cc_hdr;
5169 const gchar *to_hdr;
5170 gboolean err = FALSE;
5172 debug_print("Writing redirect header\n");
5174 cc_hdr = prefs_common_translated_header_name("Cc:");
5175 to_hdr = prefs_common_translated_header_name("To:");
5177 first_to_address = TRUE;
5178 for (list = compose->header_list; list; list = list->next) {
5179 headerentry = ((ComposeHeaderEntry *)list->data);
5180 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5182 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5183 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5184 Xstrdup_a(str, entstr, return -1);
5186 if (str[0] != '\0') {
5187 compose_convert_header
5188 (compose, buf, sizeof(buf), str,
5189 strlen("Resent-To") + 2, TRUE);
5191 if (first_to_address) {
5192 err |= (fprintf(fp, "Resent-To: ") < 0);
5193 first_to_address = FALSE;
5195 err |= (fprintf(fp, ",") < 0);
5197 err |= (fprintf(fp, "%s", buf) < 0);
5201 if (!first_to_address) {
5202 err |= (fprintf(fp, "\n") < 0);
5205 first_cc_address = TRUE;
5206 for (list = compose->header_list; list; list = list->next) {
5207 headerentry = ((ComposeHeaderEntry *)list->data);
5208 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5210 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5211 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5212 Xstrdup_a(str, strg, return -1);
5214 if (str[0] != '\0') {
5215 compose_convert_header
5216 (compose, buf, sizeof(buf), str,
5217 strlen("Resent-Cc") + 2, TRUE);
5219 if (first_cc_address) {
5220 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5221 first_cc_address = FALSE;
5223 err |= (fprintf(fp, ",") < 0);
5225 err |= (fprintf(fp, "%s", buf) < 0);
5229 if (!first_cc_address) {
5230 err |= (fprintf(fp, "\n") < 0);
5233 return (err ? -1:0);
5236 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5238 gchar buf[BUFFSIZE];
5240 const gchar *entstr;
5241 /* struct utsname utsbuf; */
5242 gboolean err = FALSE;
5244 cm_return_val_if_fail(fp != NULL, -1);
5245 cm_return_val_if_fail(compose->account != NULL, -1);
5246 cm_return_val_if_fail(compose->account->address != NULL, -1);
5249 get_rfc822_date(buf, sizeof(buf));
5250 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5253 if (compose->account->name && *compose->account->name) {
5254 compose_convert_header
5255 (compose, buf, sizeof(buf), compose->account->name,
5256 strlen("From: "), TRUE);
5257 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5258 buf, compose->account->address) < 0);
5260 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5263 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5264 if (*entstr != '\0') {
5265 Xstrdup_a(str, entstr, return -1);
5268 compose_convert_header(compose, buf, sizeof(buf), str,
5269 strlen("Subject: "), FALSE);
5270 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5274 /* Resent-Message-ID */
5275 if (compose->account->set_domain && compose->account->domain) {
5276 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5277 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5278 g_snprintf(buf, sizeof(buf), "%s",
5279 strchr(compose->account->address, '@') ?
5280 strchr(compose->account->address, '@')+1 :
5281 compose->account->address);
5283 g_snprintf(buf, sizeof(buf), "%s", "");
5286 if (compose->account->gen_msgid) {
5288 if (compose->account->msgid_with_addr) {
5289 addr = compose->account->address;
5291 generate_msgid(buf, sizeof(buf), addr);
5292 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5293 compose->msgid = g_strdup(buf);
5295 compose->msgid = NULL;
5298 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5301 /* separator between header and body */
5302 err |= (fputs("\n", fp) == EOF);
5304 return (err ? -1:0);
5307 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5311 gchar buf[BUFFSIZE];
5313 gboolean skip = FALSE;
5314 gboolean err = FALSE;
5315 gchar *not_included[]={
5316 "Return-Path:", "Delivered-To:", "Received:",
5317 "Subject:", "X-UIDL:", "AF:",
5318 "NF:", "PS:", "SRH:",
5319 "SFN:", "DSR:", "MID:",
5320 "CFG:", "PT:", "S:",
5321 "RQ:", "SSV:", "NSV:",
5322 "SSH:", "R:", "MAID:",
5323 "NAID:", "RMID:", "FMID:",
5324 "SCF:", "RRCPT:", "NG:",
5325 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5326 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5327 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5328 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5329 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5332 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5333 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5337 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5339 for (i = 0; not_included[i] != NULL; i++) {
5340 if (g_ascii_strncasecmp(buf, not_included[i],
5341 strlen(not_included[i])) == 0) {
5348 if (fputs(buf, fdest) == -1)
5351 if (!prefs_common.redirect_keep_from) {
5352 if (g_ascii_strncasecmp(buf, "From:",
5353 strlen("From:")) == 0) {
5354 err |= (fputs(" (by way of ", fdest) == EOF);
5355 if (compose->account->name
5356 && *compose->account->name) {
5357 compose_convert_header
5358 (compose, buf, sizeof(buf),
5359 compose->account->name,
5362 err |= (fprintf(fdest, "%s <%s>",
5364 compose->account->address) < 0);
5366 err |= (fprintf(fdest, "%s",
5367 compose->account->address) < 0);
5368 err |= (fputs(")", fdest) == EOF);
5372 if (fputs("\n", fdest) == -1)
5379 if (compose_redirect_write_headers(compose, fdest))
5382 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5383 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5396 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5398 GtkTextBuffer *buffer;
5399 GtkTextIter start, end;
5402 const gchar *out_codeset;
5403 EncodingType encoding = ENC_UNKNOWN;
5404 MimeInfo *mimemsg, *mimetext;
5406 const gchar *src_codeset = CS_INTERNAL;
5407 gchar *from_addr = NULL;
5408 gchar *from_name = NULL;
5410 if (action == COMPOSE_WRITE_FOR_SEND)
5411 attach_parts = TRUE;
5413 /* create message MimeInfo */
5414 mimemsg = procmime_mimeinfo_new();
5415 mimemsg->type = MIMETYPE_MESSAGE;
5416 mimemsg->subtype = g_strdup("rfc822");
5417 mimemsg->content = MIMECONTENT_MEM;
5418 mimemsg->tmp = TRUE; /* must free content later */
5419 mimemsg->data.mem = compose_get_header(compose);
5421 /* Create text part MimeInfo */
5422 /* get all composed text */
5423 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5424 gtk_text_buffer_get_start_iter(buffer, &start);
5425 gtk_text_buffer_get_end_iter(buffer, &end);
5426 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5428 out_codeset = conv_get_charset_str(compose->out_encoding);
5430 if (!out_codeset && is_ascii_str(chars)) {
5431 out_codeset = CS_US_ASCII;
5432 } else if (prefs_common.outgoing_fallback_to_ascii &&
5433 is_ascii_str(chars)) {
5434 out_codeset = CS_US_ASCII;
5435 encoding = ENC_7BIT;
5439 gchar *test_conv_global_out = NULL;
5440 gchar *test_conv_reply = NULL;
5442 /* automatic mode. be automatic. */
5443 codeconv_set_strict(TRUE);
5445 out_codeset = conv_get_outgoing_charset_str();
5447 debug_print("trying to convert to %s\n", out_codeset);
5448 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5451 if (!test_conv_global_out && compose->orig_charset
5452 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5453 out_codeset = compose->orig_charset;
5454 debug_print("failure; trying to convert to %s\n", out_codeset);
5455 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5458 if (!test_conv_global_out && !test_conv_reply) {
5460 out_codeset = CS_INTERNAL;
5461 debug_print("failure; finally using %s\n", out_codeset);
5463 g_free(test_conv_global_out);
5464 g_free(test_conv_reply);
5465 codeconv_set_strict(FALSE);
5468 if (encoding == ENC_UNKNOWN) {
5469 if (prefs_common.encoding_method == CTE_BASE64)
5470 encoding = ENC_BASE64;
5471 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5472 encoding = ENC_QUOTED_PRINTABLE;
5473 else if (prefs_common.encoding_method == CTE_8BIT)
5474 encoding = ENC_8BIT;
5476 encoding = procmime_get_encoding_for_charset(out_codeset);
5479 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5480 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5482 if (action == COMPOSE_WRITE_FOR_SEND) {
5483 codeconv_set_strict(TRUE);
5484 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5485 codeconv_set_strict(FALSE);
5491 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5492 "to the specified %s charset.\n"
5493 "Send it as %s?"), out_codeset, src_codeset);
5494 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5495 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5498 if (aval != G_ALERTALTERNATE) {
5503 out_codeset = src_codeset;
5509 out_codeset = src_codeset;
5514 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5515 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5516 strstr(buf, "\nFrom ") != NULL) {
5517 encoding = ENC_QUOTED_PRINTABLE;
5521 mimetext = procmime_mimeinfo_new();
5522 mimetext->content = MIMECONTENT_MEM;
5523 mimetext->tmp = TRUE; /* must free content later */
5524 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5525 * and free the data, which we need later. */
5526 mimetext->data.mem = g_strdup(buf);
5527 mimetext->type = MIMETYPE_TEXT;
5528 mimetext->subtype = g_strdup("plain");
5529 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5530 g_strdup(out_codeset));
5532 /* protect trailing spaces when signing message */
5533 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5534 privacy_system_can_sign(compose->privacy_system)) {
5535 encoding = ENC_QUOTED_PRINTABLE;
5538 debug_print("main text: %zd bytes encoded as %s in %d\n",
5539 strlen(buf), out_codeset, encoding);
5541 /* check for line length limit */
5542 if (action == COMPOSE_WRITE_FOR_SEND &&
5543 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5544 check_line_length(buf, 1000, &line) < 0) {
5548 msg = g_strdup_printf
5549 (_("Line %d exceeds the line length limit (998 bytes).\n"
5550 "The contents of the message might be broken on the way to the delivery.\n"
5552 "Send it anyway?"), line + 1);
5553 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5555 if (aval != G_ALERTALTERNATE) {
5561 if (encoding != ENC_UNKNOWN)
5562 procmime_encode_content(mimetext, encoding);
5564 /* append attachment parts */
5565 if (compose_use_attach(compose) && attach_parts) {
5566 MimeInfo *mimempart;
5567 gchar *boundary = NULL;
5568 mimempart = procmime_mimeinfo_new();
5569 mimempart->content = MIMECONTENT_EMPTY;
5570 mimempart->type = MIMETYPE_MULTIPART;
5571 mimempart->subtype = g_strdup("mixed");
5575 boundary = generate_mime_boundary(NULL);
5576 } while (strstr(buf, boundary) != NULL);
5578 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5581 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5583 g_node_append(mimempart->node, mimetext->node);
5584 g_node_append(mimemsg->node, mimempart->node);
5586 if (compose_add_attachments(compose, mimempart) < 0)
5589 g_node_append(mimemsg->node, mimetext->node);
5593 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5594 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5595 /* extract name and address */
5596 if (strstr(spec, " <") && strstr(spec, ">")) {
5597 from_addr = g_strdup(strrchr(spec, '<')+1);
5598 *(strrchr(from_addr, '>')) = '\0';
5599 from_name = g_strdup(spec);
5600 *(strrchr(from_name, '<')) = '\0';
5607 /* sign message if sending */
5608 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5609 privacy_system_can_sign(compose->privacy_system))
5610 if (!privacy_sign(compose->privacy_system, mimemsg,
5611 compose->account, from_addr)) {
5618 procmime_write_mimeinfo(mimemsg, fp);
5620 procmime_mimeinfo_free_all(mimemsg);
5625 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5627 GtkTextBuffer *buffer;
5628 GtkTextIter start, end;
5633 if ((fp = g_fopen(file, "wb")) == NULL) {
5634 FILE_OP_ERROR(file, "fopen");
5638 /* chmod for security */
5639 if (change_file_mode_rw(fp, file) < 0) {
5640 FILE_OP_ERROR(file, "chmod");
5641 g_warning("can't change file mode\n");
5644 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5645 gtk_text_buffer_get_start_iter(buffer, &start);
5646 gtk_text_buffer_get_end_iter(buffer, &end);
5647 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5649 chars = conv_codeset_strdup
5650 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5653 if (!chars) return -1;
5656 len = strlen(chars);
5657 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5658 FILE_OP_ERROR(file, "fwrite");
5667 if (fclose(fp) == EOF) {
5668 FILE_OP_ERROR(file, "fclose");
5675 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5678 MsgInfo *msginfo = compose->targetinfo;
5680 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5681 if (!msginfo) return -1;
5683 if (!force && MSG_IS_LOCKED(msginfo->flags))
5686 item = msginfo->folder;
5687 cm_return_val_if_fail(item != NULL, -1);
5689 if (procmsg_msg_exist(msginfo) &&
5690 (folder_has_parent_of_type(item, F_QUEUE) ||
5691 folder_has_parent_of_type(item, F_DRAFT)
5692 || msginfo == compose->autosaved_draft)) {
5693 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5694 g_warning("can't remove the old message\n");
5697 debug_print("removed reedit target %d\n", msginfo->msgnum);
5704 static void compose_remove_draft(Compose *compose)
5707 MsgInfo *msginfo = compose->targetinfo;
5708 drafts = account_get_special_folder(compose->account, F_DRAFT);
5710 if (procmsg_msg_exist(msginfo)) {
5711 folder_item_remove_msg(drafts, msginfo->msgnum);
5716 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5717 gboolean remove_reedit_target)
5719 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5722 static gboolean compose_warn_encryption(Compose *compose)
5724 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5725 AlertValue val = G_ALERTALTERNATE;
5727 if (warning == NULL)
5730 val = alertpanel_full(_("Encryption warning"), warning,
5731 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5732 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5733 if (val & G_ALERTDISABLE) {
5734 val &= ~G_ALERTDISABLE;
5735 if (val == G_ALERTALTERNATE)
5736 privacy_inhibit_encrypt_warning(compose->privacy_system,
5740 if (val == G_ALERTALTERNATE) {
5747 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5748 gchar **msgpath, gboolean check_subject,
5749 gboolean remove_reedit_target)
5756 static gboolean lock = FALSE;
5757 PrefsAccount *mailac = NULL, *newsac = NULL;
5758 gboolean err = FALSE;
5760 debug_print("queueing message...\n");
5761 cm_return_val_if_fail(compose->account != NULL, -1);
5765 if (compose_check_entries(compose, check_subject) == FALSE) {
5767 if (compose->batch) {
5768 gtk_widget_show_all(compose->window);
5773 if (!compose->to_list && !compose->newsgroup_list) {
5774 g_warning("can't get recipient list.");
5779 if (compose->to_list) {
5780 if (compose->account->protocol != A_NNTP)
5781 mailac = compose->account;
5782 else if (cur_account && cur_account->protocol != A_NNTP)
5783 mailac = cur_account;
5784 else if (!(mailac = compose_current_mail_account())) {
5786 alertpanel_error(_("No account for sending mails available!"));
5791 if (compose->newsgroup_list) {
5792 if (compose->account->protocol == A_NNTP)
5793 newsac = compose->account;
5796 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5801 /* write queue header */
5802 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5803 G_DIR_SEPARATOR, compose, (guint) rand());
5804 debug_print("queuing to %s\n", tmp);
5805 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5806 FILE_OP_ERROR(tmp, "fopen");
5812 if (change_file_mode_rw(fp, tmp) < 0) {
5813 FILE_OP_ERROR(tmp, "chmod");
5814 g_warning("can't change file mode\n");
5817 /* queueing variables */
5818 err |= (fprintf(fp, "AF:\n") < 0);
5819 err |= (fprintf(fp, "NF:0\n") < 0);
5820 err |= (fprintf(fp, "PS:10\n") < 0);
5821 err |= (fprintf(fp, "SRH:1\n") < 0);
5822 err |= (fprintf(fp, "SFN:\n") < 0);
5823 err |= (fprintf(fp, "DSR:\n") < 0);
5825 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5827 err |= (fprintf(fp, "MID:\n") < 0);
5828 err |= (fprintf(fp, "CFG:\n") < 0);
5829 err |= (fprintf(fp, "PT:0\n") < 0);
5830 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5831 err |= (fprintf(fp, "RQ:\n") < 0);
5833 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5835 err |= (fprintf(fp, "SSV:\n") < 0);
5837 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5839 err |= (fprintf(fp, "NSV:\n") < 0);
5840 err |= (fprintf(fp, "SSH:\n") < 0);
5841 /* write recepient list */
5842 if (compose->to_list) {
5843 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5844 for (cur = compose->to_list->next; cur != NULL;
5846 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5847 err |= (fprintf(fp, "\n") < 0);
5849 /* write newsgroup list */
5850 if (compose->newsgroup_list) {
5851 err |= (fprintf(fp, "NG:") < 0);
5852 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5853 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5854 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5855 err |= (fprintf(fp, "\n") < 0);
5857 /* Sylpheed account IDs */
5859 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5861 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5864 if (compose->privacy_system != NULL) {
5865 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5866 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5867 if (compose->use_encryption) {
5869 if (!compose_warn_encryption(compose)) {
5876 if (mailac && mailac->encrypt_to_self) {
5877 GSList *tmp_list = g_slist_copy(compose->to_list);
5878 tmp_list = g_slist_append(tmp_list, compose->account->address);
5879 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5880 g_slist_free(tmp_list);
5882 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5884 if (encdata != NULL) {
5885 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5886 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5887 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5889 } /* else we finally dont want to encrypt */
5891 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5892 /* and if encdata was null, it means there's been a problem in
5904 /* Save copy folder */
5905 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5906 gchar *savefolderid;
5908 savefolderid = compose_get_save_to(compose);
5909 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5910 g_free(savefolderid);
5912 /* Save copy folder */
5913 if (compose->return_receipt) {
5914 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5916 /* Message-ID of message replying to */
5917 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5920 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5921 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5924 /* Message-ID of message forwarding to */
5925 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5928 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5929 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5933 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5934 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5936 /* end of headers */
5937 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5939 if (compose->redirect_filename != NULL) {
5940 if (compose_redirect_write_to_file(compose, fp) < 0) {
5949 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5954 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5958 g_warning("failed to write queue message\n");
5965 if (fclose(fp) == EOF) {
5966 FILE_OP_ERROR(tmp, "fclose");
5973 if (item && *item) {
5976 queue = account_get_special_folder(compose->account, F_QUEUE);
5979 g_warning("can't find queue folder\n");
5985 folder_item_scan(queue);
5986 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5987 g_warning("can't queue the message\n");
5994 if (msgpath == NULL) {
6000 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
6001 compose_remove_reedit_target(compose, FALSE);
6004 if ((msgnum != NULL) && (item != NULL)) {
6012 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
6015 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
6017 struct stat statbuf;
6018 gchar *type, *subtype;
6019 GtkTreeModel *model;
6022 model = gtk_tree_view_get_model(tree_view);
6024 if (!gtk_tree_model_get_iter_first(model, &iter))
6027 gtk_tree_model_get(model, &iter,
6031 if (!is_file_exist(ainfo->file)) {
6032 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
6033 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
6034 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
6036 if (val == G_ALERTDEFAULT) {
6041 mimepart = procmime_mimeinfo_new();
6042 mimepart->content = MIMECONTENT_FILE;
6043 mimepart->data.filename = g_strdup(ainfo->file);
6044 mimepart->tmp = FALSE; /* or we destroy our attachment */
6045 mimepart->offset = 0;
6047 g_stat(ainfo->file, &statbuf);
6048 mimepart->length = statbuf.st_size;
6050 type = g_strdup(ainfo->content_type);
6052 if (!strchr(type, '/')) {
6054 type = g_strdup("application/octet-stream");
6057 subtype = strchr(type, '/') + 1;
6058 *(subtype - 1) = '\0';
6059 mimepart->type = procmime_get_media_type(type);
6060 mimepart->subtype = g_strdup(subtype);
6063 if (mimepart->type == MIMETYPE_MESSAGE &&
6064 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
6065 mimepart->disposition = DISPOSITIONTYPE_INLINE;
6066 } else if (mimepart->type == MIMETYPE_TEXT) {
6067 if (!ainfo->name && compose->mode == COMPOSE_FORWARD_INLINE) {
6068 /* Text parts with no name come from multipart/alternative
6069 * forwards. Make sure the recipient won't look at the
6070 * original HTML part by mistake. */
6071 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6072 ainfo->name = g_strdup_printf(_("Original %s part"),
6076 g_hash_table_insert(mimepart->typeparameters,
6077 g_strdup("charset"), g_strdup(ainfo->charset));
6079 if (ainfo->name && mimepart->type != MIMETYPE_MESSAGE) {
6080 if (mimepart->type == MIMETYPE_APPLICATION &&
6081 !strcmp2(mimepart->subtype, "octet-stream"))
6082 g_hash_table_insert(mimepart->typeparameters,
6083 g_strdup("name"), g_strdup(ainfo->name));
6084 g_hash_table_insert(mimepart->dispositionparameters,
6085 g_strdup("filename"), g_strdup(ainfo->name));
6086 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6089 if (mimepart->type == MIMETYPE_MESSAGE
6090 || mimepart->type == MIMETYPE_MULTIPART)
6091 ainfo->encoding = ENC_BINARY;
6092 else if (compose->use_signing) {
6093 if (ainfo->encoding == ENC_7BIT)
6094 ainfo->encoding = ENC_QUOTED_PRINTABLE;
6095 else if (ainfo->encoding == ENC_8BIT)
6096 ainfo->encoding = ENC_BASE64;
6101 procmime_encode_content(mimepart, ainfo->encoding);
6103 g_node_append(parent->node, mimepart->node);
6104 } while (gtk_tree_model_iter_next(model, &iter));
6109 #define IS_IN_CUSTOM_HEADER(header) \
6110 (compose->account->add_customhdr && \
6111 custom_header_find(compose->account->customhdr_list, header) != NULL)
6113 static void compose_add_headerfield_from_headerlist(Compose *compose,
6115 const gchar *fieldname,
6116 const gchar *seperator)
6118 gchar *str, *fieldname_w_colon;
6119 gboolean add_field = FALSE;
6121 ComposeHeaderEntry *headerentry;
6122 const gchar *headerentryname;
6123 const gchar *trans_fieldname;
6126 if (IS_IN_CUSTOM_HEADER(fieldname))
6129 debug_print("Adding %s-fields\n", fieldname);
6131 fieldstr = g_string_sized_new(64);
6133 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6134 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6136 for (list = compose->header_list; list; list = list->next) {
6137 headerentry = ((ComposeHeaderEntry *)list->data);
6138 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6140 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6141 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6143 if (str[0] != '\0') {
6145 g_string_append(fieldstr, seperator);
6146 g_string_append(fieldstr, str);
6155 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6156 compose_convert_header
6157 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6158 strlen(fieldname) + 2, TRUE);
6159 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6163 g_free(fieldname_w_colon);
6164 g_string_free(fieldstr, TRUE);
6169 static gchar *compose_get_manual_headers_info(Compose *compose)
6171 GString *sh_header = g_string_new(" ");
6173 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6175 for (list = compose->header_list; list; list = list->next) {
6176 ComposeHeaderEntry *headerentry;
6179 gchar *headername_wcolon;
6180 const gchar *headername_trans;
6182 gboolean standard_header = FALSE;
6184 headerentry = ((ComposeHeaderEntry *)list->data);
6186 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6188 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6193 if (!strstr(tmp, ":")) {
6194 headername_wcolon = g_strconcat(tmp, ":", NULL);
6195 headername = g_strdup(tmp);
6197 headername_wcolon = g_strdup(tmp);
6198 headername = g_strdup(strtok(tmp, ":"));
6202 string = std_headers;
6203 while (*string != NULL) {
6204 headername_trans = prefs_common_translated_header_name(*string);
6205 if (!strcmp(headername_trans, headername_wcolon))
6206 standard_header = TRUE;
6209 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6210 g_string_append_printf(sh_header, "%s ", headername);
6212 g_free(headername_wcolon);
6214 g_string_truncate(sh_header, strlen(sh_header->str) - 1); /* remove last space */
6215 return g_string_free(sh_header, FALSE);
6218 static gchar *compose_get_header(Compose *compose)
6220 gchar buf[BUFFSIZE];
6221 const gchar *entry_str;
6225 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6227 gchar *from_name = NULL, *from_address = NULL;
6230 cm_return_val_if_fail(compose->account != NULL, NULL);
6231 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6233 header = g_string_sized_new(64);
6236 get_rfc822_date(buf, sizeof(buf));
6237 g_string_append_printf(header, "Date: %s\n", buf);
6241 if (compose->account->name && *compose->account->name) {
6243 QUOTE_IF_REQUIRED(buf, compose->account->name);
6244 tmp = g_strdup_printf("%s <%s>",
6245 buf, compose->account->address);
6247 tmp = g_strdup_printf("%s",
6248 compose->account->address);
6250 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6251 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6253 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6254 from_address = g_strdup(compose->account->address);
6256 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6257 /* extract name and address */
6258 if (strstr(spec, " <") && strstr(spec, ">")) {
6259 from_address = g_strdup(strrchr(spec, '<')+1);
6260 *(strrchr(from_address, '>')) = '\0';
6261 from_name = g_strdup(spec);
6262 *(strrchr(from_name, '<')) = '\0';
6265 from_address = g_strdup(spec);
6272 if (from_name && *from_name) {
6273 compose_convert_header
6274 (compose, buf, sizeof(buf), from_name,
6275 strlen("From: "), TRUE);
6276 QUOTE_IF_REQUIRED(name, buf);
6278 g_string_append_printf(header, "From: %s <%s>\n",
6279 name, from_address);
6281 g_string_append_printf(header, "From: %s\n", from_address);
6284 g_free(from_address);
6287 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6290 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6293 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6297 * If this account is a NNTP account remove Bcc header from
6298 * message body since it otherwise will be publicly shown
6300 if (compose->account->protocol != A_NNTP)
6301 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6304 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6306 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6309 compose_convert_header(compose, buf, sizeof(buf), str,
6310 strlen("Subject: "), FALSE);
6311 g_string_append_printf(header, "Subject: %s\n", buf);
6317 if (compose->account->set_domain && compose->account->domain) {
6318 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6319 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6320 g_snprintf(buf, sizeof(buf), "%s",
6321 strchr(compose->account->address, '@') ?
6322 strchr(compose->account->address, '@')+1 :
6323 compose->account->address);
6325 g_snprintf(buf, sizeof(buf), "%s", "");
6328 if (compose->account->gen_msgid) {
6330 if (compose->account->msgid_with_addr) {
6331 addr = compose->account->address;
6333 generate_msgid(buf, sizeof(buf), addr);
6334 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6335 compose->msgid = g_strdup(buf);
6337 compose->msgid = NULL;
6340 if (compose->remove_references == FALSE) {
6342 if (compose->inreplyto && compose->to_list)
6343 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6346 if (compose->references)
6347 g_string_append_printf(header, "References: %s\n", compose->references);
6351 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6354 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6357 if (compose->account->organization &&
6358 strlen(compose->account->organization) &&
6359 !IS_IN_CUSTOM_HEADER("Organization")) {
6360 compose_convert_header(compose, buf, sizeof(buf),
6361 compose->account->organization,
6362 strlen("Organization: "), FALSE);
6363 g_string_append_printf(header, "Organization: %s\n", buf);
6366 /* Program version and system info */
6367 if (compose->account->gen_xmailer &&
6368 g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6369 !compose->newsgroup_list) {
6370 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6372 gtk_major_version, gtk_minor_version, gtk_micro_version,
6375 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6376 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6378 gtk_major_version, gtk_minor_version, gtk_micro_version,
6382 /* custom headers */
6383 if (compose->account->add_customhdr) {
6386 for (cur = compose->account->customhdr_list; cur != NULL;
6388 CustomHeader *chdr = (CustomHeader *)cur->data;
6390 if (custom_header_is_allowed(chdr->name)
6391 && chdr->value != NULL
6392 && *(chdr->value) != '\0') {
6393 compose_convert_header
6394 (compose, buf, sizeof(buf),
6396 strlen(chdr->name) + 2, FALSE);
6397 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6402 /* Automatic Faces and X-Faces */
6403 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6404 g_string_append_printf(header, "X-Face: %s\n", buf);
6406 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6407 g_string_append_printf(header, "X-Face: %s\n", buf);
6409 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6410 g_string_append_printf(header, "Face: %s\n", buf);
6412 else if (get_default_face (buf, sizeof(buf)) == 0) {
6413 g_string_append_printf(header, "Face: %s\n", buf);
6417 switch (compose->priority) {
6418 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6419 "X-Priority: 1 (Highest)\n");
6421 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6422 "X-Priority: 2 (High)\n");
6424 case PRIORITY_NORMAL: break;
6425 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6426 "X-Priority: 4 (Low)\n");
6428 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6429 "X-Priority: 5 (Lowest)\n");
6431 default: debug_print("compose: priority unknown : %d\n",
6435 /* Request Return Receipt */
6436 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6437 if (compose->return_receipt) {
6438 if (compose->account->name
6439 && *compose->account->name) {
6440 compose_convert_header(compose, buf, sizeof(buf),
6441 compose->account->name,
6442 strlen("Disposition-Notification-To: "),
6444 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6446 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6450 /* get special headers */
6451 for (list = compose->header_list; list; list = list->next) {
6452 ComposeHeaderEntry *headerentry;
6455 gchar *headername_wcolon;
6456 const gchar *headername_trans;
6459 gboolean standard_header = FALSE;
6461 headerentry = ((ComposeHeaderEntry *)list->data);
6463 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6465 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6470 if (!strstr(tmp, ":")) {
6471 headername_wcolon = g_strconcat(tmp, ":", NULL);
6472 headername = g_strdup(tmp);
6474 headername_wcolon = g_strdup(tmp);
6475 headername = g_strdup(strtok(tmp, ":"));
6479 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6480 Xstrdup_a(headervalue, entry_str, return NULL);
6481 subst_char(headervalue, '\r', ' ');
6482 subst_char(headervalue, '\n', ' ');
6483 string = std_headers;
6484 while (*string != NULL) {
6485 headername_trans = prefs_common_translated_header_name(*string);
6486 if (!strcmp(headername_trans, headername_wcolon))
6487 standard_header = TRUE;
6490 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6491 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6494 g_free(headername_wcolon);
6498 g_string_free(header, FALSE);
6503 #undef IS_IN_CUSTOM_HEADER
6505 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6506 gint header_len, gboolean addr_field)
6508 gchar *tmpstr = NULL;
6509 const gchar *out_codeset = NULL;
6511 cm_return_if_fail(src != NULL);
6512 cm_return_if_fail(dest != NULL);
6514 if (len < 1) return;
6516 tmpstr = g_strdup(src);
6518 subst_char(tmpstr, '\n', ' ');
6519 subst_char(tmpstr, '\r', ' ');
6522 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6523 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6524 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6529 codeconv_set_strict(TRUE);
6530 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6531 conv_get_charset_str(compose->out_encoding));
6532 codeconv_set_strict(FALSE);
6534 if (!dest || *dest == '\0') {
6535 gchar *test_conv_global_out = NULL;
6536 gchar *test_conv_reply = NULL;
6538 /* automatic mode. be automatic. */
6539 codeconv_set_strict(TRUE);
6541 out_codeset = conv_get_outgoing_charset_str();
6543 debug_print("trying to convert to %s\n", out_codeset);
6544 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6547 if (!test_conv_global_out && compose->orig_charset
6548 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6549 out_codeset = compose->orig_charset;
6550 debug_print("failure; trying to convert to %s\n", out_codeset);
6551 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6554 if (!test_conv_global_out && !test_conv_reply) {
6556 out_codeset = CS_INTERNAL;
6557 debug_print("finally using %s\n", out_codeset);
6559 g_free(test_conv_global_out);
6560 g_free(test_conv_reply);
6561 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6563 codeconv_set_strict(FALSE);
6568 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6572 cm_return_if_fail(user_data != NULL);
6574 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6575 g_strstrip(address);
6576 if (*address != '\0') {
6577 gchar *name = procheader_get_fromname(address);
6578 extract_address(address);
6579 #ifndef USE_NEW_ADDRBOOK
6580 addressbook_add_contact(name, address, NULL, NULL);
6582 debug_print("%s: %s\n", name, address);
6583 if (addressadd_selection(name, address, NULL, NULL)) {
6584 debug_print( "addressbook_add_contact - added\n" );
6591 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6593 GtkWidget *menuitem;
6596 cm_return_if_fail(menu != NULL);
6597 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6599 menuitem = gtk_separator_menu_item_new();
6600 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6601 gtk_widget_show(menuitem);
6603 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6604 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6606 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6607 g_strstrip(address);
6608 if (*address == '\0') {
6609 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6612 g_signal_connect(G_OBJECT(menuitem), "activate",
6613 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6614 gtk_widget_show(menuitem);
6617 static void compose_create_header_entry(Compose *compose)
6619 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6626 const gchar *header = NULL;
6627 ComposeHeaderEntry *headerentry;
6628 gboolean standard_header = FALSE;
6629 GtkListStore *model;
6631 #if !(GTK_CHECK_VERSION(2,12,0))
6632 GtkTooltips *tips = compose->tooltips;
6635 headerentry = g_new0(ComposeHeaderEntry, 1);
6638 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6639 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6640 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6642 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6644 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6646 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6647 COMPOSE_NEWSGROUPS);
6648 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6650 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6651 COMPOSE_FOLLOWUPTO);
6653 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6654 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6655 G_CALLBACK(compose_grab_focus_cb), compose);
6656 gtk_widget_show(combo);
6657 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6658 compose->header_nextrow, compose->header_nextrow+1,
6659 GTK_SHRINK, GTK_FILL, 0, 0);
6660 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6661 const gchar *last_header_entry = gtk_entry_get_text(
6662 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6664 while (*string != NULL) {
6665 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6666 standard_header = TRUE;
6669 if (standard_header)
6670 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6672 if (!compose->header_last || !standard_header) {
6673 switch(compose->account->protocol) {
6675 header = prefs_common_translated_header_name("Newsgroups:");
6678 header = prefs_common_translated_header_name("To:");
6683 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6685 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6686 G_CALLBACK(compose_grab_focus_cb), compose);
6688 /* Entry field with cleanup button */
6689 button = gtk_button_new();
6690 gtk_button_set_image(GTK_BUTTON(button),
6691 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6692 gtk_widget_show(button);
6693 CLAWS_SET_TIP(button,
6694 _("Delete entry contents"));
6695 entry = gtk_entry_new();
6696 gtk_widget_show(entry);
6697 CLAWS_SET_TIP(entry,
6698 _("Use <tab> to autocomplete from addressbook"));
6699 hbox = gtk_hbox_new (FALSE, 0);
6700 gtk_widget_show(hbox);
6701 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6702 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6703 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6704 compose->header_nextrow, compose->header_nextrow+1,
6705 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6707 g_signal_connect(G_OBJECT(entry), "key-press-event",
6708 G_CALLBACK(compose_headerentry_key_press_event_cb),
6710 g_signal_connect(G_OBJECT(entry), "changed",
6711 G_CALLBACK(compose_headerentry_changed_cb),
6713 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6714 G_CALLBACK(compose_grab_focus_cb), compose);
6716 g_signal_connect(G_OBJECT(button), "clicked",
6717 G_CALLBACK(compose_headerentry_button_clicked_cb),
6721 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6722 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6723 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6724 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6725 G_CALLBACK(compose_header_drag_received_cb),
6727 g_signal_connect(G_OBJECT(entry), "drag-drop",
6728 G_CALLBACK(compose_drag_drop),
6730 g_signal_connect(G_OBJECT(entry), "populate-popup",
6731 G_CALLBACK(compose_entry_popup_extend),
6734 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6736 headerentry->compose = compose;
6737 headerentry->combo = combo;
6738 headerentry->entry = entry;
6739 headerentry->button = button;
6740 headerentry->hbox = hbox;
6741 headerentry->headernum = compose->header_nextrow;
6742 headerentry->type = PREF_NONE;
6744 compose->header_nextrow++;
6745 compose->header_last = headerentry;
6746 compose->header_list =
6747 g_slist_append(compose->header_list,
6751 static void compose_add_header_entry(Compose *compose, const gchar *header,
6752 gchar *text, ComposePrefType pref_type)
6754 ComposeHeaderEntry *last_header = compose->header_last;
6755 gchar *tmp = g_strdup(text), *email;
6756 gboolean replyto_hdr;
6758 replyto_hdr = (!strcasecmp(header,
6759 prefs_common_translated_header_name("Reply-To:")) ||
6761 prefs_common_translated_header_name("Followup-To:")) ||
6763 prefs_common_translated_header_name("In-Reply-To:")));
6765 extract_address(tmp);
6766 email = g_utf8_strdown(tmp, -1);
6768 if (replyto_hdr == FALSE &&
6769 g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6771 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6772 header, text, (gint) pref_type);
6778 if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6779 gtk_entry_set_text(GTK_ENTRY(
6780 gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6782 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6783 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6784 last_header->type = pref_type;
6786 if (replyto_hdr == FALSE)
6787 g_hash_table_insert(compose->email_hashtable, email,
6788 GUINT_TO_POINTER(1));
6795 static void compose_destroy_headerentry(Compose *compose,
6796 ComposeHeaderEntry *headerentry)
6798 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6801 extract_address(text);
6802 email = g_utf8_strdown(text, -1);
6803 g_hash_table_remove(compose->email_hashtable, email);
6807 gtk_widget_destroy(headerentry->combo);
6808 gtk_widget_destroy(headerentry->entry);
6809 gtk_widget_destroy(headerentry->button);
6810 gtk_widget_destroy(headerentry->hbox);
6811 g_free(headerentry);
6814 static void compose_remove_header_entries(Compose *compose)
6817 for (list = compose->header_list; list; list = list->next)
6818 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6820 compose->header_last = NULL;
6821 g_slist_free(compose->header_list);
6822 compose->header_list = NULL;
6823 compose->header_nextrow = 1;
6824 compose_create_header_entry(compose);
6827 static GtkWidget *compose_create_header(Compose *compose)
6829 GtkWidget *from_optmenu_hbox;
6830 GtkWidget *header_scrolledwin_main;
6831 GtkWidget *header_table_main;
6832 GtkWidget *header_scrolledwin;
6833 GtkWidget *header_table;
6835 /* parent with account selection and from header */
6836 header_scrolledwin_main = gtk_scrolled_window_new(NULL, NULL);
6837 gtk_widget_show(header_scrolledwin_main);
6838 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin_main), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6840 header_table_main = gtk_table_new(2, 2, FALSE);
6841 gtk_widget_show(header_table_main);
6842 gtk_container_set_border_width(GTK_CONTAINER(header_table_main), BORDER_WIDTH);
6843 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin_main), header_table_main);
6844 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin_main)))), GTK_SHADOW_NONE);
6846 from_optmenu_hbox = compose_account_option_menu_create(compose);
6847 gtk_table_attach(GTK_TABLE(header_table_main), from_optmenu_hbox,
6848 0, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6850 /* child with header labels and entries */
6851 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6852 gtk_widget_show(header_scrolledwin);
6853 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6855 header_table = gtk_table_new(2, 2, FALSE);
6856 gtk_widget_show(header_table);
6857 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6858 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6859 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6861 gtk_table_attach(GTK_TABLE(header_table_main), header_scrolledwin,
6862 0, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 2);
6864 compose->header_table = header_table;
6865 compose->header_list = NULL;
6866 compose->header_nextrow = 0;
6868 compose_create_header_entry(compose);
6870 compose->table = NULL;
6872 return header_scrolledwin_main;
6875 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6877 Compose *compose = (Compose *)data;
6878 GdkEventButton event;
6881 event.time = gtk_get_current_event_time();
6883 return attach_button_pressed(compose->attach_clist, &event, compose);
6886 static GtkWidget *compose_create_attach(Compose *compose)
6888 GtkWidget *attach_scrwin;
6889 GtkWidget *attach_clist;
6891 GtkListStore *store;
6892 GtkCellRenderer *renderer;
6893 GtkTreeViewColumn *column;
6894 GtkTreeSelection *selection;
6896 /* attachment list */
6897 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6898 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6899 GTK_POLICY_AUTOMATIC,
6900 GTK_POLICY_AUTOMATIC);
6901 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6903 store = gtk_list_store_new(N_ATTACH_COLS,
6909 G_TYPE_AUTO_POINTER,
6911 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6912 (GTK_TREE_MODEL(store)));
6913 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6914 g_object_unref(store);
6916 renderer = gtk_cell_renderer_text_new();
6917 column = gtk_tree_view_column_new_with_attributes
6918 (_("Mime type"), renderer, "text",
6919 COL_MIMETYPE, NULL);
6920 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6922 renderer = gtk_cell_renderer_text_new();
6923 column = gtk_tree_view_column_new_with_attributes
6924 (_("Size"), renderer, "text",
6926 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6928 renderer = gtk_cell_renderer_text_new();
6929 column = gtk_tree_view_column_new_with_attributes
6930 (_("Name"), renderer, "text",
6932 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6934 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6935 prefs_common.use_stripes_everywhere);
6936 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6937 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6939 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6940 G_CALLBACK(attach_selected), compose);
6941 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6942 G_CALLBACK(attach_button_pressed), compose);
6944 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6945 G_CALLBACK(popup_attach_button_pressed), compose);
6947 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6948 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6949 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6950 G_CALLBACK(popup_attach_button_pressed), compose);
6952 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6953 G_CALLBACK(attach_key_pressed), compose);
6956 gtk_drag_dest_set(attach_clist,
6957 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6958 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6959 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6960 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6961 G_CALLBACK(compose_attach_drag_received_cb),
6963 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6964 G_CALLBACK(compose_drag_drop),
6967 compose->attach_scrwin = attach_scrwin;
6968 compose->attach_clist = attach_clist;
6970 return attach_scrwin;
6973 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6974 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6976 static GtkWidget *compose_create_others(Compose *compose)
6979 GtkWidget *savemsg_checkbtn;
6980 GtkWidget *savemsg_combo;
6981 GtkWidget *savemsg_select;
6984 gchar *folderidentifier;
6986 /* Table for settings */
6987 table = gtk_table_new(3, 1, FALSE);
6988 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6989 gtk_widget_show(table);
6990 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6993 /* Save Message to folder */
6994 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6995 gtk_widget_show(savemsg_checkbtn);
6996 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6997 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6998 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
7000 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
7001 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
7003 savemsg_combo = gtk_combo_box_entry_new_text();
7004 compose->savemsg_checkbtn = savemsg_checkbtn;
7005 compose->savemsg_combo = savemsg_combo;
7006 gtk_widget_show(savemsg_combo);
7008 if (prefs_common.compose_save_to_history)
7009 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
7010 prefs_common.compose_save_to_history);
7012 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
7013 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
7014 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
7015 G_CALLBACK(compose_grab_focus_cb), compose);
7016 if (account_get_special_folder(compose->account, F_OUTBOX)) {
7017 folderidentifier = folder_item_get_identifier(account_get_special_folder
7018 (compose->account, F_OUTBOX));
7019 compose_set_save_to(compose, folderidentifier);
7020 g_free(folderidentifier);
7023 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
7024 gtk_widget_show(savemsg_select);
7025 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7026 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
7027 G_CALLBACK(compose_savemsg_select_cb),
7035 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
7037 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
7038 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
7041 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
7046 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
7049 path = folder_item_get_identifier(dest);
7051 compose_set_save_to(compose, path);
7055 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
7056 GdkAtom clip, GtkTextIter *insert_place);
7059 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
7063 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7065 if (event->button == 3) {
7067 GtkTextIter sel_start, sel_end;
7068 gboolean stuff_selected;
7070 /* move the cursor to allow GtkAspell to check the word
7071 * under the mouse */
7072 if (event->x && event->y) {
7073 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7074 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7076 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7079 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
7080 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7083 stuff_selected = gtk_text_buffer_get_selection_bounds(
7085 &sel_start, &sel_end);
7087 gtk_text_buffer_place_cursor (buffer, &iter);
7088 /* reselect stuff */
7090 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
7091 gtk_text_buffer_select_range(buffer,
7092 &sel_start, &sel_end);
7094 return FALSE; /* pass the event so that the right-click goes through */
7097 if (event->button == 2) {
7102 /* get the middle-click position to paste at the correct place */
7103 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7104 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7106 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7109 entry_paste_clipboard(compose, text,
7110 prefs_common.linewrap_pastes,
7111 GDK_SELECTION_PRIMARY, &iter);
7119 static void compose_spell_menu_changed(void *data)
7121 Compose *compose = (Compose *)data;
7123 GtkWidget *menuitem;
7124 GtkWidget *parent_item;
7125 GtkMenu *menu = GTK_MENU(gtk_menu_new());
7128 if (compose->gtkaspell == NULL)
7131 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
7132 "/Menu/Spelling/Options");
7134 /* setting the submenu removes /Spelling/Options from the factory
7135 * so we need to save it */
7137 if (parent_item == NULL) {
7138 parent_item = compose->aspell_options_menu;
7139 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
7141 compose->aspell_options_menu = parent_item;
7143 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
7145 spell_menu = g_slist_reverse(spell_menu);
7146 for (items = spell_menu;
7147 items; items = items->next) {
7148 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
7149 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
7150 gtk_widget_show(GTK_WIDGET(menuitem));
7152 g_slist_free(spell_menu);
7154 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7155 gtk_widget_show(parent_item);
7158 static void compose_dict_changed(void *data)
7160 Compose *compose = (Compose *) data;
7162 if(compose->gtkaspell &&
7163 compose->gtkaspell->recheck_when_changing_dict == FALSE)
7166 gtkaspell_highlight_all(compose->gtkaspell);
7167 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7171 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7173 Compose *compose = (Compose *)data;
7174 GdkEventButton event;
7177 event.time = gtk_get_current_event_time();
7181 return text_clicked(compose->text, &event, compose);
7184 static gboolean compose_force_window_origin = TRUE;
7185 static Compose *compose_create(PrefsAccount *account,
7194 GtkWidget *handlebox;
7196 GtkWidget *notebook;
7198 GtkWidget *attach_hbox;
7199 GtkWidget *attach_lab1;
7200 GtkWidget *attach_lab2;
7205 GtkWidget *subject_hbox;
7206 GtkWidget *subject_frame;
7207 GtkWidget *subject_entry;
7211 GtkWidget *edit_vbox;
7212 GtkWidget *ruler_hbox;
7214 GtkWidget *scrolledwin;
7216 GtkTextBuffer *buffer;
7217 GtkClipboard *clipboard;
7219 UndoMain *undostruct;
7221 gchar *titles[N_ATTACH_COLS];
7222 GtkWidget *popupmenu;
7223 GtkWidget *tmpl_menu;
7224 GtkActionGroup *action_group = NULL;
7227 GtkAspell * gtkaspell = NULL;
7230 static GdkGeometry geometry;
7232 cm_return_val_if_fail(account != NULL, NULL);
7234 debug_print("Creating compose window...\n");
7235 compose = g_new0(Compose, 1);
7237 titles[COL_MIMETYPE] = _("MIME type");
7238 titles[COL_SIZE] = _("Size");
7239 titles[COL_NAME] = _("Name");
7240 titles[COL_CHARSET] = _("Charset");
7242 compose->batch = batch;
7243 compose->account = account;
7244 compose->folder = folder;
7246 compose->mutex = g_mutex_new();
7247 compose->set_cursor_pos = -1;
7249 #if !(GTK_CHECK_VERSION(2,12,0))
7250 compose->tooltips = tips;
7253 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7255 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7256 gtk_widget_set_size_request(window, prefs_common.compose_width,
7257 prefs_common.compose_height);
7259 if (!geometry.max_width) {
7260 geometry.max_width = gdk_screen_width();
7261 geometry.max_height = gdk_screen_height();
7264 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7265 &geometry, GDK_HINT_MAX_SIZE);
7266 if (!geometry.min_width) {
7267 geometry.min_width = 600;
7268 geometry.min_height = 440;
7270 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7271 &geometry, GDK_HINT_MIN_SIZE);
7273 #ifndef GENERIC_UMPC
7274 if (compose_force_window_origin)
7275 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7276 prefs_common.compose_y);
7278 g_signal_connect(G_OBJECT(window), "delete_event",
7279 G_CALLBACK(compose_delete_cb), compose);
7280 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7281 gtk_widget_realize(window);
7283 gtkut_widget_set_composer_icon(window);
7285 vbox = gtk_vbox_new(FALSE, 0);
7286 gtk_container_add(GTK_CONTAINER(window), vbox);
7288 compose->ui_manager = gtk_ui_manager_new();
7289 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7290 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7291 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7292 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7293 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7294 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7295 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7296 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7297 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7298 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7301 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7303 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7306 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7307 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7309 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7311 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7312 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7313 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7316 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7317 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7318 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7319 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7320 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7321 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7322 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7323 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7324 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7325 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM)
7326 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7327 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7330 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7331 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7332 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7334 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7335 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7336 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7338 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7339 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7340 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7341 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7343 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7345 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7346 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7347 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7348 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7349 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7350 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7351 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7352 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7353 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7354 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7355 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7356 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7357 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7358 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7359 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7361 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7363 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7364 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7365 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7366 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7367 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7369 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7371 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7375 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7376 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7377 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7378 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7379 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7380 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7384 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7385 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7386 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7387 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7388 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7390 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7391 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7392 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7393 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7394 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7397 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7398 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7399 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7400 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7401 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7402 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7403 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7405 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7406 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7407 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7408 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7409 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7411 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7413 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7414 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7415 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7416 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7417 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7419 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7420 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)
7421 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)
7422 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7424 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7426 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7427 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)
7428 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)
7430 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7432 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7433 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)
7434 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7436 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7437 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)
7438 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7440 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7442 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7443 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)
7444 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7445 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7446 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7448 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7449 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)
7450 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)
7451 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7452 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7454 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7455 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7456 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7457 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7458 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7459 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7461 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7462 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7463 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)
7465 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7466 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7467 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7471 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7472 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7473 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7474 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7475 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7476 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7479 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7481 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7482 gtk_widget_show_all(menubar);
7484 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7486 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7488 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7491 if (prefs_common.toolbar_detachable) {
7492 handlebox = gtk_handle_box_new();
7494 handlebox = gtk_hbox_new(FALSE, 0);
7496 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7498 gtk_widget_realize(handlebox);
7500 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7503 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7507 vbox2 = gtk_vbox_new(FALSE, 2);
7508 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7509 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7512 notebook = gtk_notebook_new();
7513 gtk_widget_set_size_request(notebook, -1, prefs_common.compose_notebook_height);
7514 gtk_widget_show(notebook);
7516 /* header labels and entries */
7517 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7518 compose_create_header(compose),
7519 gtk_label_new_with_mnemonic(_("Hea_der")));
7520 /* attachment list */
7521 attach_hbox = gtk_hbox_new(FALSE, 0);
7522 gtk_widget_show(attach_hbox);
7524 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7525 gtk_widget_show(attach_lab1);
7526 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7528 attach_lab2 = gtk_label_new("");
7529 gtk_widget_show(attach_lab2);
7530 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7532 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7533 compose_create_attach(compose),
7536 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7537 compose_create_others(compose),
7538 gtk_label_new_with_mnemonic(_("Othe_rs")));
7541 subject_hbox = gtk_hbox_new(FALSE, 0);
7542 gtk_widget_show(subject_hbox);
7544 subject_frame = gtk_frame_new(NULL);
7545 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7546 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7547 gtk_widget_show(subject_frame);
7549 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7550 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7551 gtk_widget_show(subject);
7553 label = gtk_label_new(_("Subject:"));
7554 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7555 gtk_widget_show(label);
7558 subject_entry = claws_spell_entry_new();
7560 subject_entry = gtk_entry_new();
7562 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7563 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7564 G_CALLBACK(compose_grab_focus_cb), compose);
7565 gtk_widget_show(subject_entry);
7566 compose->subject_entry = subject_entry;
7567 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7569 edit_vbox = gtk_vbox_new(FALSE, 0);
7571 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7574 ruler_hbox = gtk_hbox_new(FALSE, 0);
7575 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7577 ruler = gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL);
7578 gtk_shruler_set_range(GTK_SHRULER(ruler), 0.0, 100.0, 1.0);
7579 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7583 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7584 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7585 GTK_POLICY_AUTOMATIC,
7586 GTK_POLICY_AUTOMATIC);
7587 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7589 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7591 text = gtk_text_view_new();
7592 if (prefs_common.show_compose_margin) {
7593 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7594 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7596 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7597 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7598 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7599 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7600 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7602 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7603 g_signal_connect(G_OBJECT(notebook), "size_allocate",
7604 G_CALLBACK(compose_notebook_size_alloc), compose);
7605 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7606 G_CALLBACK(compose_edit_size_alloc),
7608 g_signal_connect(G_OBJECT(buffer), "changed",
7609 G_CALLBACK(compose_changed_cb), compose);
7610 g_signal_connect(G_OBJECT(text), "grab_focus",
7611 G_CALLBACK(compose_grab_focus_cb), compose);
7612 g_signal_connect(G_OBJECT(buffer), "insert_text",
7613 G_CALLBACK(text_inserted), compose);
7614 g_signal_connect(G_OBJECT(text), "button_press_event",
7615 G_CALLBACK(text_clicked), compose);
7617 g_signal_connect(G_OBJECT(text), "popup-menu",
7618 G_CALLBACK(compose_popup_menu), compose);
7620 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7621 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7622 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7623 G_CALLBACK(compose_popup_menu), compose);
7625 g_signal_connect(G_OBJECT(subject_entry), "changed",
7626 G_CALLBACK(compose_changed_cb), compose);
7629 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7630 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7631 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7632 g_signal_connect(G_OBJECT(text), "drag_data_received",
7633 G_CALLBACK(compose_insert_drag_received_cb),
7635 g_signal_connect(G_OBJECT(text), "drag-drop",
7636 G_CALLBACK(compose_drag_drop),
7638 gtk_widget_show_all(vbox);
7640 /* pane between attach clist and text */
7641 paned = gtk_vpaned_new();
7642 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7644 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7645 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7647 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7649 gtk_paned_add1(GTK_PANED(paned), notebook);
7650 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7651 gtk_widget_show_all(paned);
7654 if (prefs_common.textfont) {
7655 PangoFontDescription *font_desc;
7657 font_desc = pango_font_description_from_string
7658 (prefs_common.textfont);
7660 gtk_widget_modify_font(text, font_desc);
7661 pango_font_description_free(font_desc);
7665 gtk_action_group_add_actions(action_group, compose_popup_entries,
7666 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7667 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7668 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7669 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7670 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7671 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7672 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7674 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7676 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7677 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7678 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7680 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7682 undostruct = undo_init(text);
7683 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7686 address_completion_start(window);
7688 compose->window = window;
7689 compose->vbox = vbox;
7690 compose->menubar = menubar;
7691 compose->handlebox = handlebox;
7693 compose->vbox2 = vbox2;
7695 compose->paned = paned;
7697 compose->attach_label = attach_lab2;
7699 compose->notebook = notebook;
7700 compose->edit_vbox = edit_vbox;
7701 compose->ruler_hbox = ruler_hbox;
7702 compose->ruler = ruler;
7703 compose->scrolledwin = scrolledwin;
7704 compose->text = text;
7706 compose->focused_editable = NULL;
7708 compose->popupmenu = popupmenu;
7710 compose->tmpl_menu = tmpl_menu;
7712 compose->mode = mode;
7713 compose->rmode = mode;
7715 compose->targetinfo = NULL;
7716 compose->replyinfo = NULL;
7717 compose->fwdinfo = NULL;
7719 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7720 g_str_equal, (GDestroyNotify) g_free, NULL);
7722 compose->replyto = NULL;
7724 compose->bcc = NULL;
7725 compose->followup_to = NULL;
7727 compose->ml_post = NULL;
7729 compose->inreplyto = NULL;
7730 compose->references = NULL;
7731 compose->msgid = NULL;
7732 compose->boundary = NULL;
7734 compose->autowrap = prefs_common.autowrap;
7735 compose->autoindent = prefs_common.auto_indent;
7736 compose->use_signing = FALSE;
7737 compose->use_encryption = FALSE;
7738 compose->privacy_system = NULL;
7740 compose->modified = FALSE;
7742 compose->return_receipt = FALSE;
7744 compose->to_list = NULL;
7745 compose->newsgroup_list = NULL;
7747 compose->undostruct = undostruct;
7749 compose->sig_str = NULL;
7751 compose->exteditor_file = NULL;
7752 compose->exteditor_pid = -1;
7753 compose->exteditor_tag = -1;
7754 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7757 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7758 if (mode != COMPOSE_REDIRECT) {
7759 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7760 strcmp(prefs_common.dictionary, "")) {
7761 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7762 prefs_common.alt_dictionary,
7763 conv_get_locale_charset_str(),
7764 prefs_common.misspelled_col,
7765 prefs_common.check_while_typing,
7766 prefs_common.recheck_when_changing_dict,
7767 prefs_common.use_alternate,
7768 prefs_common.use_both_dicts,
7769 GTK_TEXT_VIEW(text),
7770 GTK_WINDOW(compose->window),
7771 compose_dict_changed,
7772 compose_spell_menu_changed,
7775 alertpanel_error(_("Spell checker could not "
7777 gtkaspell_checkers_strerror());
7778 gtkaspell_checkers_reset_error();
7780 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7784 compose->gtkaspell = gtkaspell;
7785 compose_spell_menu_changed(compose);
7786 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7789 compose_select_account(compose, account, TRUE);
7791 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7792 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7794 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7795 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7797 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7798 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7800 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7801 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7803 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7804 if (account->protocol != A_NNTP)
7805 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7806 prefs_common_translated_header_name("To:"));
7808 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7809 prefs_common_translated_header_name("Newsgroups:"));
7811 #ifndef USE_NEW_ADDRBOOK
7812 addressbook_set_target_compose(compose);
7814 if (mode != COMPOSE_REDIRECT)
7815 compose_set_template_menu(compose);
7817 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7820 compose_list = g_list_append(compose_list, compose);
7822 if (!prefs_common.show_ruler)
7823 gtk_widget_hide(ruler_hbox);
7825 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7828 compose->priority = PRIORITY_NORMAL;
7829 compose_update_priority_menu_item(compose);
7831 compose_set_out_encoding(compose);
7834 compose_update_actions_menu(compose);
7836 /* Privacy Systems menu */
7837 compose_update_privacy_systems_menu(compose);
7839 activate_privacy_system(compose, account, TRUE);
7840 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7842 gtk_widget_realize(window);
7844 gtk_widget_show(window);
7846 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7847 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7854 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7859 GtkWidget *optmenubox;
7862 GtkWidget *from_name = NULL;
7863 #if !(GTK_CHECK_VERSION(2,12,0))
7864 GtkTooltips *tips = compose->tooltips;
7867 gint num = 0, def_menu = 0;
7869 accounts = account_get_list();
7870 cm_return_val_if_fail(accounts != NULL, NULL);
7872 optmenubox = gtk_event_box_new();
7873 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7874 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7876 hbox = gtk_hbox_new(FALSE, 6);
7877 from_name = gtk_entry_new();
7879 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7880 G_CALLBACK(compose_grab_focus_cb), compose);
7882 for (; accounts != NULL; accounts = accounts->next, num++) {
7883 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7884 gchar *name, *from = NULL;
7886 if (ac == compose->account) def_menu = num;
7888 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7891 if (ac == compose->account) {
7892 if (ac->name && *ac->name) {
7894 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7895 from = g_strdup_printf("%s <%s>",
7897 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7899 from = g_strdup_printf("%s",
7901 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7904 COMBOBOX_ADD(menu, name, ac->account_id);
7909 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7911 g_signal_connect(G_OBJECT(optmenu), "changed",
7912 G_CALLBACK(account_activated),
7914 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7915 G_CALLBACK(compose_entry_popup_extend),
7918 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7919 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7921 CLAWS_SET_TIP(optmenubox,
7922 _("Account to use for this email"));
7923 CLAWS_SET_TIP(from_name,
7924 _("Sender address to be used"));
7926 compose->account_combo = optmenu;
7927 compose->from_name = from_name;
7932 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7934 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7935 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7936 Compose *compose = (Compose *) data;
7938 compose->priority = value;
7942 static void compose_reply_change_mode(Compose *compose,
7945 gboolean was_modified = compose->modified;
7947 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7949 cm_return_if_fail(compose->replyinfo != NULL);
7951 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7953 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7955 if (action == COMPOSE_REPLY_TO_ALL)
7957 if (action == COMPOSE_REPLY_TO_SENDER)
7959 if (action == COMPOSE_REPLY_TO_LIST)
7962 compose_remove_header_entries(compose);
7963 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7964 if (compose->account->set_autocc && compose->account->auto_cc)
7965 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7967 if (compose->account->set_autobcc && compose->account->auto_bcc)
7968 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7970 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7971 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7972 compose_show_first_last_header(compose, TRUE);
7973 compose->modified = was_modified;
7974 compose_set_title(compose);
7977 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7979 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7980 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7981 Compose *compose = (Compose *) data;
7984 compose_reply_change_mode(compose, value);
7987 static void compose_update_priority_menu_item(Compose * compose)
7989 GtkWidget *menuitem = NULL;
7990 switch (compose->priority) {
7991 case PRIORITY_HIGHEST:
7992 menuitem = gtk_ui_manager_get_widget
7993 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7996 menuitem = gtk_ui_manager_get_widget
7997 (compose->ui_manager, "/Menu/Options/Priority/High");
7999 case PRIORITY_NORMAL:
8000 menuitem = gtk_ui_manager_get_widget
8001 (compose->ui_manager, "/Menu/Options/Priority/Normal");
8004 menuitem = gtk_ui_manager_get_widget
8005 (compose->ui_manager, "/Menu/Options/Priority/Low");
8007 case PRIORITY_LOWEST:
8008 menuitem = gtk_ui_manager_get_widget
8009 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
8012 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8015 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
8017 Compose *compose = (Compose *) data;
8019 gboolean can_sign = FALSE, can_encrypt = FALSE;
8021 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
8023 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
8026 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
8027 g_free(compose->privacy_system);
8028 compose->privacy_system = NULL;
8029 if (systemid != NULL) {
8030 compose->privacy_system = g_strdup(systemid);
8032 can_sign = privacy_system_can_sign(systemid);
8033 can_encrypt = privacy_system_can_encrypt(systemid);
8036 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
8038 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8039 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8042 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
8044 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8045 GtkWidget *menuitem = NULL;
8046 GList *children, *amenu;
8047 gboolean can_sign = FALSE, can_encrypt = FALSE;
8048 gboolean found = FALSE;
8050 if (compose->privacy_system != NULL) {
8052 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8053 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
8054 cm_return_if_fail(menuitem != NULL);
8056 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
8059 while (amenu != NULL) {
8060 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
8061 if (systemid != NULL) {
8062 if (strcmp(systemid, compose->privacy_system) == 0 &&
8063 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8064 menuitem = GTK_WIDGET(amenu->data);
8066 can_sign = privacy_system_can_sign(systemid);
8067 can_encrypt = privacy_system_can_encrypt(systemid);
8071 } else if (strlen(compose->privacy_system) == 0 &&
8072 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8073 menuitem = GTK_WIDGET(amenu->data);
8076 can_encrypt = FALSE;
8081 amenu = amenu->next;
8083 g_list_free(children);
8084 if (menuitem != NULL)
8085 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8087 if (warn && !found && strlen(compose->privacy_system)) {
8088 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8089 "will not be able to sign or encrypt this message."),
8090 compose->privacy_system);
8094 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8095 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8098 static void compose_set_out_encoding(Compose *compose)
8100 CharSet out_encoding;
8101 const gchar *branch = NULL;
8102 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
8104 switch(out_encoding) {
8105 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8106 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
8107 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
8108 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
8109 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
8110 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
8111 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
8112 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
8113 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
8114 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
8115 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
8116 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
8117 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
8118 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
8119 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
8120 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
8121 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
8122 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
8123 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
8124 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
8125 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
8126 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
8127 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
8128 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
8129 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
8130 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
8131 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
8132 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
8133 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
8134 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
8135 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
8136 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
8137 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8139 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8142 static void compose_set_template_menu(Compose *compose)
8144 GSList *tmpl_list, *cur;
8148 tmpl_list = template_get_config();
8150 menu = gtk_menu_new();
8152 gtk_menu_set_accel_group (GTK_MENU (menu),
8153 gtk_ui_manager_get_accel_group(compose->ui_manager));
8154 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8155 Template *tmpl = (Template *)cur->data;
8156 gchar *accel_path = NULL;
8157 item = gtk_menu_item_new_with_label(tmpl->name);
8158 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8159 g_signal_connect(G_OBJECT(item), "activate",
8160 G_CALLBACK(compose_template_activate_cb),
8162 g_object_set_data(G_OBJECT(item), "template", tmpl);
8163 gtk_widget_show(item);
8164 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8165 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8169 gtk_widget_show(menu);
8170 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8173 void compose_update_actions_menu(Compose *compose)
8175 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8178 static void compose_update_privacy_systems_menu(Compose *compose)
8180 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8181 GSList *systems, *cur;
8183 GtkWidget *system_none;
8185 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8186 GtkWidget *privacy_menu = gtk_menu_new();
8188 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8189 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8191 g_signal_connect(G_OBJECT(system_none), "activate",
8192 G_CALLBACK(compose_set_privacy_system_cb), compose);
8194 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8195 gtk_widget_show(system_none);
8197 systems = privacy_get_system_ids();
8198 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8199 gchar *systemid = cur->data;
8201 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8202 widget = gtk_radio_menu_item_new_with_label(group,
8203 privacy_system_get_name(systemid));
8204 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8205 g_strdup(systemid), g_free);
8206 g_signal_connect(G_OBJECT(widget), "activate",
8207 G_CALLBACK(compose_set_privacy_system_cb), compose);
8209 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8210 gtk_widget_show(widget);
8213 g_slist_free(systems);
8214 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8215 gtk_widget_show_all(privacy_menu);
8216 gtk_widget_show_all(privacy_menuitem);
8219 void compose_reflect_prefs_all(void)
8224 for (cur = compose_list; cur != NULL; cur = cur->next) {
8225 compose = (Compose *)cur->data;
8226 compose_set_template_menu(compose);
8230 void compose_reflect_prefs_pixmap_theme(void)
8235 for (cur = compose_list; cur != NULL; cur = cur->next) {
8236 compose = (Compose *)cur->data;
8237 toolbar_update(TOOLBAR_COMPOSE, compose);
8241 static const gchar *compose_quote_char_from_context(Compose *compose)
8243 const gchar *qmark = NULL;
8245 cm_return_val_if_fail(compose != NULL, NULL);
8247 switch (compose->mode) {
8248 /* use forward-specific quote char */
8249 case COMPOSE_FORWARD:
8250 case COMPOSE_FORWARD_AS_ATTACH:
8251 case COMPOSE_FORWARD_INLINE:
8252 if (compose->folder && compose->folder->prefs &&
8253 compose->folder->prefs->forward_with_format)
8254 qmark = compose->folder->prefs->forward_quotemark;
8255 else if (compose->account->forward_with_format)
8256 qmark = compose->account->forward_quotemark;
8258 qmark = prefs_common.fw_quotemark;
8261 /* use reply-specific quote char in all other modes */
8263 if (compose->folder && compose->folder->prefs &&
8264 compose->folder->prefs->reply_with_format)
8265 qmark = compose->folder->prefs->reply_quotemark;
8266 else if (compose->account->reply_with_format)
8267 qmark = compose->account->reply_quotemark;
8269 qmark = prefs_common.quotemark;
8273 if (qmark == NULL || *qmark == '\0')
8279 static void compose_template_apply(Compose *compose, Template *tmpl,
8283 GtkTextBuffer *buffer;
8287 gchar *parsed_str = NULL;
8288 gint cursor_pos = 0;
8289 const gchar *err_msg = _("The body of the template has an error at line %d.");
8292 /* process the body */
8294 text = GTK_TEXT_VIEW(compose->text);
8295 buffer = gtk_text_view_get_buffer(text);
8298 qmark = compose_quote_char_from_context(compose);
8300 if (compose->replyinfo != NULL) {
8303 gtk_text_buffer_set_text(buffer, "", -1);
8304 mark = gtk_text_buffer_get_insert(buffer);
8305 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8307 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8308 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8310 } else if (compose->fwdinfo != NULL) {
8313 gtk_text_buffer_set_text(buffer, "", -1);
8314 mark = gtk_text_buffer_get_insert(buffer);
8315 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8317 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8318 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8321 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8323 GtkTextIter start, end;
8326 gtk_text_buffer_get_start_iter(buffer, &start);
8327 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8328 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8330 /* clear the buffer now */
8332 gtk_text_buffer_set_text(buffer, "", -1);
8334 parsed_str = compose_quote_fmt(compose, dummyinfo,
8335 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8336 procmsg_msginfo_free( dummyinfo );
8342 gtk_text_buffer_set_text(buffer, "", -1);
8343 mark = gtk_text_buffer_get_insert(buffer);
8344 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8347 if (replace && parsed_str && compose->account->auto_sig)
8348 compose_insert_sig(compose, FALSE);
8350 if (replace && parsed_str) {
8351 gtk_text_buffer_get_start_iter(buffer, &iter);
8352 gtk_text_buffer_place_cursor(buffer, &iter);
8356 cursor_pos = quote_fmt_get_cursor_pos();
8357 compose->set_cursor_pos = cursor_pos;
8358 if (cursor_pos == -1)
8360 gtk_text_buffer_get_start_iter(buffer, &iter);
8361 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8362 gtk_text_buffer_place_cursor(buffer, &iter);
8365 /* process the other fields */
8367 compose_template_apply_fields(compose, tmpl);
8368 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8369 quote_fmt_reset_vartable();
8370 compose_changed_cb(NULL, compose);
8373 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8374 gtkaspell_highlight_all(compose->gtkaspell);
8378 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8380 MsgInfo* dummyinfo = NULL;
8381 MsgInfo *msginfo = NULL;
8384 if (compose->replyinfo != NULL)
8385 msginfo = compose->replyinfo;
8386 else if (compose->fwdinfo != NULL)
8387 msginfo = compose->fwdinfo;
8389 dummyinfo = compose_msginfo_new_from_compose(compose);
8390 msginfo = dummyinfo;
8393 if (tmpl->from && *tmpl->from != '\0') {
8395 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8396 compose->gtkaspell);
8398 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8400 quote_fmt_scan_string(tmpl->from);
8403 buf = quote_fmt_get_buffer();
8405 alertpanel_error(_("Template From format error."));
8407 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8411 if (tmpl->to && *tmpl->to != '\0') {
8413 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8414 compose->gtkaspell);
8416 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8418 quote_fmt_scan_string(tmpl->to);
8421 buf = quote_fmt_get_buffer();
8423 alertpanel_error(_("Template To format error."));
8425 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8429 if (tmpl->cc && *tmpl->cc != '\0') {
8431 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8432 compose->gtkaspell);
8434 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8436 quote_fmt_scan_string(tmpl->cc);
8439 buf = quote_fmt_get_buffer();
8441 alertpanel_error(_("Template Cc format error."));
8443 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8447 if (tmpl->bcc && *tmpl->bcc != '\0') {
8449 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8450 compose->gtkaspell);
8452 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8454 quote_fmt_scan_string(tmpl->bcc);
8457 buf = quote_fmt_get_buffer();
8459 alertpanel_error(_("Template Bcc format error."));
8461 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8465 /* process the subject */
8466 if (tmpl->subject && *tmpl->subject != '\0') {
8468 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8469 compose->gtkaspell);
8471 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8473 quote_fmt_scan_string(tmpl->subject);
8476 buf = quote_fmt_get_buffer();
8478 alertpanel_error(_("Template subject format error."));
8480 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8484 procmsg_msginfo_free( dummyinfo );
8487 static void compose_destroy(Compose *compose)
8489 GtkAllocation allocation;
8490 GtkTextBuffer *buffer;
8491 GtkClipboard *clipboard;
8493 compose_list = g_list_remove(compose_list, compose);
8495 if (compose->updating) {
8496 debug_print("danger, not destroying anything now\n");
8497 compose->deferred_destroy = TRUE;
8500 /* NOTE: address_completion_end() does nothing with the window
8501 * however this may change. */
8502 address_completion_end(compose->window);
8504 slist_free_strings(compose->to_list);
8505 g_slist_free(compose->to_list);
8506 slist_free_strings(compose->newsgroup_list);
8507 g_slist_free(compose->newsgroup_list);
8508 slist_free_strings(compose->header_list);
8509 g_slist_free(compose->header_list);
8511 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8513 g_hash_table_destroy(compose->email_hashtable);
8515 procmsg_msginfo_free(compose->targetinfo);
8516 procmsg_msginfo_free(compose->replyinfo);
8517 procmsg_msginfo_free(compose->fwdinfo);
8519 g_free(compose->replyto);
8520 g_free(compose->cc);
8521 g_free(compose->bcc);
8522 g_free(compose->newsgroups);
8523 g_free(compose->followup_to);
8525 g_free(compose->ml_post);
8527 g_free(compose->inreplyto);
8528 g_free(compose->references);
8529 g_free(compose->msgid);
8530 g_free(compose->boundary);
8532 g_free(compose->redirect_filename);
8533 if (compose->undostruct)
8534 undo_destroy(compose->undostruct);
8536 g_free(compose->sig_str);
8538 g_free(compose->exteditor_file);
8540 g_free(compose->orig_charset);
8542 g_free(compose->privacy_system);
8544 #ifndef USE_NEW_ADDRBOOK
8545 if (addressbook_get_target_compose() == compose)
8546 addressbook_set_target_compose(NULL);
8549 if (compose->gtkaspell) {
8550 gtkaspell_delete(compose->gtkaspell);
8551 compose->gtkaspell = NULL;
8555 if (!compose->batch) {
8556 gtk_widget_get_allocation(compose->window, &allocation);
8557 prefs_common.compose_width = allocation.width;
8558 prefs_common.compose_height = allocation.height;
8561 if (!gtk_widget_get_parent(compose->paned))
8562 gtk_widget_destroy(compose->paned);
8563 gtk_widget_destroy(compose->popupmenu);
8565 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8566 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8567 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8569 gtk_widget_destroy(compose->window);
8570 toolbar_destroy(compose->toolbar);
8571 g_free(compose->toolbar);
8572 g_mutex_free(compose->mutex);
8576 static void compose_attach_info_free(AttachInfo *ainfo)
8578 g_free(ainfo->file);
8579 g_free(ainfo->content_type);
8580 g_free(ainfo->name);
8581 g_free(ainfo->charset);
8585 static void compose_attach_update_label(Compose *compose)
8590 GtkTreeModel *model;
8595 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8596 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8597 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8601 while(gtk_tree_model_iter_next(model, &iter))
8604 text = g_strdup_printf("(%d)", i);
8605 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8609 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8611 Compose *compose = (Compose *)data;
8612 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8613 GtkTreeSelection *selection;
8615 GtkTreeModel *model;
8617 selection = gtk_tree_view_get_selection(tree_view);
8618 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8623 for (cur = sel; cur != NULL; cur = cur->next) {
8624 GtkTreePath *path = cur->data;
8625 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8628 gtk_tree_path_free(path);
8631 for (cur = sel; cur != NULL; cur = cur->next) {
8632 GtkTreeRowReference *ref = cur->data;
8633 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8636 if (gtk_tree_model_get_iter(model, &iter, path))
8637 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8639 gtk_tree_path_free(path);
8640 gtk_tree_row_reference_free(ref);
8644 compose_attach_update_label(compose);
8647 static struct _AttachProperty
8650 GtkWidget *mimetype_entry;
8651 GtkWidget *encoding_optmenu;
8652 GtkWidget *path_entry;
8653 GtkWidget *filename_entry;
8655 GtkWidget *cancel_btn;
8658 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8660 gtk_tree_path_free((GtkTreePath *)ptr);
8663 static void compose_attach_property(GtkAction *action, gpointer data)
8665 Compose *compose = (Compose *)data;
8666 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8668 GtkComboBox *optmenu;
8669 GtkTreeSelection *selection;
8671 GtkTreeModel *model;
8674 static gboolean cancelled;
8676 /* only if one selected */
8677 selection = gtk_tree_view_get_selection(tree_view);
8678 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8681 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8685 path = (GtkTreePath *) sel->data;
8686 gtk_tree_model_get_iter(model, &iter, path);
8687 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8690 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8696 if (!attach_prop.window)
8697 compose_attach_property_create(&cancelled);
8698 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8699 gtk_widget_grab_focus(attach_prop.ok_btn);
8700 gtk_widget_show(attach_prop.window);
8701 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8703 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8704 if (ainfo->encoding == ENC_UNKNOWN)
8705 combobox_select_by_data(optmenu, ENC_BASE64);
8707 combobox_select_by_data(optmenu, ainfo->encoding);
8709 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8710 ainfo->content_type ? ainfo->content_type : "");
8711 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8712 ainfo->file ? ainfo->file : "");
8713 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8714 ainfo->name ? ainfo->name : "");
8717 const gchar *entry_text;
8719 gchar *cnttype = NULL;
8726 gtk_widget_hide(attach_prop.window);
8727 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8732 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8733 if (*entry_text != '\0') {
8736 text = g_strstrip(g_strdup(entry_text));
8737 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8738 cnttype = g_strdup(text);
8741 alertpanel_error(_("Invalid MIME type."));
8747 ainfo->encoding = combobox_get_active_data(optmenu);
8749 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8750 if (*entry_text != '\0') {
8751 if (is_file_exist(entry_text) &&
8752 (size = get_file_size(entry_text)) > 0)
8753 file = g_strdup(entry_text);
8756 (_("File doesn't exist or is empty."));
8762 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8763 if (*entry_text != '\0') {
8764 g_free(ainfo->name);
8765 ainfo->name = g_strdup(entry_text);
8769 g_free(ainfo->content_type);
8770 ainfo->content_type = cnttype;
8773 g_free(ainfo->file);
8777 ainfo->size = (goffset)size;
8779 /* update tree store */
8780 text = to_human_readable(ainfo->size);
8781 gtk_tree_model_get_iter(model, &iter, path);
8782 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8783 COL_MIMETYPE, ainfo->content_type,
8785 COL_NAME, ainfo->name,
8786 COL_CHARSET, ainfo->charset,
8792 gtk_tree_path_free(path);
8795 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8797 label = gtk_label_new(str); \
8798 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8799 GTK_FILL, 0, 0, 0); \
8800 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8802 entry = gtk_entry_new(); \
8803 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8804 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8807 static void compose_attach_property_create(gboolean *cancelled)
8813 GtkWidget *mimetype_entry;
8816 GtkListStore *optmenu_menu;
8817 GtkWidget *path_entry;
8818 GtkWidget *filename_entry;
8821 GtkWidget *cancel_btn;
8822 GList *mime_type_list, *strlist;
8825 debug_print("Creating attach_property window...\n");
8827 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8828 gtk_widget_set_size_request(window, 480, -1);
8829 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8830 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8831 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8832 g_signal_connect(G_OBJECT(window), "delete_event",
8833 G_CALLBACK(attach_property_delete_event),
8835 g_signal_connect(G_OBJECT(window), "key_press_event",
8836 G_CALLBACK(attach_property_key_pressed),
8839 vbox = gtk_vbox_new(FALSE, 8);
8840 gtk_container_add(GTK_CONTAINER(window), vbox);
8842 table = gtk_table_new(4, 2, FALSE);
8843 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8844 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8845 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8847 label = gtk_label_new(_("MIME type"));
8848 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8850 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8851 mimetype_entry = gtk_combo_box_entry_new_text();
8852 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8853 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8855 /* stuff with list */
8856 mime_type_list = procmime_get_mime_type_list();
8858 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8859 MimeType *type = (MimeType *) mime_type_list->data;
8862 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8864 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8867 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8868 (GCompareFunc)strcmp2);
8871 for (mime_type_list = strlist; mime_type_list != NULL;
8872 mime_type_list = mime_type_list->next) {
8873 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8874 g_free(mime_type_list->data);
8876 g_list_free(strlist);
8877 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8878 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8880 label = gtk_label_new(_("Encoding"));
8881 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8883 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8885 hbox = gtk_hbox_new(FALSE, 0);
8886 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8887 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8889 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8890 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8892 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8893 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8894 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8895 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8896 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8898 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8900 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8901 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8903 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8904 &ok_btn, GTK_STOCK_OK,
8906 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8907 gtk_widget_grab_default(ok_btn);
8909 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8910 G_CALLBACK(attach_property_ok),
8912 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8913 G_CALLBACK(attach_property_cancel),
8916 gtk_widget_show_all(vbox);
8918 attach_prop.window = window;
8919 attach_prop.mimetype_entry = mimetype_entry;
8920 attach_prop.encoding_optmenu = optmenu;
8921 attach_prop.path_entry = path_entry;
8922 attach_prop.filename_entry = filename_entry;
8923 attach_prop.ok_btn = ok_btn;
8924 attach_prop.cancel_btn = cancel_btn;
8927 #undef SET_LABEL_AND_ENTRY
8929 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8935 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8941 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8942 gboolean *cancelled)
8950 static gboolean attach_property_key_pressed(GtkWidget *widget,
8952 gboolean *cancelled)
8954 if (event && event->keyval == GDK_KEY_Escape) {
8958 if (event && event->keyval == GDK_KEY_Return) {
8966 static void compose_exec_ext_editor(Compose *compose)
8973 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8974 G_DIR_SEPARATOR, compose);
8976 if (pipe(pipe_fds) < 0) {
8982 if ((pid = fork()) < 0) {
8989 /* close the write side of the pipe */
8992 compose->exteditor_file = g_strdup(tmp);
8993 compose->exteditor_pid = pid;
8995 compose_set_ext_editor_sensitive(compose, FALSE);
8998 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
9000 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
9002 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
9006 } else { /* process-monitoring process */
9012 /* close the read side of the pipe */
9015 if (compose_write_body_to_file(compose, tmp) < 0) {
9016 fd_write_all(pipe_fds[1], "2\n", 2);
9020 pid_ed = compose_exec_ext_editor_real(tmp);
9022 fd_write_all(pipe_fds[1], "1\n", 2);
9026 /* wait until editor is terminated */
9027 waitpid(pid_ed, NULL, 0);
9029 fd_write_all(pipe_fds[1], "0\n", 2);
9036 #endif /* G_OS_UNIX */
9040 static gint compose_exec_ext_editor_real(const gchar *file)
9047 cm_return_val_if_fail(file != NULL, -1);
9049 if ((pid = fork()) < 0) {
9054 if (pid != 0) return pid;
9056 /* grandchild process */
9058 if (setpgid(0, getppid()))
9061 if (prefs_common_get_ext_editor_cmd() &&
9062 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
9063 *(p + 1) == 's' && !strchr(p + 2, '%')) {
9064 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
9066 if (prefs_common_get_ext_editor_cmd())
9067 g_warning("External editor command-line is invalid: '%s'\n",
9068 prefs_common_get_ext_editor_cmd());
9069 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
9072 cmdline = strsplit_with_quote(buf, " ", 1024);
9073 execvp(cmdline[0], cmdline);
9076 g_strfreev(cmdline);
9081 static gboolean compose_ext_editor_kill(Compose *compose)
9083 pid_t pgid = compose->exteditor_pid * -1;
9086 ret = kill(pgid, 0);
9088 if (ret == 0 || (ret == -1 && EPERM == errno)) {
9092 msg = g_strdup_printf
9093 (_("The external editor is still working.\n"
9094 "Force terminating the process?\n"
9095 "process group id: %d"), -pgid);
9096 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
9097 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
9101 if (val == G_ALERTALTERNATE) {
9102 g_source_remove(compose->exteditor_tag);
9103 g_io_channel_shutdown(compose->exteditor_ch,
9105 g_io_channel_unref(compose->exteditor_ch);
9107 if (kill(pgid, SIGTERM) < 0) perror("kill");
9108 waitpid(compose->exteditor_pid, NULL, 0);
9110 g_warning("Terminated process group id: %d", -pgid);
9111 g_warning("Temporary file: %s",
9112 compose->exteditor_file);
9114 compose_set_ext_editor_sensitive(compose, TRUE);
9116 g_free(compose->exteditor_file);
9117 compose->exteditor_file = NULL;
9118 compose->exteditor_pid = -1;
9119 compose->exteditor_ch = NULL;
9120 compose->exteditor_tag = -1;
9128 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
9132 Compose *compose = (Compose *)data;
9135 debug_print("Compose: input from monitoring process\n");
9137 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
9139 g_io_channel_shutdown(source, FALSE, NULL);
9140 g_io_channel_unref(source);
9142 waitpid(compose->exteditor_pid, NULL, 0);
9144 if (buf[0] == '0') { /* success */
9145 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9146 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9148 gtk_text_buffer_set_text(buffer, "", -1);
9149 compose_insert_file(compose, compose->exteditor_file);
9150 compose_changed_cb(NULL, compose);
9151 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9153 if (claws_unlink(compose->exteditor_file) < 0)
9154 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9155 } else if (buf[0] == '1') { /* failed */
9156 g_warning("Couldn't exec external editor\n");
9157 if (claws_unlink(compose->exteditor_file) < 0)
9158 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9159 } else if (buf[0] == '2') {
9160 g_warning("Couldn't write to file\n");
9161 } else if (buf[0] == '3') {
9162 g_warning("Pipe read failed\n");
9165 compose_set_ext_editor_sensitive(compose, TRUE);
9167 g_free(compose->exteditor_file);
9168 compose->exteditor_file = NULL;
9169 compose->exteditor_pid = -1;
9170 compose->exteditor_ch = NULL;
9171 compose->exteditor_tag = -1;
9176 static void compose_set_ext_editor_sensitive(Compose *compose,
9179 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9180 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9181 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9182 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9183 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9184 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9185 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9187 gtk_widget_set_sensitive(compose->text, sensitive);
9188 if (compose->toolbar->send_btn)
9189 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
9190 if (compose->toolbar->sendl_btn)
9191 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
9192 if (compose->toolbar->draft_btn)
9193 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
9194 if (compose->toolbar->insert_btn)
9195 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
9196 if (compose->toolbar->sig_btn)
9197 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
9198 if (compose->toolbar->exteditor_btn)
9199 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9200 if (compose->toolbar->linewrap_current_btn)
9201 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9202 if (compose->toolbar->linewrap_all_btn)
9203 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9205 #endif /* G_OS_UNIX */
9208 * compose_undo_state_changed:
9210 * Change the sensivity of the menuentries undo and redo
9212 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9213 gint redo_state, gpointer data)
9215 Compose *compose = (Compose *)data;
9217 switch (undo_state) {
9218 case UNDO_STATE_TRUE:
9219 if (!undostruct->undo_state) {
9220 undostruct->undo_state = TRUE;
9221 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9224 case UNDO_STATE_FALSE:
9225 if (undostruct->undo_state) {
9226 undostruct->undo_state = FALSE;
9227 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9230 case UNDO_STATE_UNCHANGED:
9232 case UNDO_STATE_REFRESH:
9233 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9236 g_warning("Undo state not recognized");
9240 switch (redo_state) {
9241 case UNDO_STATE_TRUE:
9242 if (!undostruct->redo_state) {
9243 undostruct->redo_state = TRUE;
9244 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9247 case UNDO_STATE_FALSE:
9248 if (undostruct->redo_state) {
9249 undostruct->redo_state = FALSE;
9250 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9253 case UNDO_STATE_UNCHANGED:
9255 case UNDO_STATE_REFRESH:
9256 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9259 g_warning("Redo state not recognized");
9264 /* callback functions */
9266 static void compose_notebook_size_alloc(GtkNotebook *notebook,
9267 GtkAllocation *allocation,
9270 prefs_common.compose_notebook_height = allocation->height;
9273 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9274 * includes "non-client" (windows-izm) in calculation, so this calculation
9275 * may not be accurate.
9277 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9278 GtkAllocation *allocation,
9279 GtkSHRuler *shruler)
9281 if (prefs_common.show_ruler) {
9282 gint char_width = 0, char_height = 0;
9283 gint line_width_in_chars;
9285 gtkut_get_font_size(GTK_WIDGET(widget),
9286 &char_width, &char_height);
9287 line_width_in_chars =
9288 (allocation->width - allocation->x) / char_width;
9290 /* got the maximum */
9291 gtk_shruler_set_range(GTK_SHRULER(shruler),
9292 0.0, line_width_in_chars, 0);
9301 ComposePrefType type;
9302 gboolean entry_marked;
9305 static void account_activated(GtkComboBox *optmenu, gpointer data)
9307 Compose *compose = (Compose *)data;
9310 gchar *folderidentifier;
9311 gint account_id = 0;
9314 GSList *list, *saved_list = NULL;
9315 HeaderEntryState *state;
9316 GtkRcStyle *style = NULL;
9317 static GdkColor yellow;
9318 static gboolean color_set = FALSE;
9320 /* Get ID of active account in the combo box */
9321 menu = gtk_combo_box_get_model(optmenu);
9322 gtk_combo_box_get_active_iter(optmenu, &iter);
9323 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9325 ac = account_find_from_id(account_id);
9326 cm_return_if_fail(ac != NULL);
9328 if (ac != compose->account) {
9329 compose_select_account(compose, ac, FALSE);
9331 for (list = compose->header_list; list; list = list->next) {
9332 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9334 if (hentry->type == PREF_ACCOUNT || !list->next) {
9335 compose_destroy_headerentry(compose, hentry);
9339 state = g_malloc0(sizeof(HeaderEntryState));
9340 state->header = gtk_editable_get_chars(GTK_EDITABLE(
9341 gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9342 state->entry = gtk_editable_get_chars(
9343 GTK_EDITABLE(hentry->entry), 0, -1);
9344 state->type = hentry->type;
9347 gdk_color_parse("#f5f6be", &yellow);
9348 color_set = gdk_colormap_alloc_color(
9349 gdk_colormap_get_system(),
9350 &yellow, FALSE, TRUE);
9353 style = gtk_widget_get_modifier_style(hentry->entry);
9354 state->entry_marked = gdk_color_equal(&yellow,
9355 &style->base[GTK_STATE_NORMAL]);
9357 saved_list = g_slist_append(saved_list, state);
9358 compose_destroy_headerentry(compose, hentry);
9361 compose->header_last = NULL;
9362 g_slist_free(compose->header_list);
9363 compose->header_list = NULL;
9364 compose->header_nextrow = 1;
9365 compose_create_header_entry(compose);
9367 if (ac->set_autocc && ac->auto_cc)
9368 compose_entry_append(compose, ac->auto_cc,
9369 COMPOSE_CC, PREF_ACCOUNT);
9371 if (ac->set_autobcc && ac->auto_bcc)
9372 compose_entry_append(compose, ac->auto_bcc,
9373 COMPOSE_BCC, PREF_ACCOUNT);
9375 if (ac->set_autoreplyto && ac->auto_replyto)
9376 compose_entry_append(compose, ac->auto_replyto,
9377 COMPOSE_REPLYTO, PREF_ACCOUNT);
9379 for (list = saved_list; list; list = list->next) {
9380 state = (HeaderEntryState *) list->data;
9382 compose_add_header_entry(compose, state->header,
9383 state->entry, state->type);
9384 if (state->entry_marked)
9385 compose_entry_mark_default_to(compose, state->entry);
9387 g_free(state->header);
9388 g_free(state->entry);
9391 g_slist_free(saved_list);
9393 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9394 (ac->protocol == A_NNTP) ?
9395 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9398 /* Set message save folder */
9399 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9400 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9402 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9403 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9405 compose_set_save_to(compose, NULL);
9406 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9407 folderidentifier = folder_item_get_identifier(account_get_special_folder
9408 (compose->account, F_OUTBOX));
9409 compose_set_save_to(compose, folderidentifier);
9410 g_free(folderidentifier);
9414 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9415 GtkTreeViewColumn *column, Compose *compose)
9417 compose_attach_property(NULL, compose);
9420 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9423 Compose *compose = (Compose *)data;
9424 GtkTreeSelection *attach_selection;
9425 gint attach_nr_selected;
9427 if (!event) return FALSE;
9429 if (event->button == 3) {
9430 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9431 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9433 if (attach_nr_selected > 0)
9435 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9436 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9438 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9439 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9442 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9443 NULL, NULL, event->button, event->time);
9450 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9453 Compose *compose = (Compose *)data;
9455 if (!event) return FALSE;
9457 switch (event->keyval) {
9458 case GDK_KEY_Delete:
9459 compose_attach_remove_selected(NULL, compose);
9465 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9467 toolbar_comp_set_sensitive(compose, allow);
9468 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9469 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9471 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9473 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9474 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9475 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9477 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9481 static void compose_send_cb(GtkAction *action, gpointer data)
9483 Compose *compose = (Compose *)data;
9485 if (prefs_common.work_offline &&
9486 !inc_offline_should_override(TRUE,
9487 _("Claws Mail needs network access in order "
9488 "to send this email.")))
9491 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9492 g_source_remove(compose->draft_timeout_tag);
9493 compose->draft_timeout_tag = -1;
9496 compose_send(compose);
9499 static void compose_send_later_cb(GtkAction *action, gpointer data)
9501 Compose *compose = (Compose *)data;
9505 compose_allow_user_actions(compose, FALSE);
9506 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9507 compose_allow_user_actions(compose, TRUE);
9511 compose_close(compose);
9512 } else if (val == -1) {
9513 alertpanel_error(_("Could not queue message."));
9514 } else if (val == -2) {
9515 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9516 } else if (val == -3) {
9517 if (privacy_peek_error())
9518 alertpanel_error(_("Could not queue message for sending:\n\n"
9519 "Signature failed: %s"), privacy_get_error());
9520 } else if (val == -4) {
9521 alertpanel_error(_("Could not queue message for sending:\n\n"
9522 "Charset conversion failed."));
9523 } else if (val == -5) {
9524 alertpanel_error(_("Could not queue message for sending:\n\n"
9525 "Couldn't get recipient encryption key."));
9526 } else if (val == -6) {
9529 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9532 #define DRAFTED_AT_EXIT "drafted_at_exit"
9533 static void compose_register_draft(MsgInfo *info)
9535 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9536 DRAFTED_AT_EXIT, NULL);
9537 FILE *fp = g_fopen(filepath, "ab");
9540 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9548 gboolean compose_draft (gpointer data, guint action)
9550 Compose *compose = (Compose *)data;
9555 MsgFlags flag = {0, 0};
9556 static gboolean lock = FALSE;
9557 MsgInfo *newmsginfo;
9559 gboolean target_locked = FALSE;
9560 gboolean err = FALSE;
9562 if (lock) return FALSE;
9564 if (compose->sending)
9567 draft = account_get_special_folder(compose->account, F_DRAFT);
9568 cm_return_val_if_fail(draft != NULL, FALSE);
9570 if (!g_mutex_trylock(compose->mutex)) {
9571 /* we don't want to lock the mutex once it's available,
9572 * because as the only other part of compose.c locking
9573 * it is compose_close - which means once unlocked,
9574 * the compose struct will be freed */
9575 debug_print("couldn't lock mutex, probably sending\n");
9581 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9582 G_DIR_SEPARATOR, compose);
9583 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9584 FILE_OP_ERROR(tmp, "fopen");
9588 /* chmod for security */
9589 if (change_file_mode_rw(fp, tmp) < 0) {
9590 FILE_OP_ERROR(tmp, "chmod");
9591 g_warning("can't change file mode\n");
9594 /* Save draft infos */
9595 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9596 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9598 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9599 gchar *savefolderid;
9601 savefolderid = compose_get_save_to(compose);
9602 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9603 g_free(savefolderid);
9605 if (compose->return_receipt) {
9606 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9608 if (compose->privacy_system) {
9609 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9610 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9611 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9614 /* Message-ID of message replying to */
9615 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9618 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9619 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9622 /* Message-ID of message forwarding to */
9623 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9626 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9627 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9631 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9632 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9634 sheaders = compose_get_manual_headers_info(compose);
9635 err |= (fprintf(fp, "X-Claws-Manual-Headers:%s\n", sheaders) < 0);
9638 /* end of headers */
9639 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9646 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9650 if (fclose(fp) == EOF) {
9654 if (compose->targetinfo) {
9655 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9656 flag.perm_flags = target_locked?MSG_LOCKED:0;
9658 flag.tmp_flags = MSG_DRAFT;
9660 folder_item_scan(draft);
9661 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9662 MsgInfo *tmpinfo = NULL;
9663 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9664 if (compose->msgid) {
9665 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9668 msgnum = tmpinfo->msgnum;
9669 procmsg_msginfo_free(tmpinfo);
9670 debug_print("got draft msgnum %d from scanning\n", msgnum);
9672 debug_print("didn't get draft msgnum after scanning\n");
9675 debug_print("got draft msgnum %d from adding\n", msgnum);
9681 if (action != COMPOSE_AUTO_SAVE) {
9682 if (action != COMPOSE_DRAFT_FOR_EXIT)
9683 alertpanel_error(_("Could not save draft."));
9686 gtkut_window_popup(compose->window);
9687 val = alertpanel_full(_("Could not save draft"),
9688 _("Could not save draft.\n"
9689 "Do you want to cancel exit or discard this email?"),
9690 _("_Cancel exit"), _("_Discard email"), NULL,
9691 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9692 if (val == G_ALERTALTERNATE) {
9694 g_mutex_unlock(compose->mutex); /* must be done before closing */
9695 compose_close(compose);
9699 g_mutex_unlock(compose->mutex); /* must be done before closing */
9708 if (compose->mode == COMPOSE_REEDIT) {
9709 compose_remove_reedit_target(compose, TRUE);
9712 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9715 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9717 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9719 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9720 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9721 procmsg_msginfo_set_flags(newmsginfo, 0,
9722 MSG_HAS_ATTACHMENT);
9724 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9725 compose_register_draft(newmsginfo);
9727 procmsg_msginfo_free(newmsginfo);
9730 folder_item_scan(draft);
9732 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9734 g_mutex_unlock(compose->mutex); /* must be done before closing */
9735 compose_close(compose);
9741 path = folder_item_fetch_msg(draft, msgnum);
9743 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9746 if (g_stat(path, &s) < 0) {
9747 FILE_OP_ERROR(path, "stat");
9753 procmsg_msginfo_free(compose->targetinfo);
9754 compose->targetinfo = procmsg_msginfo_new();
9755 compose->targetinfo->msgnum = msgnum;
9756 compose->targetinfo->size = (goffset)s.st_size;
9757 compose->targetinfo->mtime = s.st_mtime;
9758 compose->targetinfo->folder = draft;
9760 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9761 compose->mode = COMPOSE_REEDIT;
9763 if (action == COMPOSE_AUTO_SAVE) {
9764 compose->autosaved_draft = compose->targetinfo;
9766 compose->modified = FALSE;
9767 compose_set_title(compose);
9771 g_mutex_unlock(compose->mutex);
9775 void compose_clear_exit_drafts(void)
9777 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9778 DRAFTED_AT_EXIT, NULL);
9779 if (is_file_exist(filepath))
9780 claws_unlink(filepath);
9785 void compose_reopen_exit_drafts(void)
9787 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9788 DRAFTED_AT_EXIT, NULL);
9789 FILE *fp = g_fopen(filepath, "rb");
9793 while (fgets(buf, sizeof(buf), fp)) {
9794 gchar **parts = g_strsplit(buf, "\t", 2);
9795 const gchar *folder = parts[0];
9796 int msgnum = parts[1] ? atoi(parts[1]):-1;
9798 if (folder && *folder && msgnum > -1) {
9799 FolderItem *item = folder_find_item_from_identifier(folder);
9800 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9802 compose_reedit(info, FALSE);
9809 compose_clear_exit_drafts();
9812 static void compose_save_cb(GtkAction *action, gpointer data)
9814 Compose *compose = (Compose *)data;
9815 compose_draft(compose, COMPOSE_KEEP_EDITING);
9816 compose->rmode = COMPOSE_REEDIT;
9819 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9821 if (compose && file_list) {
9824 for ( tmp = file_list; tmp; tmp = tmp->next) {
9825 gchar *file = (gchar *) tmp->data;
9826 gchar *utf8_filename = conv_filename_to_utf8(file);
9827 compose_attach_append(compose, file, utf8_filename, NULL, NULL);
9828 compose_changed_cb(NULL, compose);
9833 g_free(utf8_filename);
9838 static void compose_attach_cb(GtkAction *action, gpointer data)
9840 Compose *compose = (Compose *)data;
9843 if (compose->redirect_filename != NULL)
9846 file_list = filesel_select_multiple_files_open(_("Select file"));
9849 compose_attach_from_list(compose, file_list, TRUE);
9850 g_list_free(file_list);
9854 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9856 Compose *compose = (Compose *)data;
9858 gint files_inserted = 0;
9860 file_list = filesel_select_multiple_files_open(_("Select file"));
9865 for ( tmp = file_list; tmp; tmp = tmp->next) {
9866 gchar *file = (gchar *) tmp->data;
9867 gchar *filedup = g_strdup(file);
9868 gchar *shortfile = g_path_get_basename(filedup);
9869 ComposeInsertResult res;
9870 /* insert the file if the file is short or if the user confirmed that
9871 he/she wants to insert the large file */
9872 res = compose_insert_file(compose, file);
9873 if (res == COMPOSE_INSERT_READ_ERROR) {
9874 alertpanel_error(_("File '%s' could not be read."), shortfile);
9875 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9876 alertpanel_error(_("File '%s' contained invalid characters\n"
9877 "for the current encoding, insertion may be incorrect."),
9879 } else if (res == COMPOSE_INSERT_SUCCESS)
9886 g_list_free(file_list);
9890 if (files_inserted > 0 && compose->gtkaspell &&
9891 compose->gtkaspell->check_while_typing)
9892 gtkaspell_highlight_all(compose->gtkaspell);
9896 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9898 Compose *compose = (Compose *)data;
9900 compose_insert_sig(compose, FALSE);
9903 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9907 Compose *compose = (Compose *)data;
9909 gtkut_widget_get_uposition(widget, &x, &y);
9910 if (!compose->batch) {
9911 prefs_common.compose_x = x;
9912 prefs_common.compose_y = y;
9914 if (compose->sending || compose->updating)
9916 compose_close_cb(NULL, compose);
9920 void compose_close_toolbar(Compose *compose)
9922 compose_close_cb(NULL, compose);
9925 static void compose_close_cb(GtkAction *action, gpointer data)
9927 Compose *compose = (Compose *)data;
9931 if (compose->exteditor_tag != -1) {
9932 if (!compose_ext_editor_kill(compose))
9937 if (compose->modified) {
9938 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9939 if (!g_mutex_trylock(compose->mutex)) {
9940 /* we don't want to lock the mutex once it's available,
9941 * because as the only other part of compose.c locking
9942 * it is compose_close - which means once unlocked,
9943 * the compose struct will be freed */
9944 debug_print("couldn't lock mutex, probably sending\n");
9948 val = alertpanel(_("Discard message"),
9949 _("This message has been modified. Discard it?"),
9950 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9952 val = alertpanel(_("Save changes"),
9953 _("This message has been modified. Save the latest changes?"),
9954 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9956 g_mutex_unlock(compose->mutex);
9958 case G_ALERTDEFAULT:
9959 if (prefs_common.autosave && !reedit)
9960 compose_remove_draft(compose);
9962 case G_ALERTALTERNATE:
9963 compose_draft(data, COMPOSE_QUIT_EDITING);
9970 compose_close(compose);
9973 static void compose_print_cb(GtkAction *action, gpointer data)
9975 Compose *compose = (Compose *) data;
9977 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9978 if (compose->targetinfo)
9979 messageview_print(compose->targetinfo, FALSE, -1, -1, 0);
9982 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9984 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9985 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9986 Compose *compose = (Compose *) data;
9989 compose->out_encoding = (CharSet)value;
9992 static void compose_address_cb(GtkAction *action, gpointer data)
9994 Compose *compose = (Compose *)data;
9996 #ifndef USE_NEW_ADDRBOOK
9997 addressbook_open(compose);
9999 GError* error = NULL;
10000 addressbook_connect_signals(compose);
10001 addressbook_dbus_open(TRUE, &error);
10003 g_warning("%s", error->message);
10004 g_error_free(error);
10009 static void about_show_cb(GtkAction *action, gpointer data)
10014 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
10016 Compose *compose = (Compose *)data;
10021 tmpl = g_object_get_data(G_OBJECT(widget), "template");
10022 cm_return_if_fail(tmpl != NULL);
10024 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
10026 val = alertpanel(_("Apply template"), msg,
10027 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
10030 if (val == G_ALERTDEFAULT)
10031 compose_template_apply(compose, tmpl, TRUE);
10032 else if (val == G_ALERTALTERNATE)
10033 compose_template_apply(compose, tmpl, FALSE);
10036 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
10038 Compose *compose = (Compose *)data;
10040 compose_exec_ext_editor(compose);
10043 static void compose_undo_cb(GtkAction *action, gpointer data)
10045 Compose *compose = (Compose *)data;
10046 gboolean prev_autowrap = compose->autowrap;
10048 compose->autowrap = FALSE;
10049 undo_undo(compose->undostruct);
10050 compose->autowrap = prev_autowrap;
10053 static void compose_redo_cb(GtkAction *action, gpointer data)
10055 Compose *compose = (Compose *)data;
10056 gboolean prev_autowrap = compose->autowrap;
10058 compose->autowrap = FALSE;
10059 undo_redo(compose->undostruct);
10060 compose->autowrap = prev_autowrap;
10063 static void entry_cut_clipboard(GtkWidget *entry)
10065 if (GTK_IS_EDITABLE(entry))
10066 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
10067 else if (GTK_IS_TEXT_VIEW(entry))
10068 gtk_text_buffer_cut_clipboard(
10069 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10070 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
10074 static void entry_copy_clipboard(GtkWidget *entry)
10076 if (GTK_IS_EDITABLE(entry))
10077 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
10078 else if (GTK_IS_TEXT_VIEW(entry))
10079 gtk_text_buffer_copy_clipboard(
10080 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10081 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
10084 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
10085 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
10087 if (GTK_IS_TEXT_VIEW(entry)) {
10088 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10089 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
10090 GtkTextIter start_iter, end_iter;
10092 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
10094 if (contents == NULL)
10097 /* we shouldn't delete the selection when middle-click-pasting, or we
10098 * can't mid-click-paste our own selection */
10099 if (clip != GDK_SELECTION_PRIMARY) {
10100 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
10101 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
10104 if (insert_place == NULL) {
10105 /* if insert_place isn't specified, insert at the cursor.
10106 * used for Ctrl-V pasting */
10107 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10108 start = gtk_text_iter_get_offset(&start_iter);
10109 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
10111 /* if insert_place is specified, paste here.
10112 * used for mid-click-pasting */
10113 start = gtk_text_iter_get_offset(insert_place);
10114 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
10115 if (prefs_common.primary_paste_unselects)
10116 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
10120 /* paste unwrapped: mark the paste so it's not wrapped later */
10121 end = start + strlen(contents);
10122 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
10123 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
10124 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
10125 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
10126 /* rewrap paragraph now (after a mid-click-paste) */
10127 mark_start = gtk_text_buffer_get_insert(buffer);
10128 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10129 gtk_text_iter_backward_char(&start_iter);
10130 compose_beautify_paragraph(compose, &start_iter, TRUE);
10132 } else if (GTK_IS_EDITABLE(entry))
10133 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
10135 compose->modified = TRUE;
10138 static void entry_allsel(GtkWidget *entry)
10140 if (GTK_IS_EDITABLE(entry))
10141 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
10142 else if (GTK_IS_TEXT_VIEW(entry)) {
10143 GtkTextIter startiter, enditer;
10144 GtkTextBuffer *textbuf;
10146 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10147 gtk_text_buffer_get_start_iter(textbuf, &startiter);
10148 gtk_text_buffer_get_end_iter(textbuf, &enditer);
10150 gtk_text_buffer_move_mark_by_name(textbuf,
10151 "selection_bound", &startiter);
10152 gtk_text_buffer_move_mark_by_name(textbuf,
10153 "insert", &enditer);
10157 static void compose_cut_cb(GtkAction *action, gpointer data)
10159 Compose *compose = (Compose *)data;
10160 if (compose->focused_editable
10161 #ifndef GENERIC_UMPC
10162 && gtk_widget_has_focus(compose->focused_editable)
10165 entry_cut_clipboard(compose->focused_editable);
10168 static void compose_copy_cb(GtkAction *action, gpointer data)
10170 Compose *compose = (Compose *)data;
10171 if (compose->focused_editable
10172 #ifndef GENERIC_UMPC
10173 && gtk_widget_has_focus(compose->focused_editable)
10176 entry_copy_clipboard(compose->focused_editable);
10179 static void compose_paste_cb(GtkAction *action, gpointer data)
10181 Compose *compose = (Compose *)data;
10182 gint prev_autowrap;
10183 GtkTextBuffer *buffer;
10185 if (compose->focused_editable &&
10186 #ifndef GENERIC_UMPC
10187 gtk_widget_has_focus(compose->focused_editable)
10190 entry_paste_clipboard(compose, compose->focused_editable,
10191 prefs_common.linewrap_pastes,
10192 GDK_SELECTION_CLIPBOARD, NULL);
10197 #ifndef GENERIC_UMPC
10198 gtk_widget_has_focus(compose->text) &&
10200 compose->gtkaspell &&
10201 compose->gtkaspell->check_while_typing)
10202 gtkaspell_highlight_all(compose->gtkaspell);
10206 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10208 Compose *compose = (Compose *)data;
10209 gint wrap_quote = prefs_common.linewrap_quote;
10210 if (compose->focused_editable
10211 #ifndef GENERIC_UMPC
10212 && gtk_widget_has_focus(compose->focused_editable)
10215 /* let text_insert() (called directly or at a later time
10216 * after the gtk_editable_paste_clipboard) know that
10217 * text is to be inserted as a quotation. implemented
10218 * by using a simple refcount... */
10219 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10220 G_OBJECT(compose->focused_editable),
10221 "paste_as_quotation"));
10222 g_object_set_data(G_OBJECT(compose->focused_editable),
10223 "paste_as_quotation",
10224 GINT_TO_POINTER(paste_as_quotation + 1));
10225 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10226 entry_paste_clipboard(compose, compose->focused_editable,
10227 prefs_common.linewrap_pastes,
10228 GDK_SELECTION_CLIPBOARD, NULL);
10229 prefs_common.linewrap_quote = wrap_quote;
10233 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10235 Compose *compose = (Compose *)data;
10236 gint prev_autowrap;
10237 GtkTextBuffer *buffer;
10239 if (compose->focused_editable
10240 #ifndef GENERIC_UMPC
10241 && gtk_widget_has_focus(compose->focused_editable)
10244 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10245 GDK_SELECTION_CLIPBOARD, NULL);
10250 #ifndef GENERIC_UMPC
10251 gtk_widget_has_focus(compose->text) &&
10253 compose->gtkaspell &&
10254 compose->gtkaspell->check_while_typing)
10255 gtkaspell_highlight_all(compose->gtkaspell);
10259 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10261 Compose *compose = (Compose *)data;
10262 gint prev_autowrap;
10263 GtkTextBuffer *buffer;
10265 if (compose->focused_editable
10266 #ifndef GENERIC_UMPC
10267 && gtk_widget_has_focus(compose->focused_editable)
10270 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10271 GDK_SELECTION_CLIPBOARD, NULL);
10276 #ifndef GENERIC_UMPC
10277 gtk_widget_has_focus(compose->text) &&
10279 compose->gtkaspell &&
10280 compose->gtkaspell->check_while_typing)
10281 gtkaspell_highlight_all(compose->gtkaspell);
10285 static void compose_allsel_cb(GtkAction *action, gpointer data)
10287 Compose *compose = (Compose *)data;
10288 if (compose->focused_editable
10289 #ifndef GENERIC_UMPC
10290 && gtk_widget_has_focus(compose->focused_editable)
10293 entry_allsel(compose->focused_editable);
10296 static void textview_move_beginning_of_line (GtkTextView *text)
10298 GtkTextBuffer *buffer;
10302 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10304 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10305 mark = gtk_text_buffer_get_insert(buffer);
10306 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10307 gtk_text_iter_set_line_offset(&ins, 0);
10308 gtk_text_buffer_place_cursor(buffer, &ins);
10311 static void textview_move_forward_character (GtkTextView *text)
10313 GtkTextBuffer *buffer;
10317 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10319 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10320 mark = gtk_text_buffer_get_insert(buffer);
10321 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10322 if (gtk_text_iter_forward_cursor_position(&ins))
10323 gtk_text_buffer_place_cursor(buffer, &ins);
10326 static void textview_move_backward_character (GtkTextView *text)
10328 GtkTextBuffer *buffer;
10332 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10334 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10335 mark = gtk_text_buffer_get_insert(buffer);
10336 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10337 if (gtk_text_iter_backward_cursor_position(&ins))
10338 gtk_text_buffer_place_cursor(buffer, &ins);
10341 static void textview_move_forward_word (GtkTextView *text)
10343 GtkTextBuffer *buffer;
10348 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10350 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10351 mark = gtk_text_buffer_get_insert(buffer);
10352 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10353 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10354 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10355 gtk_text_iter_backward_word_start(&ins);
10356 gtk_text_buffer_place_cursor(buffer, &ins);
10360 static void textview_move_backward_word (GtkTextView *text)
10362 GtkTextBuffer *buffer;
10367 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10369 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10370 mark = gtk_text_buffer_get_insert(buffer);
10371 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10372 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10373 if (gtk_text_iter_backward_word_starts(&ins, 1))
10374 gtk_text_buffer_place_cursor(buffer, &ins);
10377 static void textview_move_end_of_line (GtkTextView *text)
10379 GtkTextBuffer *buffer;
10383 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10385 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10386 mark = gtk_text_buffer_get_insert(buffer);
10387 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10388 if (gtk_text_iter_forward_to_line_end(&ins))
10389 gtk_text_buffer_place_cursor(buffer, &ins);
10392 static void textview_move_next_line (GtkTextView *text)
10394 GtkTextBuffer *buffer;
10399 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10401 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10402 mark = gtk_text_buffer_get_insert(buffer);
10403 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10404 offset = gtk_text_iter_get_line_offset(&ins);
10405 if (gtk_text_iter_forward_line(&ins)) {
10406 gtk_text_iter_set_line_offset(&ins, offset);
10407 gtk_text_buffer_place_cursor(buffer, &ins);
10411 static void textview_move_previous_line (GtkTextView *text)
10413 GtkTextBuffer *buffer;
10418 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10420 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10421 mark = gtk_text_buffer_get_insert(buffer);
10422 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10423 offset = gtk_text_iter_get_line_offset(&ins);
10424 if (gtk_text_iter_backward_line(&ins)) {
10425 gtk_text_iter_set_line_offset(&ins, offset);
10426 gtk_text_buffer_place_cursor(buffer, &ins);
10430 static void textview_delete_forward_character (GtkTextView *text)
10432 GtkTextBuffer *buffer;
10434 GtkTextIter ins, end_iter;
10436 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10438 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10439 mark = gtk_text_buffer_get_insert(buffer);
10440 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10442 if (gtk_text_iter_forward_char(&end_iter)) {
10443 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10447 static void textview_delete_backward_character (GtkTextView *text)
10449 GtkTextBuffer *buffer;
10451 GtkTextIter ins, end_iter;
10453 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10455 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10456 mark = gtk_text_buffer_get_insert(buffer);
10457 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10459 if (gtk_text_iter_backward_char(&end_iter)) {
10460 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10464 static void textview_delete_forward_word (GtkTextView *text)
10466 GtkTextBuffer *buffer;
10468 GtkTextIter ins, end_iter;
10470 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10472 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10473 mark = gtk_text_buffer_get_insert(buffer);
10474 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10476 if (gtk_text_iter_forward_word_end(&end_iter)) {
10477 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10481 static void textview_delete_backward_word (GtkTextView *text)
10483 GtkTextBuffer *buffer;
10485 GtkTextIter ins, end_iter;
10487 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10489 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10490 mark = gtk_text_buffer_get_insert(buffer);
10491 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10493 if (gtk_text_iter_backward_word_start(&end_iter)) {
10494 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10498 static void textview_delete_line (GtkTextView *text)
10500 GtkTextBuffer *buffer;
10502 GtkTextIter ins, start_iter, end_iter;
10504 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10506 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10507 mark = gtk_text_buffer_get_insert(buffer);
10508 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10511 gtk_text_iter_set_line_offset(&start_iter, 0);
10514 if (gtk_text_iter_ends_line(&end_iter)){
10515 if (!gtk_text_iter_forward_char(&end_iter))
10516 gtk_text_iter_backward_char(&start_iter);
10519 gtk_text_iter_forward_to_line_end(&end_iter);
10520 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10523 static void textview_delete_to_line_end (GtkTextView *text)
10525 GtkTextBuffer *buffer;
10527 GtkTextIter ins, end_iter;
10529 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10531 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10532 mark = gtk_text_buffer_get_insert(buffer);
10533 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10535 if (gtk_text_iter_ends_line(&end_iter))
10536 gtk_text_iter_forward_char(&end_iter);
10538 gtk_text_iter_forward_to_line_end(&end_iter);
10539 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10542 #define DO_ACTION(name, act) { \
10543 if(!strcmp(name, a_name)) { \
10547 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10549 const gchar *a_name = gtk_action_get_name(action);
10550 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10551 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10552 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10553 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10554 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10555 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10556 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10557 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10558 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10559 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10560 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10561 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10562 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10563 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10567 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10569 Compose *compose = (Compose *)data;
10570 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10571 ComposeCallAdvancedAction action = -1;
10573 action = compose_call_advanced_action_from_path(gaction);
10576 void (*do_action) (GtkTextView *text);
10577 } action_table[] = {
10578 {textview_move_beginning_of_line},
10579 {textview_move_forward_character},
10580 {textview_move_backward_character},
10581 {textview_move_forward_word},
10582 {textview_move_backward_word},
10583 {textview_move_end_of_line},
10584 {textview_move_next_line},
10585 {textview_move_previous_line},
10586 {textview_delete_forward_character},
10587 {textview_delete_backward_character},
10588 {textview_delete_forward_word},
10589 {textview_delete_backward_word},
10590 {textview_delete_line},
10591 {textview_delete_to_line_end}
10594 if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
10596 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10597 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10598 if (action_table[action].do_action)
10599 action_table[action].do_action(text);
10601 g_warning("Not implemented yet.");
10605 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10607 GtkAllocation allocation;
10611 if (GTK_IS_EDITABLE(widget)) {
10612 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10613 gtk_editable_set_position(GTK_EDITABLE(widget),
10616 if ((parent = gtk_widget_get_parent(widget))
10617 && (parent = gtk_widget_get_parent(parent))
10618 && (parent = gtk_widget_get_parent(parent))) {
10619 if (GTK_IS_SCROLLED_WINDOW(parent)) {
10620 gtk_widget_get_allocation(widget, &allocation);
10621 gint y = allocation.y;
10622 gint height = allocation.height;
10623 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10624 (GTK_SCROLLED_WINDOW(parent));
10626 gfloat value = gtk_adjustment_get_value(shown);
10627 gfloat upper = gtk_adjustment_get_upper(shown);
10628 gfloat page_size = gtk_adjustment_get_page_size(shown);
10629 if (y < (int)value) {
10630 gtk_adjustment_set_value(shown, y - 1);
10632 if ((y + height) > ((int)value + (int)page_size)) {
10633 if ((y - height - 1) < ((int)upper - (int)page_size)) {
10634 gtk_adjustment_set_value(shown,
10635 y + height - (int)page_size - 1);
10637 gtk_adjustment_set_value(shown,
10638 (int)upper - (int)page_size - 1);
10645 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10646 compose->focused_editable = widget;
10648 #ifdef GENERIC_UMPC
10649 if (GTK_IS_TEXT_VIEW(widget)
10650 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10651 g_object_ref(compose->notebook);
10652 g_object_ref(compose->edit_vbox);
10653 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10654 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10655 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10656 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10657 g_object_unref(compose->notebook);
10658 g_object_unref(compose->edit_vbox);
10659 g_signal_handlers_block_by_func(G_OBJECT(widget),
10660 G_CALLBACK(compose_grab_focus_cb),
10662 gtk_widget_grab_focus(widget);
10663 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10664 G_CALLBACK(compose_grab_focus_cb),
10666 } else if (!GTK_IS_TEXT_VIEW(widget)
10667 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10668 g_object_ref(compose->notebook);
10669 g_object_ref(compose->edit_vbox);
10670 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10671 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10672 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10673 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10674 g_object_unref(compose->notebook);
10675 g_object_unref(compose->edit_vbox);
10676 g_signal_handlers_block_by_func(G_OBJECT(widget),
10677 G_CALLBACK(compose_grab_focus_cb),
10679 gtk_widget_grab_focus(widget);
10680 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10681 G_CALLBACK(compose_grab_focus_cb),
10687 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10689 compose->modified = TRUE;
10690 // compose_beautify_paragraph(compose, NULL, TRUE);
10691 #ifndef GENERIC_UMPC
10692 compose_set_title(compose);
10696 static void compose_wrap_cb(GtkAction *action, gpointer data)
10698 Compose *compose = (Compose *)data;
10699 compose_beautify_paragraph(compose, NULL, TRUE);
10702 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10704 Compose *compose = (Compose *)data;
10705 compose_wrap_all_full(compose, TRUE);
10708 static void compose_find_cb(GtkAction *action, gpointer data)
10710 Compose *compose = (Compose *)data;
10712 message_search_compose(compose);
10715 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10718 Compose *compose = (Compose *)data;
10719 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10720 if (compose->autowrap)
10721 compose_wrap_all_full(compose, TRUE);
10722 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10725 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10728 Compose *compose = (Compose *)data;
10729 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10732 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10734 Compose *compose = (Compose *)data;
10736 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10739 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10741 Compose *compose = (Compose *)data;
10743 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10746 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10748 g_free(compose->privacy_system);
10750 compose->privacy_system = g_strdup(account->default_privacy_system);
10751 compose_update_privacy_system_menu_item(compose, warn);
10754 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10756 Compose *compose = (Compose *)data;
10758 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10759 gtk_widget_show(compose->ruler_hbox);
10760 prefs_common.show_ruler = TRUE;
10762 gtk_widget_hide(compose->ruler_hbox);
10763 gtk_widget_queue_resize(compose->edit_vbox);
10764 prefs_common.show_ruler = FALSE;
10768 static void compose_attach_drag_received_cb (GtkWidget *widget,
10769 GdkDragContext *context,
10772 GtkSelectionData *data,
10775 gpointer user_data)
10777 Compose *compose = (Compose *)user_data;
10781 type = gtk_selection_data_get_data_type(data);
10782 if (((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
10784 || (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND"))
10786 ) && gtk_drag_get_source_widget(context) !=
10787 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10788 list = uri_list_extract_filenames(
10789 (const gchar *)gtk_selection_data_get_data(data));
10790 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10791 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10792 compose_attach_append
10793 (compose, (const gchar *)tmp->data,
10794 utf8_filename, NULL, NULL);
10795 g_free(utf8_filename);
10797 if (list) compose_changed_cb(NULL, compose);
10798 list_free_strings(list);
10800 } else if (gtk_drag_get_source_widget(context)
10801 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10802 /* comes from our summaryview */
10803 SummaryView * summaryview = NULL;
10804 GSList * list = NULL, *cur = NULL;
10806 if (mainwindow_get_mainwindow())
10807 summaryview = mainwindow_get_mainwindow()->summaryview;
10810 list = summary_get_selected_msg_list(summaryview);
10812 for (cur = list; cur; cur = cur->next) {
10813 MsgInfo *msginfo = (MsgInfo *)cur->data;
10814 gchar *file = NULL;
10816 file = procmsg_get_message_file_full(msginfo,
10819 compose_attach_append(compose, (const gchar *)file,
10820 (const gchar *)file, "message/rfc822", NULL);
10824 g_slist_free(list);
10828 static gboolean compose_drag_drop(GtkWidget *widget,
10829 GdkDragContext *drag_context,
10831 guint time, gpointer user_data)
10833 /* not handling this signal makes compose_insert_drag_received_cb
10838 static void compose_insert_drag_received_cb (GtkWidget *widget,
10839 GdkDragContext *drag_context,
10842 GtkSelectionData *data,
10845 gpointer user_data)
10847 Compose *compose = (Compose *)user_data;
10851 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10853 type = gtk_selection_data_get_data_type(data);
10855 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
10857 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND")) {
10859 AlertValue val = G_ALERTDEFAULT;
10860 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
10862 list = uri_list_extract_filenames(ddata);
10863 if (list == NULL && strstr(ddata, "://")) {
10864 /* Assume a list of no files, and data has ://, is a remote link */
10865 gchar *tmpdata = g_strstrip(g_strdup(ddata));
10866 gchar *tmpfile = get_tmp_file();
10867 str_write_to_file(tmpdata, tmpfile);
10869 compose_insert_file(compose, tmpfile);
10870 claws_unlink(tmpfile);
10872 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10873 compose_beautify_paragraph(compose, NULL, TRUE);
10876 switch (prefs_common.compose_dnd_mode) {
10877 case COMPOSE_DND_ASK:
10878 val = alertpanel_full(_("Insert or attach?"),
10879 _("Do you want to insert the contents of the file(s) "
10880 "into the message body, or attach it to the email?"),
10881 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10882 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10884 case COMPOSE_DND_INSERT:
10885 val = G_ALERTALTERNATE;
10887 case COMPOSE_DND_ATTACH:
10888 val = G_ALERTOTHER;
10891 /* unexpected case */
10892 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10895 if (val & G_ALERTDISABLE) {
10896 val &= ~G_ALERTDISABLE;
10897 /* remember what action to perform by default, only if we don't click Cancel */
10898 if (val == G_ALERTALTERNATE)
10899 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10900 else if (val == G_ALERTOTHER)
10901 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10904 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10905 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10906 list_free_strings(list);
10909 } else if (val == G_ALERTOTHER) {
10910 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10911 list_free_strings(list);
10916 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10917 compose_insert_file(compose, (const gchar *)tmp->data);
10919 list_free_strings(list);
10921 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10926 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10929 static void compose_header_drag_received_cb (GtkWidget *widget,
10930 GdkDragContext *drag_context,
10933 GtkSelectionData *data,
10936 gpointer user_data)
10938 GtkEditable *entry = (GtkEditable *)user_data;
10939 const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
10941 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10944 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10945 gchar *decoded=g_new(gchar, strlen(email));
10948 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
10949 gtk_editable_delete_text(entry, 0, -1);
10950 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10951 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10955 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10958 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10960 Compose *compose = (Compose *)data;
10962 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10963 compose->return_receipt = TRUE;
10965 compose->return_receipt = FALSE;
10968 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10970 Compose *compose = (Compose *)data;
10972 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10973 compose->remove_references = TRUE;
10975 compose->remove_references = FALSE;
10978 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10979 ComposeHeaderEntry *headerentry)
10981 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10985 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10986 GdkEventKey *event,
10987 ComposeHeaderEntry *headerentry)
10989 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10990 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10991 !(event->state & GDK_MODIFIER_MASK) &&
10992 (event->keyval == GDK_KEY_BackSpace) &&
10993 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10994 gtk_container_remove
10995 (GTK_CONTAINER(headerentry->compose->header_table),
10996 headerentry->combo);
10997 gtk_container_remove
10998 (GTK_CONTAINER(headerentry->compose->header_table),
10999 headerentry->entry);
11000 headerentry->compose->header_list =
11001 g_slist_remove(headerentry->compose->header_list,
11003 g_free(headerentry);
11004 } else if (event->keyval == GDK_KEY_Tab) {
11005 if (headerentry->compose->header_last == headerentry) {
11006 /* Override default next focus, and give it to subject_entry
11007 * instead of notebook tabs
11009 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
11010 gtk_widget_grab_focus(headerentry->compose->subject_entry);
11017 static gboolean scroll_postpone(gpointer data)
11019 Compose *compose = (Compose *)data;
11021 cm_return_val_if_fail(!compose->batch, FALSE);
11023 GTK_EVENTS_FLUSH();
11024 compose_show_first_last_header(compose, FALSE);
11028 static void compose_headerentry_changed_cb(GtkWidget *entry,
11029 ComposeHeaderEntry *headerentry)
11031 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
11032 compose_create_header_entry(headerentry->compose);
11033 g_signal_handlers_disconnect_matched
11034 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
11035 0, 0, NULL, NULL, headerentry);
11037 if (!headerentry->compose->batch)
11038 g_timeout_add(0, scroll_postpone, headerentry->compose);
11042 static gboolean compose_defer_auto_save_draft(Compose *compose)
11044 compose->draft_timeout_tag = -1;
11045 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
11049 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
11051 GtkAdjustment *vadj;
11053 cm_return_if_fail(compose);
11054 cm_return_if_fail(!compose->batch);
11055 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
11056 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
11057 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11058 gtk_widget_get_parent(compose->header_table)));
11059 gtk_adjustment_set_value(vadj, (show_first ?
11060 gtk_adjustment_get_lower(vadj) :
11061 (gtk_adjustment_get_upper(vadj) -
11062 gtk_adjustment_get_page_size(vadj))));
11063 gtk_adjustment_changed(vadj);
11066 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
11067 const gchar *text, gint len, Compose *compose)
11069 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
11070 (G_OBJECT(compose->text), "paste_as_quotation"));
11073 cm_return_if_fail(text != NULL);
11075 g_signal_handlers_block_by_func(G_OBJECT(buffer),
11076 G_CALLBACK(text_inserted),
11078 if (paste_as_quotation) {
11080 const gchar *qmark;
11082 GtkTextIter start_iter;
11085 len = strlen(text);
11087 new_text = g_strndup(text, len);
11089 qmark = compose_quote_char_from_context(compose);
11091 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11092 gtk_text_buffer_place_cursor(buffer, iter);
11094 pos = gtk_text_iter_get_offset(iter);
11096 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
11097 _("Quote format error at line %d."));
11098 quote_fmt_reset_vartable();
11100 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
11101 GINT_TO_POINTER(paste_as_quotation - 1));
11103 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11104 gtk_text_buffer_place_cursor(buffer, iter);
11105 gtk_text_buffer_delete_mark(buffer, mark);
11107 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
11108 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
11109 compose_beautify_paragraph(compose, &start_iter, FALSE);
11110 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
11111 gtk_text_buffer_delete_mark(buffer, mark);
11113 if (strcmp(text, "\n") || compose->automatic_break
11114 || gtk_text_iter_starts_line(iter)) {
11115 GtkTextIter before_ins;
11116 gtk_text_buffer_insert(buffer, iter, text, len);
11117 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
11118 before_ins = *iter;
11119 gtk_text_iter_backward_chars(&before_ins, len);
11120 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
11123 /* check if the preceding is just whitespace or quote */
11124 GtkTextIter start_line;
11125 gchar *tmp = NULL, *quote = NULL;
11126 gint quote_len = 0, is_normal = 0;
11127 start_line = *iter;
11128 gtk_text_iter_set_line_offset(&start_line, 0);
11129 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
11132 if (*tmp == '\0') {
11135 quote = compose_get_quote_str(buffer, &start_line, "e_len);
11143 gtk_text_buffer_insert(buffer, iter, text, len);
11145 gtk_text_buffer_insert_with_tags_by_name(buffer,
11146 iter, text, len, "no_join", NULL);
11151 if (!paste_as_quotation) {
11152 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11153 compose_beautify_paragraph(compose, iter, FALSE);
11154 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11155 gtk_text_buffer_delete_mark(buffer, mark);
11158 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
11159 G_CALLBACK(text_inserted),
11161 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
11163 if (prefs_common.autosave &&
11164 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
11165 compose->draft_timeout_tag != -2 /* disabled while loading */)
11166 compose->draft_timeout_tag = g_timeout_add
11167 (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
11171 static void compose_check_all(GtkAction *action, gpointer data)
11173 Compose *compose = (Compose *)data;
11174 if (!compose->gtkaspell)
11177 if (gtk_widget_has_focus(compose->subject_entry))
11178 claws_spell_entry_check_all(
11179 CLAWS_SPELL_ENTRY(compose->subject_entry));
11181 gtkaspell_check_all(compose->gtkaspell);
11184 static void compose_highlight_all(GtkAction *action, gpointer data)
11186 Compose *compose = (Compose *)data;
11187 if (compose->gtkaspell) {
11188 claws_spell_entry_recheck_all(
11189 CLAWS_SPELL_ENTRY(compose->subject_entry));
11190 gtkaspell_highlight_all(compose->gtkaspell);
11194 static void compose_check_backwards(GtkAction *action, gpointer data)
11196 Compose *compose = (Compose *)data;
11197 if (!compose->gtkaspell) {
11198 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11202 if (gtk_widget_has_focus(compose->subject_entry))
11203 claws_spell_entry_check_backwards(
11204 CLAWS_SPELL_ENTRY(compose->subject_entry));
11206 gtkaspell_check_backwards(compose->gtkaspell);
11209 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11211 Compose *compose = (Compose *)data;
11212 if (!compose->gtkaspell) {
11213 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11217 if (gtk_widget_has_focus(compose->subject_entry))
11218 claws_spell_entry_check_forwards_go(
11219 CLAWS_SPELL_ENTRY(compose->subject_entry));
11221 gtkaspell_check_forwards_go(compose->gtkaspell);
11226 *\brief Guess originating forward account from MsgInfo and several
11227 * "common preference" settings. Return NULL if no guess.
11229 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11231 PrefsAccount *account = NULL;
11233 cm_return_val_if_fail(msginfo, NULL);
11234 cm_return_val_if_fail(msginfo->folder, NULL);
11235 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11237 if (msginfo->folder->prefs->enable_default_account)
11238 account = account_find_from_id(msginfo->folder->prefs->default_account);
11241 account = msginfo->folder->folder->account;
11243 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11245 Xstrdup_a(to, msginfo->to, return NULL);
11246 extract_address(to);
11247 account = account_find_from_address(to, FALSE);
11250 if (!account && prefs_common.forward_account_autosel) {
11251 gchar cc[BUFFSIZE];
11252 if (!procheader_get_header_from_msginfo
11253 (msginfo, cc,sizeof cc , "Cc:")) {
11254 gchar *buf = cc + strlen("Cc:");
11255 extract_address(buf);
11256 account = account_find_from_address(buf, FALSE);
11260 if (!account && prefs_common.forward_account_autosel) {
11261 gchar deliveredto[BUFFSIZE];
11262 if (!procheader_get_header_from_msginfo
11263 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
11264 gchar *buf = deliveredto + strlen("Delivered-To:");
11265 extract_address(buf);
11266 account = account_find_from_address(buf, FALSE);
11273 gboolean compose_close(Compose *compose)
11277 if (!g_mutex_trylock(compose->mutex)) {
11278 /* we have to wait for the (possibly deferred by auto-save)
11279 * drafting to be done, before destroying the compose under
11281 debug_print("waiting for drafting to finish...\n");
11282 compose_allow_user_actions(compose, FALSE);
11283 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11286 cm_return_val_if_fail(compose, FALSE);
11287 gtkut_widget_get_uposition(compose->window, &x, &y);
11288 if (!compose->batch) {
11289 prefs_common.compose_x = x;
11290 prefs_common.compose_y = y;
11292 g_mutex_unlock(compose->mutex);
11293 compose_destroy(compose);
11298 * Add entry field for each address in list.
11299 * \param compose E-Mail composition object.
11300 * \param listAddress List of (formatted) E-Mail addresses.
11302 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11305 node = listAddress;
11307 addr = ( gchar * ) node->data;
11308 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11309 node = g_list_next( node );
11313 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11314 guint action, gboolean opening_multiple)
11316 gchar *body = NULL;
11317 GSList *new_msglist = NULL;
11318 MsgInfo *tmp_msginfo = NULL;
11319 gboolean originally_enc = FALSE;
11320 gboolean originally_sig = FALSE;
11321 Compose *compose = NULL;
11322 gchar *s_system = NULL;
11324 cm_return_if_fail(msgview != NULL);
11326 cm_return_if_fail(msginfo_list != NULL);
11328 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11329 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11330 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11332 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11333 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11334 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11335 orig_msginfo, mimeinfo);
11336 if (tmp_msginfo != NULL) {
11337 new_msglist = g_slist_append(NULL, tmp_msginfo);
11339 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11340 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11341 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11343 tmp_msginfo->folder = orig_msginfo->folder;
11344 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11345 if (orig_msginfo->tags) {
11346 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11347 tmp_msginfo->folder->tags_dirty = TRUE;
11353 if (!opening_multiple)
11354 body = messageview_get_selection(msgview);
11357 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11358 procmsg_msginfo_free(tmp_msginfo);
11359 g_slist_free(new_msglist);
11361 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11363 if (compose && originally_enc) {
11364 compose_force_encryption(compose, compose->account, FALSE, s_system);
11367 if (compose && originally_sig && compose->account->default_sign_reply) {
11368 compose_force_signing(compose, compose->account, s_system);
11372 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11375 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11378 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11379 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11380 GSList *cur = msginfo_list;
11381 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11382 "messages. Opening the windows "
11383 "could take some time. Do you "
11384 "want to continue?"),
11385 g_slist_length(msginfo_list));
11386 if (g_slist_length(msginfo_list) > 9
11387 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11388 != G_ALERTALTERNATE) {
11393 /* We'll open multiple compose windows */
11394 /* let the WM place the next windows */
11395 compose_force_window_origin = FALSE;
11396 for (; cur; cur = cur->next) {
11398 tmplist.data = cur->data;
11399 tmplist.next = NULL;
11400 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11402 compose_force_window_origin = TRUE;
11404 /* forwarding multiple mails as attachments is done via a
11405 * single compose window */
11406 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11410 void compose_check_for_email_account(Compose *compose)
11412 PrefsAccount *ac = NULL, *curr = NULL;
11418 if (compose->account && compose->account->protocol == A_NNTP) {
11419 ac = account_get_cur_account();
11420 if (ac->protocol == A_NNTP) {
11421 list = account_get_list();
11423 for( ; list != NULL ; list = g_list_next(list)) {
11424 curr = (PrefsAccount *) list->data;
11425 if (curr->protocol != A_NNTP) {
11431 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11436 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo,
11437 const gchar *address)
11439 GSList *msginfo_list = NULL;
11440 gchar *body = messageview_get_selection(msgview);
11443 msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11445 compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11446 compose_check_for_email_account(compose);
11447 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11448 compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11449 compose_reply_set_subject(compose, msginfo);
11452 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11455 void compose_set_position(Compose *compose, gint pos)
11457 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11459 gtkut_text_view_set_position(text, pos);
11462 gboolean compose_search_string(Compose *compose,
11463 const gchar *str, gboolean case_sens)
11465 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11467 return gtkut_text_view_search_string(text, str, case_sens);
11470 gboolean compose_search_string_backward(Compose *compose,
11471 const gchar *str, gboolean case_sens)
11473 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11475 return gtkut_text_view_search_string_backward(text, str, case_sens);
11478 /* allocate a msginfo structure and populate its data from a compose data structure */
11479 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11481 MsgInfo *newmsginfo;
11483 gchar buf[BUFFSIZE];
11485 cm_return_val_if_fail( compose != NULL, NULL );
11487 newmsginfo = procmsg_msginfo_new();
11490 get_rfc822_date(buf, sizeof(buf));
11491 newmsginfo->date = g_strdup(buf);
11494 if (compose->from_name) {
11495 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11496 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11500 if (compose->subject_entry)
11501 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11503 /* to, cc, reply-to, newsgroups */
11504 for (list = compose->header_list; list; list = list->next) {
11505 gchar *header = gtk_editable_get_chars(
11507 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11508 gchar *entry = gtk_editable_get_chars(
11509 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11511 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11512 if ( newmsginfo->to == NULL ) {
11513 newmsginfo->to = g_strdup(entry);
11514 } else if (entry && *entry) {
11515 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11516 g_free(newmsginfo->to);
11517 newmsginfo->to = tmp;
11520 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11521 if ( newmsginfo->cc == NULL ) {
11522 newmsginfo->cc = g_strdup(entry);
11523 } else if (entry && *entry) {
11524 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11525 g_free(newmsginfo->cc);
11526 newmsginfo->cc = tmp;
11529 if ( strcasecmp(header,
11530 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11531 if ( newmsginfo->newsgroups == NULL ) {
11532 newmsginfo->newsgroups = g_strdup(entry);
11533 } else if (entry && *entry) {
11534 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11535 g_free(newmsginfo->newsgroups);
11536 newmsginfo->newsgroups = tmp;
11544 /* other data is unset */
11550 /* update compose's dictionaries from folder dict settings */
11551 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11552 FolderItem *folder_item)
11554 cm_return_if_fail(compose != NULL);
11556 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11557 FolderItemPrefs *prefs = folder_item->prefs;
11559 if (prefs->enable_default_dictionary)
11560 gtkaspell_change_dict(compose->gtkaspell,
11561 prefs->default_dictionary, FALSE);
11562 if (folder_item->prefs->enable_default_alt_dictionary)
11563 gtkaspell_change_alt_dict(compose->gtkaspell,
11564 prefs->default_alt_dictionary);
11565 if (prefs->enable_default_dictionary
11566 || prefs->enable_default_alt_dictionary)
11567 compose_spell_menu_changed(compose);