2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2011 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #include "addressbook.h"
64 #include "folderview.h"
67 #include "stock_pixmap.h"
68 #include "send_message.h"
71 #include "customheader.h"
72 #include "prefs_common.h"
73 #include "prefs_account.h"
77 #include "procheader.h"
79 #include "statusbar.h"
82 #include "quoted-printable.h"
86 #include "gtkshruler.h"
88 #include "alertpanel.h"
89 #include "manage_window.h"
91 #include "addr_compl.h"
92 #include "quote_fmt.h"
94 #include "foldersel.h"
97 #include "message_search.h"
102 #include "autofaces.h"
103 #include "spell_entry.h"
116 #define N_ATTACH_COLS (N_COL_COLUMNS)
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
132 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
134 } ComposeCallAdvancedAction;
138 PRIORITY_HIGHEST = 1,
147 COMPOSE_INSERT_SUCCESS,
148 COMPOSE_INSERT_READ_ERROR,
149 COMPOSE_INSERT_INVALID_CHARACTER,
150 COMPOSE_INSERT_NO_FILE
151 } ComposeInsertResult;
155 COMPOSE_WRITE_FOR_SEND,
156 COMPOSE_WRITE_FOR_STORE
161 COMPOSE_QUOTE_FORCED,
168 SUBJECT_FIELD_PRESENT,
173 #define B64_LINE_SIZE 57
174 #define B64_BUFFSIZE 77
176 #define MAX_REFERENCES_LEN 999
178 static GList *compose_list = NULL;
180 static Compose *compose_generic_new (PrefsAccount *account,
183 GPtrArray *attach_files,
184 GList *listAddress );
186 static Compose *compose_create (PrefsAccount *account,
191 static void compose_entry_mark_default_to (Compose *compose,
192 const gchar *address);
193 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
194 ComposeQuoteMode quote_mode,
198 static Compose *compose_forward_multiple (PrefsAccount *account,
199 GSList *msginfo_list);
200 static Compose *compose_reply (MsgInfo *msginfo,
201 ComposeQuoteMode quote_mode,
206 static Compose *compose_reply_mode (ComposeMode mode,
207 GSList *msginfo_list,
209 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
210 static void compose_update_privacy_systems_menu(Compose *compose);
212 static GtkWidget *compose_account_option_menu_create
214 static void compose_set_out_encoding (Compose *compose);
215 static void compose_set_template_menu (Compose *compose);
216 static void compose_destroy (Compose *compose);
218 static MailField compose_entries_set (Compose *compose,
220 ComposeEntryType to_type);
221 static gint compose_parse_header (Compose *compose,
223 static gint compose_parse_manual_headers (Compose *compose,
225 HeaderEntry *entries);
226 static gchar *compose_parse_references (const gchar *ref,
229 static gchar *compose_quote_fmt (Compose *compose,
235 gboolean need_unescape,
236 const gchar *err_msg);
238 static void compose_reply_set_entry (Compose *compose,
244 followup_and_reply_to);
245 static void compose_reedit_set_entry (Compose *compose,
248 static void compose_insert_sig (Compose *compose,
250 static ComposeInsertResult compose_insert_file (Compose *compose,
253 static gboolean compose_attach_append (Compose *compose,
256 const gchar *content_type,
257 const gchar *charset);
258 static void compose_attach_parts (Compose *compose,
261 static gboolean compose_beautify_paragraph (Compose *compose,
262 GtkTextIter *par_iter,
264 static void compose_wrap_all (Compose *compose);
265 static void compose_wrap_all_full (Compose *compose,
268 static void compose_set_title (Compose *compose);
269 static void compose_select_account (Compose *compose,
270 PrefsAccount *account,
273 static PrefsAccount *compose_current_mail_account(void);
274 /* static gint compose_send (Compose *compose); */
275 static gboolean compose_check_for_valid_recipient
277 static gboolean compose_check_entries (Compose *compose,
278 gboolean check_everything);
279 static gint compose_write_to_file (Compose *compose,
282 gboolean attach_parts);
283 static gint compose_write_body_to_file (Compose *compose,
285 static gint compose_remove_reedit_target (Compose *compose,
287 static void compose_remove_draft (Compose *compose);
288 static gint compose_queue_sub (Compose *compose,
292 gboolean check_subject,
293 gboolean remove_reedit_target);
294 static int compose_add_attachments (Compose *compose,
296 static gchar *compose_get_header (Compose *compose);
297 static gchar *compose_get_manual_headers_info (Compose *compose);
299 static void compose_convert_header (Compose *compose,
304 gboolean addr_field);
306 static void compose_attach_info_free (AttachInfo *ainfo);
307 static void compose_attach_remove_selected (GtkAction *action,
310 static void compose_template_apply (Compose *compose,
313 static void compose_attach_property (GtkAction *action,
315 static void compose_attach_property_create (gboolean *cancelled);
316 static void attach_property_ok (GtkWidget *widget,
317 gboolean *cancelled);
318 static void attach_property_cancel (GtkWidget *widget,
319 gboolean *cancelled);
320 static gint attach_property_delete_event (GtkWidget *widget,
322 gboolean *cancelled);
323 static gboolean attach_property_key_pressed (GtkWidget *widget,
325 gboolean *cancelled);
327 static void compose_exec_ext_editor (Compose *compose);
329 static gint compose_exec_ext_editor_real (const gchar *file);
330 static gboolean compose_ext_editor_kill (Compose *compose);
331 static gboolean compose_input_cb (GIOChannel *source,
332 GIOCondition condition,
334 static void compose_set_ext_editor_sensitive (Compose *compose,
336 #endif /* G_OS_UNIX */
338 static void compose_undo_state_changed (UndoMain *undostruct,
343 static void compose_create_header_entry (Compose *compose);
344 static void compose_add_header_entry (Compose *compose, const gchar *header,
345 gchar *text, ComposePrefType pref_type);
346 static void compose_remove_header_entries(Compose *compose);
348 static void compose_update_priority_menu_item(Compose * compose);
350 static void compose_spell_menu_changed (void *data);
351 static void compose_dict_changed (void *data);
353 static void compose_add_field_list ( Compose *compose,
354 GList *listAddress );
356 /* callback functions */
358 static void compose_notebook_size_alloc (GtkNotebook *notebook,
359 GtkAllocation *allocation,
361 static gboolean compose_edit_size_alloc (GtkEditable *widget,
362 GtkAllocation *allocation,
363 GtkSHRuler *shruler);
364 static void account_activated (GtkComboBox *optmenu,
366 static void attach_selected (GtkTreeView *tree_view,
367 GtkTreePath *tree_path,
368 GtkTreeViewColumn *column,
370 static gboolean attach_button_pressed (GtkWidget *widget,
371 GdkEventButton *event,
373 static gboolean attach_key_pressed (GtkWidget *widget,
376 static void compose_send_cb (GtkAction *action, gpointer data);
377 static void compose_send_later_cb (GtkAction *action, gpointer data);
379 static void compose_save_cb (GtkAction *action,
382 static void compose_attach_cb (GtkAction *action,
384 static void compose_insert_file_cb (GtkAction *action,
386 static void compose_insert_sig_cb (GtkAction *action,
389 static void compose_close_cb (GtkAction *action,
391 static void compose_print_cb (GtkAction *action,
394 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
396 static void compose_address_cb (GtkAction *action,
398 static void about_show_cb (GtkAction *action,
400 static void compose_template_activate_cb(GtkWidget *widget,
403 static void compose_ext_editor_cb (GtkAction *action,
406 static gint compose_delete_cb (GtkWidget *widget,
410 static void compose_undo_cb (GtkAction *action,
412 static void compose_redo_cb (GtkAction *action,
414 static void compose_cut_cb (GtkAction *action,
416 static void compose_copy_cb (GtkAction *action,
418 static void compose_paste_cb (GtkAction *action,
420 static void compose_paste_as_quote_cb (GtkAction *action,
422 static void compose_paste_no_wrap_cb (GtkAction *action,
424 static void compose_paste_wrap_cb (GtkAction *action,
426 static void compose_allsel_cb (GtkAction *action,
429 static void compose_advanced_action_cb (GtkAction *action,
432 static void compose_grab_focus_cb (GtkWidget *widget,
435 static void compose_changed_cb (GtkTextBuffer *textbuf,
438 static void compose_wrap_cb (GtkAction *action,
440 static void compose_wrap_all_cb (GtkAction *action,
442 static void compose_find_cb (GtkAction *action,
444 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
446 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
449 static void compose_toggle_ruler_cb (GtkToggleAction *action,
451 static void compose_toggle_sign_cb (GtkToggleAction *action,
453 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
455 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
456 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
457 static void activate_privacy_system (Compose *compose,
458 PrefsAccount *account,
460 static void compose_use_signing(Compose *compose, gboolean use_signing);
461 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
462 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
464 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
466 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
467 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
468 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
470 static void compose_attach_drag_received_cb (GtkWidget *widget,
471 GdkDragContext *drag_context,
474 GtkSelectionData *data,
478 static void compose_insert_drag_received_cb (GtkWidget *widget,
479 GdkDragContext *drag_context,
482 GtkSelectionData *data,
486 static void compose_header_drag_received_cb (GtkWidget *widget,
487 GdkDragContext *drag_context,
490 GtkSelectionData *data,
495 static gboolean compose_drag_drop (GtkWidget *widget,
496 GdkDragContext *drag_context,
498 guint time, gpointer user_data);
500 static void text_inserted (GtkTextBuffer *buffer,
505 static Compose *compose_generic_reply(MsgInfo *msginfo,
506 ComposeQuoteMode quote_mode,
510 gboolean followup_and_reply_to,
513 static void compose_headerentry_changed_cb (GtkWidget *entry,
514 ComposeHeaderEntry *headerentry);
515 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
517 ComposeHeaderEntry *headerentry);
518 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
519 ComposeHeaderEntry *headerentry);
521 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
523 static void compose_allow_user_actions (Compose *compose, gboolean allow);
525 static void compose_nothing_cb (GtkAction *action, gpointer data)
531 static void compose_check_all (GtkAction *action, gpointer data);
532 static void compose_highlight_all (GtkAction *action, gpointer data);
533 static void compose_check_backwards (GtkAction *action, gpointer data);
534 static void compose_check_forwards_go (GtkAction *action, gpointer data);
537 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
539 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
542 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
543 FolderItem *folder_item);
545 static void compose_attach_update_label(Compose *compose);
546 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
547 gboolean respect_default_to);
549 static GtkActionEntry compose_popup_entries[] =
551 {"Compose", NULL, "Compose" },
552 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
553 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
554 {"Compose/---", NULL, "---", NULL, NULL, NULL },
555 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
558 static GtkActionEntry compose_entries[] =
560 {"Menu", NULL, "Menu" },
562 {"Message", NULL, N_("_Message") },
563 {"Edit", NULL, N_("_Edit") },
565 {"Spelling", NULL, N_("_Spelling") },
567 {"Options", NULL, N_("_Options") },
568 {"Tools", NULL, N_("_Tools") },
569 {"Help", NULL, N_("_Help") },
571 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
572 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
573 {"Message/---", NULL, "---" },
575 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
576 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
577 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
578 /* {"Message/---", NULL, "---" }, */
579 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
580 /* {"Message/---", NULL, "---" }, */
581 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
582 /* {"Message/---", NULL, "---" }, */
583 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
586 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
587 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
588 {"Edit/---", NULL, "---" },
590 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
591 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
592 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
594 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
595 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
596 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
597 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
599 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
601 {"Edit/Advanced", NULL, N_("A_dvanced") },
602 {"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*/
603 {"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*/
604 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
605 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
606 {"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*/
607 {"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*/
608 {"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*/
609 {"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*/
610 {"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*/
611 {"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*/
612 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
613 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
614 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
615 {"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*/
617 /* {"Edit/---", NULL, "---" }, */
618 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
620 /* {"Edit/---", NULL, "---" }, */
621 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
622 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
623 /* {"Edit/---", NULL, "---" }, */
624 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
627 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
628 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
629 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
630 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
632 {"Spelling/---", NULL, "---" },
633 {"Spelling/Options", NULL, N_("_Options") },
638 {"Options/ReplyMode", NULL, N_("Reply _mode") },
639 {"Options/---", NULL, "---" },
640 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
641 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
643 /* {"Options/---", NULL, "---" }, */
645 {"Options/Priority", NULL, N_("_Priority") },
647 {"Options/Encoding", NULL, N_("Character _encoding") },
648 {"Options/Encoding/---", NULL, "---" },
649 #define ENC_ACTION(cs_char,c_char,string) \
650 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
652 {"Options/Encoding/Western", NULL, N_("Western European") },
653 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
654 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
655 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
656 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
657 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
658 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
659 {"Options/Encoding/Korean", NULL, N_("Korean") },
660 {"Options/Encoding/Thai", NULL, N_("Thai") },
663 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
665 {"Tools/Template", NULL, N_("_Template") },
666 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
667 {"Tools/Actions", NULL, N_("Actio_ns") },
668 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
671 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
674 static GtkToggleActionEntry compose_toggle_entries[] =
676 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
677 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
678 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
679 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
680 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
681 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
682 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
685 static GtkRadioActionEntry compose_radio_rm_entries[] =
687 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
688 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
689 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
690 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
693 static GtkRadioActionEntry compose_radio_prio_entries[] =
695 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
696 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
697 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
698 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
699 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
702 static GtkRadioActionEntry compose_radio_enc_entries[] =
704 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
729 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
730 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
731 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
732 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
733 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
734 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
735 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
738 static GtkTargetEntry compose_mime_types[] =
740 {"text/uri-list", 0, 0},
741 {"UTF8_STRING", 0, 0},
745 static gboolean compose_put_existing_to_front(MsgInfo *info)
747 GList *compose_list = compose_get_compose_list();
751 for (elem = compose_list; elem != NULL && elem->data != NULL;
753 Compose *c = (Compose*)elem->data;
755 if (!c->targetinfo || !c->targetinfo->msgid ||
759 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
760 gtkut_window_popup(c->window);
768 static GdkColor quote_color1 =
769 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
770 static GdkColor quote_color2 =
771 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
772 static GdkColor quote_color3 =
773 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
775 static GdkColor quote_bgcolor1 =
776 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
777 static GdkColor quote_bgcolor2 =
778 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
779 static GdkColor quote_bgcolor3 =
780 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
782 static GdkColor signature_color = {
789 static GdkColor uri_color = {
796 static void compose_create_tags(GtkTextView *text, Compose *compose)
798 GtkTextBuffer *buffer;
799 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
800 #if !GTK_CHECK_VERSION(2, 24, 0)
807 buffer = gtk_text_view_get_buffer(text);
809 if (prefs_common.enable_color) {
810 /* grab the quote colors, converting from an int to a GdkColor */
811 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
813 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
815 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
817 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
819 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
821 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
823 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
825 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
828 signature_color = quote_color1 = quote_color2 = quote_color3 =
829 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
832 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
833 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
834 "foreground-gdk", "e_color1,
835 "paragraph-background-gdk", "e_bgcolor1,
837 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
838 "foreground-gdk", "e_color2,
839 "paragraph-background-gdk", "e_bgcolor2,
841 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
842 "foreground-gdk", "e_color3,
843 "paragraph-background-gdk", "e_bgcolor3,
846 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
847 "foreground-gdk", "e_color1,
849 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
850 "foreground-gdk", "e_color2,
852 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
853 "foreground-gdk", "e_color3,
857 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
858 "foreground-gdk", &signature_color,
861 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
862 "foreground-gdk", &uri_color,
864 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
865 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
867 color[0] = quote_color1;
868 color[1] = quote_color2;
869 color[2] = quote_color3;
870 color[3] = quote_bgcolor1;
871 color[4] = quote_bgcolor2;
872 color[5] = quote_bgcolor3;
873 color[6] = signature_color;
874 color[7] = uri_color;
875 #if !GTK_CHECK_VERSION(2, 24, 0)
876 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
877 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
879 for (i = 0; i < 8; i++) {
880 if (success[i] == FALSE) {
883 g_warning("Compose: color allocation failed.\n");
884 style = gtk_widget_get_style(GTK_WIDGET(text));
885 quote_color1 = quote_color2 = quote_color3 =
886 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
887 signature_color = uri_color = black;
893 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
894 GPtrArray *attach_files)
896 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
899 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
901 return compose_generic_new(account, mailto, item, NULL, NULL);
904 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
906 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
909 #define SCROLL_TO_CURSOR(compose) { \
910 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
911 gtk_text_view_get_buffer( \
912 GTK_TEXT_VIEW(compose->text))); \
913 gtk_text_view_scroll_mark_onscreen( \
914 GTK_TEXT_VIEW(compose->text), \
918 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
921 if (folderidentifier) {
922 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
923 prefs_common.compose_save_to_history = add_history(
924 prefs_common.compose_save_to_history, folderidentifier);
925 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
926 prefs_common.compose_save_to_history);
929 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
930 if (folderidentifier)
931 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
933 gtk_entry_set_text(GTK_ENTRY(entry), "");
936 static gchar *compose_get_save_to(Compose *compose)
939 gchar *result = NULL;
940 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
941 result = gtk_editable_get_chars(entry, 0, -1);
944 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
945 prefs_common.compose_save_to_history = add_history(
946 prefs_common.compose_save_to_history, result);
947 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
948 prefs_common.compose_save_to_history);
953 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
954 GPtrArray *attach_files, GList *listAddress )
957 GtkTextView *textview;
958 GtkTextBuffer *textbuf;
960 const gchar *subject_format = NULL;
961 const gchar *body_format = NULL;
962 gchar *mailto_from = NULL;
963 PrefsAccount *mailto_account = NULL;
964 MsgInfo* dummyinfo = NULL;
965 gint cursor_pos = -1;
966 MailField mfield = NO_FIELD_PRESENT;
970 /* check if mailto defines a from */
971 if (mailto && *mailto != '\0') {
972 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
973 /* mailto defines a from, check if we can get account prefs from it,
974 if not, the account prefs will be guessed using other ways, but we'll keep
977 mailto_account = account_find_from_address(mailto_from, TRUE);
979 account = mailto_account;
982 /* if no account prefs set from mailto, set if from folder prefs (if any) */
983 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
984 account = account_find_from_id(item->prefs->default_account);
986 /* if no account prefs set, fallback to the current one */
987 if (!account) account = cur_account;
988 cm_return_val_if_fail(account != NULL, NULL);
990 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
992 /* override from name if mailto asked for it */
994 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
997 /* override from name according to folder properties */
998 if (item && item->prefs &&
999 item->prefs->compose_with_format &&
1000 item->prefs->compose_override_from_format &&
1001 *item->prefs->compose_override_from_format != '\0') {
1006 dummyinfo = compose_msginfo_new_from_compose(compose);
1008 /* decode \-escape sequences in the internal representation of the quote format */
1009 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1010 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1013 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1014 compose->gtkaspell);
1016 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1018 quote_fmt_scan_string(tmp);
1021 buf = quote_fmt_get_buffer();
1023 alertpanel_error(_("New message From format error."));
1025 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1026 quote_fmt_reset_vartable();
1031 compose->replyinfo = NULL;
1032 compose->fwdinfo = NULL;
1034 textview = GTK_TEXT_VIEW(compose->text);
1035 textbuf = gtk_text_view_get_buffer(textview);
1036 compose_create_tags(textview, compose);
1038 undo_block(compose->undostruct);
1040 compose_set_dictionaries_from_folder_prefs(compose, item);
1043 if (account->auto_sig)
1044 compose_insert_sig(compose, FALSE);
1045 gtk_text_buffer_get_start_iter(textbuf, &iter);
1046 gtk_text_buffer_place_cursor(textbuf, &iter);
1048 if (account->protocol != A_NNTP) {
1049 if (mailto && *mailto != '\0') {
1050 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1053 compose_set_folder_prefs(compose, item, TRUE);
1055 if (item && item->ret_rcpt) {
1056 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1059 if (mailto && *mailto != '\0') {
1060 if (!strchr(mailto, '@'))
1061 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1063 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1064 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1065 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1066 mfield = TO_FIELD_PRESENT;
1069 * CLAWS: just don't allow return receipt request, even if the user
1070 * may want to send an email. simple but foolproof.
1072 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1074 compose_add_field_list( compose, listAddress );
1076 if (item && item->prefs && item->prefs->compose_with_format) {
1077 subject_format = item->prefs->compose_subject_format;
1078 body_format = item->prefs->compose_body_format;
1079 } else if (account->compose_with_format) {
1080 subject_format = account->compose_subject_format;
1081 body_format = account->compose_body_format;
1082 } else if (prefs_common.compose_with_format) {
1083 subject_format = prefs_common.compose_subject_format;
1084 body_format = prefs_common.compose_body_format;
1087 if (subject_format || body_format) {
1090 && *subject_format != '\0' )
1092 gchar *subject = NULL;
1097 dummyinfo = compose_msginfo_new_from_compose(compose);
1099 /* decode \-escape sequences in the internal representation of the quote format */
1100 tmp = g_malloc(strlen(subject_format)+1);
1101 pref_get_unescaped_pref(tmp, subject_format);
1103 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1105 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1106 compose->gtkaspell);
1108 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1110 quote_fmt_scan_string(tmp);
1113 buf = quote_fmt_get_buffer();
1115 alertpanel_error(_("New message subject format error."));
1117 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1118 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1119 quote_fmt_reset_vartable();
1123 mfield = SUBJECT_FIELD_PRESENT;
1127 && *body_format != '\0' )
1130 GtkTextBuffer *buffer;
1131 GtkTextIter start, end;
1135 dummyinfo = compose_msginfo_new_from_compose(compose);
1137 text = GTK_TEXT_VIEW(compose->text);
1138 buffer = gtk_text_view_get_buffer(text);
1139 gtk_text_buffer_get_start_iter(buffer, &start);
1140 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1141 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1143 compose_quote_fmt(compose, dummyinfo,
1145 NULL, tmp, FALSE, TRUE,
1146 _("The body of the \"New message\" template has an error at line %d."));
1147 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1148 quote_fmt_reset_vartable();
1152 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1153 gtkaspell_highlight_all(compose->gtkaspell);
1155 mfield = BODY_FIELD_PRESENT;
1159 procmsg_msginfo_free( dummyinfo );
1165 for (i = 0; i < attach_files->len; i++) {
1166 file = g_ptr_array_index(attach_files, i);
1167 compose_attach_append(compose, file, file, NULL, NULL);
1171 compose_show_first_last_header(compose, TRUE);
1173 /* Set save folder */
1174 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1175 gchar *folderidentifier;
1177 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1178 folderidentifier = folder_item_get_identifier(item);
1179 compose_set_save_to(compose, folderidentifier);
1180 g_free(folderidentifier);
1183 /* Place cursor according to provided input (mfield) */
1185 case NO_FIELD_PRESENT:
1186 if (compose->header_last)
1187 gtk_widget_grab_focus(compose->header_last->entry);
1189 case TO_FIELD_PRESENT:
1190 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1192 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1195 gtk_widget_grab_focus(compose->subject_entry);
1197 case SUBJECT_FIELD_PRESENT:
1198 textview = GTK_TEXT_VIEW(compose->text);
1201 textbuf = gtk_text_view_get_buffer(textview);
1204 mark = gtk_text_buffer_get_insert(textbuf);
1205 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1206 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1208 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1209 * only defers where it comes to the variable body
1210 * is not null. If no body is present compose->text
1211 * will be null in which case you cannot place the
1212 * cursor inside the component so. An empty component
1213 * is therefore created before placing the cursor
1215 case BODY_FIELD_PRESENT:
1216 cursor_pos = quote_fmt_get_cursor_pos();
1217 if (cursor_pos == -1)
1218 gtk_widget_grab_focus(compose->header_last->entry);
1220 gtk_widget_grab_focus(compose->text);
1224 undo_unblock(compose->undostruct);
1226 if (prefs_common.auto_exteditor)
1227 compose_exec_ext_editor(compose);
1229 compose->draft_timeout_tag = -1;
1230 SCROLL_TO_CURSOR(compose);
1232 compose->modified = FALSE;
1233 compose_set_title(compose);
1235 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1240 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1241 gboolean override_pref, const gchar *system)
1243 const gchar *privacy = NULL;
1245 cm_return_if_fail(compose != NULL);
1246 cm_return_if_fail(account != NULL);
1248 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1253 else if (account->default_privacy_system
1254 && strlen(account->default_privacy_system)) {
1255 privacy = account->default_privacy_system;
1257 GSList *privacy_avail = privacy_get_system_ids();
1258 if (privacy_avail && g_slist_length(privacy_avail)) {
1259 privacy = (gchar *)(privacy_avail->data);
1262 if (privacy != NULL) {
1264 g_free(compose->privacy_system);
1265 compose->privacy_system = NULL;
1267 if (compose->privacy_system == NULL)
1268 compose->privacy_system = g_strdup(privacy);
1269 else if (*(compose->privacy_system) == '\0') {
1270 g_free(compose->privacy_system);
1271 compose->privacy_system = g_strdup(privacy);
1273 compose_update_privacy_system_menu_item(compose, FALSE);
1274 compose_use_encryption(compose, TRUE);
1278 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1280 const gchar *privacy = NULL;
1284 else if (account->default_privacy_system
1285 && strlen(account->default_privacy_system)) {
1286 privacy = account->default_privacy_system;
1288 GSList *privacy_avail = privacy_get_system_ids();
1289 if (privacy_avail && g_slist_length(privacy_avail)) {
1290 privacy = (gchar *)(privacy_avail->data);
1294 if (privacy != NULL) {
1296 g_free(compose->privacy_system);
1297 compose->privacy_system = NULL;
1299 if (compose->privacy_system == NULL)
1300 compose->privacy_system = g_strdup(privacy);
1301 compose_update_privacy_system_menu_item(compose, FALSE);
1302 compose_use_signing(compose, TRUE);
1306 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1310 Compose *compose = NULL;
1312 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1314 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1315 cm_return_val_if_fail(msginfo != NULL, NULL);
1317 list_len = g_slist_length(msginfo_list);
1321 case COMPOSE_REPLY_TO_ADDRESS:
1322 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1323 FALSE, prefs_common.default_reply_list, FALSE, body);
1325 case COMPOSE_REPLY_WITH_QUOTE:
1326 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1327 FALSE, prefs_common.default_reply_list, FALSE, body);
1329 case COMPOSE_REPLY_WITHOUT_QUOTE:
1330 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1331 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1333 case COMPOSE_REPLY_TO_SENDER:
1334 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1335 FALSE, FALSE, TRUE, body);
1337 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1338 compose = compose_followup_and_reply_to(msginfo,
1339 COMPOSE_QUOTE_CHECK,
1340 FALSE, FALSE, body);
1342 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1343 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1344 FALSE, FALSE, TRUE, body);
1346 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1347 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1348 FALSE, FALSE, TRUE, NULL);
1350 case COMPOSE_REPLY_TO_ALL:
1351 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1352 TRUE, FALSE, FALSE, body);
1354 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1355 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1356 TRUE, FALSE, FALSE, body);
1358 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1359 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1360 TRUE, FALSE, FALSE, NULL);
1362 case COMPOSE_REPLY_TO_LIST:
1363 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1364 FALSE, TRUE, FALSE, body);
1366 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1367 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1368 FALSE, TRUE, FALSE, body);
1370 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1371 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1372 FALSE, TRUE, FALSE, NULL);
1374 case COMPOSE_FORWARD:
1375 if (prefs_common.forward_as_attachment) {
1376 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1379 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1383 case COMPOSE_FORWARD_INLINE:
1384 /* check if we reply to more than one Message */
1385 if (list_len == 1) {
1386 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1389 /* more messages FALL THROUGH */
1390 case COMPOSE_FORWARD_AS_ATTACH:
1391 compose = compose_forward_multiple(NULL, msginfo_list);
1393 case COMPOSE_REDIRECT:
1394 compose = compose_redirect(NULL, msginfo, FALSE);
1397 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1400 if (compose == NULL) {
1401 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1405 compose->rmode = mode;
1406 switch (compose->rmode) {
1408 case COMPOSE_REPLY_WITH_QUOTE:
1409 case COMPOSE_REPLY_WITHOUT_QUOTE:
1410 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1411 debug_print("reply mode Normal\n");
1412 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1413 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1415 case COMPOSE_REPLY_TO_SENDER:
1416 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1417 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1418 debug_print("reply mode Sender\n");
1419 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1421 case COMPOSE_REPLY_TO_ALL:
1422 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1423 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1424 debug_print("reply mode All\n");
1425 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1427 case COMPOSE_REPLY_TO_LIST:
1428 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1429 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1430 debug_print("reply mode List\n");
1431 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1433 case COMPOSE_REPLY_TO_ADDRESS:
1434 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1442 static Compose *compose_reply(MsgInfo *msginfo,
1443 ComposeQuoteMode quote_mode,
1449 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1450 to_sender, FALSE, body);
1453 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1454 ComposeQuoteMode quote_mode,
1459 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1460 to_sender, TRUE, body);
1463 static void compose_extract_original_charset(Compose *compose)
1465 MsgInfo *info = NULL;
1466 if (compose->replyinfo) {
1467 info = compose->replyinfo;
1468 } else if (compose->fwdinfo) {
1469 info = compose->fwdinfo;
1470 } else if (compose->targetinfo) {
1471 info = compose->targetinfo;
1474 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1475 MimeInfo *partinfo = mimeinfo;
1476 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1477 partinfo = procmime_mimeinfo_next(partinfo);
1479 compose->orig_charset =
1480 g_strdup(procmime_mimeinfo_get_parameter(
1481 partinfo, "charset"));
1483 procmime_mimeinfo_free_all(mimeinfo);
1487 #define SIGNAL_BLOCK(buffer) { \
1488 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1489 G_CALLBACK(compose_changed_cb), \
1491 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1492 G_CALLBACK(text_inserted), \
1496 #define SIGNAL_UNBLOCK(buffer) { \
1497 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1498 G_CALLBACK(compose_changed_cb), \
1500 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1501 G_CALLBACK(text_inserted), \
1505 static Compose *compose_generic_reply(MsgInfo *msginfo,
1506 ComposeQuoteMode quote_mode,
1507 gboolean to_all, gboolean to_ml,
1509 gboolean followup_and_reply_to,
1513 PrefsAccount *account = NULL;
1514 GtkTextView *textview;
1515 GtkTextBuffer *textbuf;
1516 gboolean quote = FALSE;
1517 const gchar *qmark = NULL;
1518 const gchar *body_fmt = NULL;
1519 gchar *s_system = NULL;
1521 cm_return_val_if_fail(msginfo != NULL, NULL);
1522 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1524 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1526 cm_return_val_if_fail(account != NULL, NULL);
1528 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1530 compose->updating = TRUE;
1532 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1533 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1535 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1536 if (!compose->replyinfo)
1537 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1539 compose_extract_original_charset(compose);
1541 if (msginfo->folder && msginfo->folder->ret_rcpt)
1542 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1544 /* Set save folder */
1545 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1546 gchar *folderidentifier;
1548 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1549 folderidentifier = folder_item_get_identifier(msginfo->folder);
1550 compose_set_save_to(compose, folderidentifier);
1551 g_free(folderidentifier);
1554 if (compose_parse_header(compose, msginfo) < 0) {
1555 compose->updating = FALSE;
1556 compose_destroy(compose);
1560 /* override from name according to folder properties */
1561 if (msginfo->folder && msginfo->folder->prefs &&
1562 msginfo->folder->prefs->reply_with_format &&
1563 msginfo->folder->prefs->reply_override_from_format &&
1564 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1569 /* decode \-escape sequences in the internal representation of the quote format */
1570 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1571 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1574 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1575 compose->gtkaspell);
1577 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1579 quote_fmt_scan_string(tmp);
1582 buf = quote_fmt_get_buffer();
1584 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1586 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1587 quote_fmt_reset_vartable();
1592 textview = (GTK_TEXT_VIEW(compose->text));
1593 textbuf = gtk_text_view_get_buffer(textview);
1594 compose_create_tags(textview, compose);
1596 undo_block(compose->undostruct);
1598 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1599 gtkaspell_block_check(compose->gtkaspell);
1602 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1603 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1604 /* use the reply format of folder (if enabled), or the account's one
1605 (if enabled) or fallback to the global reply format, which is always
1606 enabled (even if empty), and use the relevant quotemark */
1608 if (msginfo->folder && msginfo->folder->prefs &&
1609 msginfo->folder->prefs->reply_with_format) {
1610 qmark = msginfo->folder->prefs->reply_quotemark;
1611 body_fmt = msginfo->folder->prefs->reply_body_format;
1613 } else if (account->reply_with_format) {
1614 qmark = account->reply_quotemark;
1615 body_fmt = account->reply_body_format;
1618 qmark = prefs_common.quotemark;
1619 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1620 body_fmt = gettext(prefs_common.quotefmt);
1627 /* empty quotemark is not allowed */
1628 if (qmark == NULL || *qmark == '\0')
1630 compose_quote_fmt(compose, compose->replyinfo,
1631 body_fmt, qmark, body, FALSE, TRUE,
1632 _("The body of the \"Reply\" template has an error at line %d."));
1633 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1634 quote_fmt_reset_vartable();
1637 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1638 compose_force_encryption(compose, account, FALSE, s_system);
1641 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1642 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1643 compose_force_signing(compose, account, s_system);
1647 SIGNAL_BLOCK(textbuf);
1649 if (account->auto_sig)
1650 compose_insert_sig(compose, FALSE);
1652 compose_wrap_all(compose);
1655 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1656 gtkaspell_highlight_all(compose->gtkaspell);
1657 gtkaspell_unblock_check(compose->gtkaspell);
1659 SIGNAL_UNBLOCK(textbuf);
1661 gtk_widget_grab_focus(compose->text);
1663 undo_unblock(compose->undostruct);
1665 if (prefs_common.auto_exteditor)
1666 compose_exec_ext_editor(compose);
1668 compose->modified = FALSE;
1669 compose_set_title(compose);
1671 compose->updating = FALSE;
1672 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1673 SCROLL_TO_CURSOR(compose);
1675 if (compose->deferred_destroy) {
1676 compose_destroy(compose);
1684 #define INSERT_FW_HEADER(var, hdr) \
1685 if (msginfo->var && *msginfo->var) { \
1686 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1687 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1688 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1691 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1692 gboolean as_attach, const gchar *body,
1693 gboolean no_extedit,
1697 GtkTextView *textview;
1698 GtkTextBuffer *textbuf;
1699 gint cursor_pos = -1;
1702 cm_return_val_if_fail(msginfo != NULL, NULL);
1703 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1706 !(account = compose_guess_forward_account_from_msginfo
1708 account = cur_account;
1710 if (!prefs_common.forward_as_attachment)
1711 mode = COMPOSE_FORWARD_INLINE;
1713 mode = COMPOSE_FORWARD;
1714 compose = compose_create(account, msginfo->folder, mode, batch);
1716 compose->updating = TRUE;
1717 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1718 if (!compose->fwdinfo)
1719 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1721 compose_extract_original_charset(compose);
1723 if (msginfo->subject && *msginfo->subject) {
1724 gchar *buf, *buf2, *p;
1726 buf = p = g_strdup(msginfo->subject);
1727 p += subject_get_prefix_length(p);
1728 memmove(buf, p, strlen(p) + 1);
1730 buf2 = g_strdup_printf("Fw: %s", buf);
1731 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1737 /* override from name according to folder properties */
1738 if (msginfo->folder && msginfo->folder->prefs &&
1739 msginfo->folder->prefs->forward_with_format &&
1740 msginfo->folder->prefs->forward_override_from_format &&
1741 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1745 MsgInfo *full_msginfo = NULL;
1748 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1750 full_msginfo = procmsg_msginfo_copy(msginfo);
1752 /* decode \-escape sequences in the internal representation of the quote format */
1753 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1754 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1757 gtkaspell_block_check(compose->gtkaspell);
1758 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1759 compose->gtkaspell);
1761 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1763 quote_fmt_scan_string(tmp);
1766 buf = quote_fmt_get_buffer();
1768 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1770 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1771 quote_fmt_reset_vartable();
1774 procmsg_msginfo_free(full_msginfo);
1777 textview = GTK_TEXT_VIEW(compose->text);
1778 textbuf = gtk_text_view_get_buffer(textview);
1779 compose_create_tags(textview, compose);
1781 undo_block(compose->undostruct);
1785 msgfile = procmsg_get_message_file(msginfo);
1786 if (!is_file_exist(msgfile))
1787 g_warning("%s: file not exist\n", msgfile);
1789 compose_attach_append(compose, msgfile, msgfile,
1790 "message/rfc822", NULL);
1794 const gchar *qmark = NULL;
1795 const gchar *body_fmt = NULL;
1796 MsgInfo *full_msginfo;
1798 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1799 body_fmt = gettext(prefs_common.fw_quotefmt);
1803 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1805 full_msginfo = procmsg_msginfo_copy(msginfo);
1807 /* use the forward format of folder (if enabled), or the account's one
1808 (if enabled) or fallback to the global forward format, which is always
1809 enabled (even if empty), and use the relevant quotemark */
1810 if (msginfo->folder && msginfo->folder->prefs &&
1811 msginfo->folder->prefs->forward_with_format) {
1812 qmark = msginfo->folder->prefs->forward_quotemark;
1813 body_fmt = msginfo->folder->prefs->forward_body_format;
1815 } else if (account->forward_with_format) {
1816 qmark = account->forward_quotemark;
1817 body_fmt = account->forward_body_format;
1820 qmark = prefs_common.fw_quotemark;
1821 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1822 body_fmt = gettext(prefs_common.fw_quotefmt);
1827 /* empty quotemark is not allowed */
1828 if (qmark == NULL || *qmark == '\0')
1831 compose_quote_fmt(compose, full_msginfo,
1832 body_fmt, qmark, body, FALSE, TRUE,
1833 _("The body of the \"Forward\" template has an error at line %d."));
1834 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1835 quote_fmt_reset_vartable();
1836 compose_attach_parts(compose, msginfo);
1838 procmsg_msginfo_free(full_msginfo);
1841 SIGNAL_BLOCK(textbuf);
1843 if (account->auto_sig)
1844 compose_insert_sig(compose, FALSE);
1846 compose_wrap_all(compose);
1849 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1850 gtkaspell_highlight_all(compose->gtkaspell);
1851 gtkaspell_unblock_check(compose->gtkaspell);
1853 SIGNAL_UNBLOCK(textbuf);
1855 cursor_pos = quote_fmt_get_cursor_pos();
1856 if (cursor_pos == -1)
1857 gtk_widget_grab_focus(compose->header_last->entry);
1859 gtk_widget_grab_focus(compose->text);
1861 if (!no_extedit && prefs_common.auto_exteditor)
1862 compose_exec_ext_editor(compose);
1865 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1866 gchar *folderidentifier;
1868 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1869 folderidentifier = folder_item_get_identifier(msginfo->folder);
1870 compose_set_save_to(compose, folderidentifier);
1871 g_free(folderidentifier);
1874 undo_unblock(compose->undostruct);
1876 compose->modified = FALSE;
1877 compose_set_title(compose);
1879 compose->updating = FALSE;
1880 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1881 SCROLL_TO_CURSOR(compose);
1883 if (compose->deferred_destroy) {
1884 compose_destroy(compose);
1888 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1893 #undef INSERT_FW_HEADER
1895 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1898 GtkTextView *textview;
1899 GtkTextBuffer *textbuf;
1903 gboolean single_mail = TRUE;
1905 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1907 if (g_slist_length(msginfo_list) > 1)
1908 single_mail = FALSE;
1910 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1911 if (((MsgInfo *)msginfo->data)->folder == NULL)
1914 /* guess account from first selected message */
1916 !(account = compose_guess_forward_account_from_msginfo
1917 (msginfo_list->data)))
1918 account = cur_account;
1920 cm_return_val_if_fail(account != NULL, NULL);
1922 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1923 if (msginfo->data) {
1924 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1925 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1929 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1930 g_warning("no msginfo_list");
1934 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1936 compose->updating = TRUE;
1938 /* override from name according to folder properties */
1939 if (msginfo_list->data) {
1940 MsgInfo *msginfo = msginfo_list->data;
1942 if (msginfo->folder && msginfo->folder->prefs &&
1943 msginfo->folder->prefs->forward_with_format &&
1944 msginfo->folder->prefs->forward_override_from_format &&
1945 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1950 /* decode \-escape sequences in the internal representation of the quote format */
1951 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1952 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1955 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1956 compose->gtkaspell);
1958 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1960 quote_fmt_scan_string(tmp);
1963 buf = quote_fmt_get_buffer();
1965 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1967 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1968 quote_fmt_reset_vartable();
1974 textview = GTK_TEXT_VIEW(compose->text);
1975 textbuf = gtk_text_view_get_buffer(textview);
1976 compose_create_tags(textview, compose);
1978 undo_block(compose->undostruct);
1979 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1980 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1982 if (!is_file_exist(msgfile))
1983 g_warning("%s: file not exist\n", msgfile);
1985 compose_attach_append(compose, msgfile, msgfile,
1986 "message/rfc822", NULL);
1991 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1992 if (info->subject && *info->subject) {
1993 gchar *buf, *buf2, *p;
1995 buf = p = g_strdup(info->subject);
1996 p += subject_get_prefix_length(p);
1997 memmove(buf, p, strlen(p) + 1);
1999 buf2 = g_strdup_printf("Fw: %s", buf);
2000 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2006 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2007 _("Fw: multiple emails"));
2010 SIGNAL_BLOCK(textbuf);
2012 if (account->auto_sig)
2013 compose_insert_sig(compose, FALSE);
2015 compose_wrap_all(compose);
2017 SIGNAL_UNBLOCK(textbuf);
2019 gtk_text_buffer_get_start_iter(textbuf, &iter);
2020 gtk_text_buffer_place_cursor(textbuf, &iter);
2022 gtk_widget_grab_focus(compose->header_last->entry);
2023 undo_unblock(compose->undostruct);
2024 compose->modified = FALSE;
2025 compose_set_title(compose);
2027 compose->updating = FALSE;
2028 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2029 SCROLL_TO_CURSOR(compose);
2031 if (compose->deferred_destroy) {
2032 compose_destroy(compose);
2036 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2041 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2043 GtkTextIter start = *iter;
2044 GtkTextIter end_iter;
2045 int start_pos = gtk_text_iter_get_offset(&start);
2047 if (!compose->account->sig_sep)
2050 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2051 start_pos+strlen(compose->account->sig_sep));
2053 /* check sig separator */
2054 str = gtk_text_iter_get_text(&start, &end_iter);
2055 if (!strcmp(str, compose->account->sig_sep)) {
2057 /* check end of line (\n) */
2058 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2059 start_pos+strlen(compose->account->sig_sep));
2060 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2061 start_pos+strlen(compose->account->sig_sep)+1);
2062 tmp = gtk_text_iter_get_text(&start, &end_iter);
2063 if (!strcmp(tmp,"\n")) {
2075 static void compose_colorize_signature(Compose *compose)
2077 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2079 GtkTextIter end_iter;
2080 gtk_text_buffer_get_start_iter(buffer, &iter);
2081 while (gtk_text_iter_forward_line(&iter))
2082 if (compose_is_sig_separator(compose, buffer, &iter)) {
2083 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2084 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2088 #define BLOCK_WRAP() { \
2089 prev_autowrap = compose->autowrap; \
2090 buffer = gtk_text_view_get_buffer( \
2091 GTK_TEXT_VIEW(compose->text)); \
2092 compose->autowrap = FALSE; \
2094 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2095 G_CALLBACK(compose_changed_cb), \
2097 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2098 G_CALLBACK(text_inserted), \
2101 #define UNBLOCK_WRAP() { \
2102 compose->autowrap = prev_autowrap; \
2103 if (compose->autowrap) { \
2104 gint old = compose->draft_timeout_tag; \
2105 compose->draft_timeout_tag = -2; \
2106 compose_wrap_all(compose); \
2107 compose->draft_timeout_tag = old; \
2110 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2111 G_CALLBACK(compose_changed_cb), \
2113 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2114 G_CALLBACK(text_inserted), \
2118 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2120 Compose *compose = NULL;
2121 PrefsAccount *account = NULL;
2122 GtkTextView *textview;
2123 GtkTextBuffer *textbuf;
2127 gchar buf[BUFFSIZE];
2128 gboolean use_signing = FALSE;
2129 gboolean use_encryption = FALSE;
2130 gchar *privacy_system = NULL;
2131 int priority = PRIORITY_NORMAL;
2132 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2133 gboolean autowrap = prefs_common.autowrap;
2134 gboolean autoindent = prefs_common.auto_indent;
2135 HeaderEntry *manual_headers = NULL;
2137 cm_return_val_if_fail(msginfo != NULL, NULL);
2138 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2140 if (compose_put_existing_to_front(msginfo)) {
2144 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2145 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2146 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2147 gchar queueheader_buf[BUFFSIZE];
2150 /* Select Account from queue headers */
2151 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2152 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2153 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2154 account = account_find_from_id(id);
2156 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2157 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2158 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2159 account = account_find_from_id(id);
2161 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2162 sizeof(queueheader_buf), "NAID:")) {
2163 id = atoi(&queueheader_buf[strlen("NAID:")]);
2164 account = account_find_from_id(id);
2166 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2167 sizeof(queueheader_buf), "MAID:")) {
2168 id = atoi(&queueheader_buf[strlen("MAID:")]);
2169 account = account_find_from_id(id);
2171 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2172 sizeof(queueheader_buf), "S:")) {
2173 account = account_find_from_address(queueheader_buf, FALSE);
2175 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2176 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2177 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2178 use_signing = param;
2181 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2182 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2183 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2184 use_signing = param;
2187 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2188 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2189 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2190 use_encryption = param;
2192 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2193 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2194 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2195 use_encryption = param;
2197 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2198 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2199 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2202 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2203 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2204 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2207 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2208 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2209 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2211 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2212 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2213 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2215 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2216 sizeof(queueheader_buf), "X-Priority: ")) {
2217 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2220 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2221 sizeof(queueheader_buf), "RMID:")) {
2222 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2223 if (tokens[0] && tokens[1] && tokens[2]) {
2224 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2225 if (orig_item != NULL) {
2226 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2231 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2232 sizeof(queueheader_buf), "FMID:")) {
2233 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2234 if (tokens[0] && tokens[1] && tokens[2]) {
2235 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2236 if (orig_item != NULL) {
2237 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2242 /* Get manual headers */
2243 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2244 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2245 if (*listmh != '\0') {
2246 debug_print("Got manual headers: %s\n", listmh);
2247 manual_headers = procheader_entries_from_str(listmh);
2252 account = msginfo->folder->folder->account;
2255 if (!account && prefs_common.reedit_account_autosel) {
2256 gchar from[BUFFSIZE];
2257 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2258 extract_address(from);
2259 account = account_find_from_address(from, FALSE);
2263 account = cur_account;
2265 cm_return_val_if_fail(account != NULL, NULL);
2267 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2269 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2270 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2271 compose->autowrap = autowrap;
2272 compose->replyinfo = replyinfo;
2273 compose->fwdinfo = fwdinfo;
2275 compose->updating = TRUE;
2276 compose->priority = priority;
2278 if (privacy_system != NULL) {
2279 compose->privacy_system = privacy_system;
2280 compose_use_signing(compose, use_signing);
2281 compose_use_encryption(compose, use_encryption);
2282 compose_update_privacy_system_menu_item(compose, FALSE);
2284 activate_privacy_system(compose, account, FALSE);
2287 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2289 compose_extract_original_charset(compose);
2291 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2292 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2293 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2294 gchar queueheader_buf[BUFFSIZE];
2296 /* Set message save folder */
2297 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2298 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2299 compose_set_save_to(compose, &queueheader_buf[4]);
2301 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2302 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2304 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2309 if (compose_parse_header(compose, msginfo) < 0) {
2310 compose->updating = FALSE;
2311 compose_destroy(compose);
2314 compose_reedit_set_entry(compose, msginfo);
2316 textview = GTK_TEXT_VIEW(compose->text);
2317 textbuf = gtk_text_view_get_buffer(textview);
2318 compose_create_tags(textview, compose);
2320 mark = gtk_text_buffer_get_insert(textbuf);
2321 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2323 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2324 G_CALLBACK(compose_changed_cb),
2327 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2328 fp = procmime_get_first_encrypted_text_content(msginfo);
2330 compose_force_encryption(compose, account, TRUE, NULL);
2333 fp = procmime_get_first_text_content(msginfo);
2336 g_warning("Can't get text part\n");
2340 gboolean prev_autowrap = compose->autowrap;
2341 GtkTextBuffer *buffer = textbuf;
2343 while (fgets(buf, sizeof(buf), fp) != NULL) {
2345 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2351 compose_attach_parts(compose, msginfo);
2353 compose_colorize_signature(compose);
2355 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2356 G_CALLBACK(compose_changed_cb),
2359 if (manual_headers != NULL) {
2360 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2361 procheader_entries_free(manual_headers);
2362 compose->updating = FALSE;
2363 compose_destroy(compose);
2366 procheader_entries_free(manual_headers);
2369 gtk_widget_grab_focus(compose->text);
2371 if (prefs_common.auto_exteditor) {
2372 compose_exec_ext_editor(compose);
2374 compose->modified = FALSE;
2375 compose_set_title(compose);
2377 compose->updating = FALSE;
2378 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2379 SCROLL_TO_CURSOR(compose);
2381 if (compose->deferred_destroy) {
2382 compose_destroy(compose);
2386 compose->sig_str = account_get_signature_str(compose->account);
2388 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2393 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2400 cm_return_val_if_fail(msginfo != NULL, NULL);
2403 account = account_get_reply_account(msginfo,
2404 prefs_common.reply_account_autosel);
2405 cm_return_val_if_fail(account != NULL, NULL);
2407 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2409 compose->updating = TRUE;
2411 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2412 compose->replyinfo = NULL;
2413 compose->fwdinfo = NULL;
2415 compose_show_first_last_header(compose, TRUE);
2417 gtk_widget_grab_focus(compose->header_last->entry);
2419 filename = procmsg_get_message_file(msginfo);
2421 if (filename == NULL) {
2422 compose->updating = FALSE;
2423 compose_destroy(compose);
2428 compose->redirect_filename = filename;
2430 /* Set save folder */
2431 item = msginfo->folder;
2432 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2433 gchar *folderidentifier;
2435 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2436 folderidentifier = folder_item_get_identifier(item);
2437 compose_set_save_to(compose, folderidentifier);
2438 g_free(folderidentifier);
2441 compose_attach_parts(compose, msginfo);
2443 if (msginfo->subject)
2444 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2446 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2448 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2449 _("The body of the \"Redirect\" template has an error at line %d."));
2450 quote_fmt_reset_vartable();
2451 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2453 compose_colorize_signature(compose);
2456 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2457 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2458 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2460 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2461 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2462 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2463 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2464 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2465 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2466 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2467 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2469 if (compose->toolbar->draft_btn)
2470 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2471 if (compose->toolbar->insert_btn)
2472 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2473 if (compose->toolbar->attach_btn)
2474 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2475 if (compose->toolbar->sig_btn)
2476 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2477 if (compose->toolbar->exteditor_btn)
2478 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2479 if (compose->toolbar->linewrap_current_btn)
2480 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2481 if (compose->toolbar->linewrap_all_btn)
2482 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2484 compose->modified = FALSE;
2485 compose_set_title(compose);
2486 compose->updating = FALSE;
2487 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2488 SCROLL_TO_CURSOR(compose);
2490 if (compose->deferred_destroy) {
2491 compose_destroy(compose);
2495 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2500 GList *compose_get_compose_list(void)
2502 return compose_list;
2505 void compose_entry_append(Compose *compose, const gchar *address,
2506 ComposeEntryType type, ComposePrefType pref_type)
2508 const gchar *header;
2510 gboolean in_quote = FALSE;
2511 if (!address || *address == '\0') return;
2518 header = N_("Bcc:");
2520 case COMPOSE_REPLYTO:
2521 header = N_("Reply-To:");
2523 case COMPOSE_NEWSGROUPS:
2524 header = N_("Newsgroups:");
2526 case COMPOSE_FOLLOWUPTO:
2527 header = N_( "Followup-To:");
2529 case COMPOSE_INREPLYTO:
2530 header = N_( "In-Reply-To:");
2537 header = prefs_common_translated_header_name(header);
2539 cur = begin = (gchar *)address;
2541 /* we separate the line by commas, but not if we're inside a quoted
2543 while (*cur != '\0') {
2545 in_quote = !in_quote;
2546 if (*cur == ',' && !in_quote) {
2547 gchar *tmp = g_strdup(begin);
2549 tmp[cur-begin]='\0';
2552 while (*tmp == ' ' || *tmp == '\t')
2554 compose_add_header_entry(compose, header, tmp, pref_type);
2561 gchar *tmp = g_strdup(begin);
2563 tmp[cur-begin]='\0';
2566 while (*tmp == ' ' || *tmp == '\t')
2568 compose_add_header_entry(compose, header, tmp, pref_type);
2573 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2575 #if !GTK_CHECK_VERSION(3, 0, 0)
2576 static GdkColor yellow;
2577 static GdkColor black;
2578 static gboolean yellow_initialised = FALSE;
2580 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2581 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2586 #if !GTK_CHECK_VERSION(3, 0, 0)
2587 if (!yellow_initialised) {
2588 gdk_color_parse("#f5f6be", &yellow);
2589 gdk_color_parse("#000000", &black);
2590 yellow_initialised = gdk_colormap_alloc_color(
2591 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2592 yellow_initialised &= gdk_colormap_alloc_color(
2593 gdk_colormap_get_system(), &black, FALSE, TRUE);
2597 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2598 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2599 if (gtk_entry_get_text(entry) &&
2600 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2601 #if !GTK_CHECK_VERSION(3, 0, 0)
2602 if (yellow_initialised) {
2604 gtk_widget_modify_base(
2605 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2606 GTK_STATE_NORMAL, &yellow);
2607 gtk_widget_modify_text(
2608 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2609 GTK_STATE_NORMAL, &black);
2610 #if !GTK_CHECK_VERSION(3, 0, 0)
2617 void compose_toolbar_cb(gint action, gpointer data)
2619 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2620 Compose *compose = (Compose*)toolbar_item->parent;
2622 cm_return_if_fail(compose != NULL);
2626 compose_send_cb(NULL, compose);
2629 compose_send_later_cb(NULL, compose);
2632 compose_draft(compose, COMPOSE_QUIT_EDITING);
2635 compose_insert_file_cb(NULL, compose);
2638 compose_attach_cb(NULL, compose);
2641 compose_insert_sig(compose, FALSE);
2644 compose_ext_editor_cb(NULL, compose);
2646 case A_LINEWRAP_CURRENT:
2647 compose_beautify_paragraph(compose, NULL, TRUE);
2649 case A_LINEWRAP_ALL:
2650 compose_wrap_all_full(compose, TRUE);
2653 compose_address_cb(NULL, compose);
2656 case A_CHECK_SPELLING:
2657 compose_check_all(NULL, compose);
2665 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2670 gchar *subject = NULL;
2674 gchar **attach = NULL;
2675 gchar *inreplyto = NULL;
2676 MailField mfield = NO_FIELD_PRESENT;
2678 /* get mailto parts but skip from */
2679 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2682 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2683 mfield = TO_FIELD_PRESENT;
2686 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2688 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2690 if (!g_utf8_validate (subject, -1, NULL)) {
2691 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2692 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2695 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2697 mfield = SUBJECT_FIELD_PRESENT;
2700 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2701 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2704 gboolean prev_autowrap = compose->autowrap;
2706 compose->autowrap = FALSE;
2708 mark = gtk_text_buffer_get_insert(buffer);
2709 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2711 if (!g_utf8_validate (body, -1, NULL)) {
2712 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2713 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2716 gtk_text_buffer_insert(buffer, &iter, body, -1);
2718 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2720 compose->autowrap = prev_autowrap;
2721 if (compose->autowrap)
2722 compose_wrap_all(compose);
2723 mfield = BODY_FIELD_PRESENT;
2727 gint i = 0, att = 0;
2728 gchar *warn_files = NULL;
2729 while (attach[i] != NULL) {
2730 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2731 if (utf8_filename) {
2732 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2733 gchar *tmp = g_strdup_printf("%s%s\n",
2734 warn_files?warn_files:"",
2740 g_free(utf8_filename);
2742 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2747 alertpanel_notice(ngettext(
2748 "The following file has been attached: \n%s",
2749 "The following files have been attached: \n%s", att), warn_files);
2754 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2767 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2769 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2770 {"Cc:", NULL, TRUE},
2771 {"References:", NULL, FALSE},
2772 {"Bcc:", NULL, TRUE},
2773 {"Newsgroups:", NULL, TRUE},
2774 {"Followup-To:", NULL, TRUE},
2775 {"List-Post:", NULL, FALSE},
2776 {"X-Priority:", NULL, FALSE},
2777 {NULL, NULL, FALSE}};
2793 cm_return_val_if_fail(msginfo != NULL, -1);
2795 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2796 procheader_get_header_fields(fp, hentry);
2799 if (hentry[H_REPLY_TO].body != NULL) {
2800 if (hentry[H_REPLY_TO].body[0] != '\0') {
2802 conv_unmime_header(hentry[H_REPLY_TO].body,
2805 g_free(hentry[H_REPLY_TO].body);
2806 hentry[H_REPLY_TO].body = NULL;
2808 if (hentry[H_CC].body != NULL) {
2809 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2810 g_free(hentry[H_CC].body);
2811 hentry[H_CC].body = NULL;
2813 if (hentry[H_REFERENCES].body != NULL) {
2814 if (compose->mode == COMPOSE_REEDIT)
2815 compose->references = hentry[H_REFERENCES].body;
2817 compose->references = compose_parse_references
2818 (hentry[H_REFERENCES].body, msginfo->msgid);
2819 g_free(hentry[H_REFERENCES].body);
2821 hentry[H_REFERENCES].body = NULL;
2823 if (hentry[H_BCC].body != NULL) {
2824 if (compose->mode == COMPOSE_REEDIT)
2826 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2827 g_free(hentry[H_BCC].body);
2828 hentry[H_BCC].body = NULL;
2830 if (hentry[H_NEWSGROUPS].body != NULL) {
2831 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2832 hentry[H_NEWSGROUPS].body = NULL;
2834 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2835 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2836 compose->followup_to =
2837 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2840 g_free(hentry[H_FOLLOWUP_TO].body);
2841 hentry[H_FOLLOWUP_TO].body = NULL;
2843 if (hentry[H_LIST_POST].body != NULL) {
2844 gchar *to = NULL, *start = NULL;
2846 extract_address(hentry[H_LIST_POST].body);
2847 if (hentry[H_LIST_POST].body[0] != '\0') {
2848 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2850 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2851 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2854 g_free(compose->ml_post);
2855 compose->ml_post = to;
2858 g_free(hentry[H_LIST_POST].body);
2859 hentry[H_LIST_POST].body = NULL;
2862 /* CLAWS - X-Priority */
2863 if (compose->mode == COMPOSE_REEDIT)
2864 if (hentry[H_X_PRIORITY].body != NULL) {
2867 priority = atoi(hentry[H_X_PRIORITY].body);
2868 g_free(hentry[H_X_PRIORITY].body);
2870 hentry[H_X_PRIORITY].body = NULL;
2872 if (priority < PRIORITY_HIGHEST ||
2873 priority > PRIORITY_LOWEST)
2874 priority = PRIORITY_NORMAL;
2876 compose->priority = priority;
2879 if (compose->mode == COMPOSE_REEDIT) {
2880 if (msginfo->inreplyto && *msginfo->inreplyto)
2881 compose->inreplyto = g_strdup(msginfo->inreplyto);
2885 if (msginfo->msgid && *msginfo->msgid)
2886 compose->inreplyto = g_strdup(msginfo->msgid);
2888 if (!compose->references) {
2889 if (msginfo->msgid && *msginfo->msgid) {
2890 if (msginfo->inreplyto && *msginfo->inreplyto)
2891 compose->references =
2892 g_strdup_printf("<%s>\n\t<%s>",
2896 compose->references =
2897 g_strconcat("<", msginfo->msgid, ">",
2899 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2900 compose->references =
2901 g_strconcat("<", msginfo->inreplyto, ">",
2909 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2914 cm_return_val_if_fail(msginfo != NULL, -1);
2916 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2917 procheader_get_header_fields(fp, entries);
2921 while (he != NULL && he->name != NULL) {
2923 GtkListStore *model = NULL;
2925 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
2926 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
2927 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
2928 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
2929 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
2936 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2938 GSList *ref_id_list, *cur;
2942 ref_id_list = references_list_append(NULL, ref);
2943 if (!ref_id_list) return NULL;
2944 if (msgid && *msgid)
2945 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2950 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2951 /* "<" + Message-ID + ">" + CR+LF+TAB */
2952 len += strlen((gchar *)cur->data) + 5;
2954 if (len > MAX_REFERENCES_LEN) {
2955 /* remove second message-ID */
2956 if (ref_id_list && ref_id_list->next &&
2957 ref_id_list->next->next) {
2958 g_free(ref_id_list->next->data);
2959 ref_id_list = g_slist_remove
2960 (ref_id_list, ref_id_list->next->data);
2962 slist_free_strings(ref_id_list);
2963 g_slist_free(ref_id_list);
2970 new_ref = g_string_new("");
2971 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2972 if (new_ref->len > 0)
2973 g_string_append(new_ref, "\n\t");
2974 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2977 slist_free_strings(ref_id_list);
2978 g_slist_free(ref_id_list);
2980 new_ref_str = new_ref->str;
2981 g_string_free(new_ref, FALSE);
2986 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2987 const gchar *fmt, const gchar *qmark,
2988 const gchar *body, gboolean rewrap,
2989 gboolean need_unescape,
2990 const gchar *err_msg)
2992 MsgInfo* dummyinfo = NULL;
2993 gchar *quote_str = NULL;
2995 gboolean prev_autowrap;
2996 const gchar *trimmed_body = body;
2997 gint cursor_pos = -1;
2998 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2999 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3004 SIGNAL_BLOCK(buffer);
3007 dummyinfo = compose_msginfo_new_from_compose(compose);
3008 msginfo = dummyinfo;
3011 if (qmark != NULL) {
3013 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3014 compose->gtkaspell);
3016 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3018 quote_fmt_scan_string(qmark);
3021 buf = quote_fmt_get_buffer();
3023 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3025 Xstrdup_a(quote_str, buf, goto error)
3028 if (fmt && *fmt != '\0') {
3031 while (*trimmed_body == '\n')
3035 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3036 compose->gtkaspell);
3038 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3040 if (need_unescape) {
3043 /* decode \-escape sequences in the internal representation of the quote format */
3044 tmp = g_malloc(strlen(fmt)+1);
3045 pref_get_unescaped_pref(tmp, fmt);
3046 quote_fmt_scan_string(tmp);
3050 quote_fmt_scan_string(fmt);
3054 buf = quote_fmt_get_buffer();
3056 gint line = quote_fmt_get_line();
3057 alertpanel_error(err_msg, line);
3063 prev_autowrap = compose->autowrap;
3064 compose->autowrap = FALSE;
3066 mark = gtk_text_buffer_get_insert(buffer);
3067 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3068 if (g_utf8_validate(buf, -1, NULL)) {
3069 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3071 gchar *tmpout = NULL;
3072 tmpout = conv_codeset_strdup
3073 (buf, conv_get_locale_charset_str_no_utf8(),
3075 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3077 tmpout = g_malloc(strlen(buf)*2+1);
3078 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3080 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3084 cursor_pos = quote_fmt_get_cursor_pos();
3085 if (cursor_pos == -1)
3086 cursor_pos = gtk_text_iter_get_offset(&iter);
3087 compose->set_cursor_pos = cursor_pos;
3089 gtk_text_buffer_get_start_iter(buffer, &iter);
3090 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3091 gtk_text_buffer_place_cursor(buffer, &iter);
3093 compose->autowrap = prev_autowrap;
3094 if (compose->autowrap && rewrap)
3095 compose_wrap_all(compose);
3102 SIGNAL_UNBLOCK(buffer);
3104 procmsg_msginfo_free( dummyinfo );
3109 /* if ml_post is of type addr@host and from is of type
3110 * addr-anything@host, return TRUE
3112 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3114 gchar *left_ml = NULL;
3115 gchar *right_ml = NULL;
3116 gchar *left_from = NULL;
3117 gchar *right_from = NULL;
3118 gboolean result = FALSE;
3120 if (!ml_post || !from)
3123 left_ml = g_strdup(ml_post);
3124 if (strstr(left_ml, "@")) {
3125 right_ml = strstr(left_ml, "@")+1;
3126 *(strstr(left_ml, "@")) = '\0';
3129 left_from = g_strdup(from);
3130 if (strstr(left_from, "@")) {
3131 right_from = strstr(left_from, "@")+1;
3132 *(strstr(left_from, "@")) = '\0';
3135 if (left_ml && left_from && right_ml && right_from
3136 && !strncmp(left_from, left_ml, strlen(left_ml))
3137 && !strcmp(right_from, right_ml)) {
3146 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3147 gboolean respect_default_to)
3151 if (!folder || !folder->prefs)
3154 if (respect_default_to && folder->prefs->enable_default_to) {
3155 compose_entry_append(compose, folder->prefs->default_to,
3156 COMPOSE_TO, PREF_FOLDER);
3157 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3159 if (folder->prefs->enable_default_cc)
3160 compose_entry_append(compose, folder->prefs->default_cc,
3161 COMPOSE_CC, PREF_FOLDER);
3162 if (folder->prefs->enable_default_bcc)
3163 compose_entry_append(compose, folder->prefs->default_bcc,
3164 COMPOSE_BCC, PREF_FOLDER);
3165 if (folder->prefs->enable_default_replyto)
3166 compose_entry_append(compose, folder->prefs->default_replyto,
3167 COMPOSE_REPLYTO, PREF_FOLDER);
3170 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3175 if (!compose || !msginfo)
3178 if (msginfo->subject && *msginfo->subject) {
3179 buf = p = g_strdup(msginfo->subject);
3180 p += subject_get_prefix_length(p);
3181 memmove(buf, p, strlen(p) + 1);
3183 buf2 = g_strdup_printf("Re: %s", buf);
3184 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3189 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3192 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3193 gboolean to_all, gboolean to_ml,
3195 gboolean followup_and_reply_to)
3197 GSList *cc_list = NULL;
3200 gchar *replyto = NULL;
3201 gchar *ac_email = NULL;
3203 gboolean reply_to_ml = FALSE;
3204 gboolean default_reply_to = FALSE;
3206 cm_return_if_fail(compose->account != NULL);
3207 cm_return_if_fail(msginfo != NULL);
3209 reply_to_ml = to_ml && compose->ml_post;
3211 default_reply_to = msginfo->folder &&
3212 msginfo->folder->prefs->enable_default_reply_to;
3214 if (compose->account->protocol != A_NNTP) {
3215 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3217 if (reply_to_ml && !default_reply_to) {
3219 gboolean is_subscr = is_subscription(compose->ml_post,
3222 /* normal answer to ml post with a reply-to */
3223 compose_entry_append(compose,
3225 COMPOSE_TO, PREF_ML);
3226 if (compose->replyto)
3227 compose_entry_append(compose,
3229 COMPOSE_CC, PREF_ML);
3231 /* answer to subscription confirmation */
3232 if (compose->replyto)
3233 compose_entry_append(compose,
3235 COMPOSE_TO, PREF_ML);
3236 else if (msginfo->from)
3237 compose_entry_append(compose,
3239 COMPOSE_TO, PREF_ML);
3242 else if (!(to_all || to_sender) && default_reply_to) {
3243 compose_entry_append(compose,
3244 msginfo->folder->prefs->default_reply_to,
3245 COMPOSE_TO, PREF_FOLDER);
3246 compose_entry_mark_default_to(compose,
3247 msginfo->folder->prefs->default_reply_to);
3252 Xstrdup_a(tmp1, msginfo->from, return);
3253 extract_address(tmp1);
3254 if (to_all || to_sender ||
3255 !account_find_from_address(tmp1, FALSE))
3256 compose_entry_append(compose,
3257 (compose->replyto && !to_sender)
3258 ? compose->replyto :
3259 msginfo->from ? msginfo->from : "",
3260 COMPOSE_TO, PREF_NONE);
3261 else if (!to_all && !to_sender) {
3262 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3263 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3264 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3265 if (compose->replyto) {
3266 compose_entry_append(compose,
3268 COMPOSE_TO, PREF_NONE);
3270 compose_entry_append(compose,
3271 msginfo->from ? msginfo->from : "",
3272 COMPOSE_TO, PREF_NONE);
3275 /* replying to own mail, use original recp */
3276 compose_entry_append(compose,
3277 msginfo->to ? msginfo->to : "",
3278 COMPOSE_TO, PREF_NONE);
3279 compose_entry_append(compose,
3280 msginfo->cc ? msginfo->cc : "",
3281 COMPOSE_CC, PREF_NONE);
3286 if (to_sender || (compose->followup_to &&
3287 !strncmp(compose->followup_to, "poster", 6)))
3288 compose_entry_append
3290 (compose->replyto ? compose->replyto :
3291 msginfo->from ? msginfo->from : ""),
3292 COMPOSE_TO, PREF_NONE);
3294 else if (followup_and_reply_to || to_all) {
3295 compose_entry_append
3297 (compose->replyto ? compose->replyto :
3298 msginfo->from ? msginfo->from : ""),
3299 COMPOSE_TO, PREF_NONE);
3301 compose_entry_append
3303 compose->followup_to ? compose->followup_to :
3304 compose->newsgroups ? compose->newsgroups : "",
3305 COMPOSE_NEWSGROUPS, PREF_NONE);
3308 compose_entry_append
3310 compose->followup_to ? compose->followup_to :
3311 compose->newsgroups ? compose->newsgroups : "",
3312 COMPOSE_NEWSGROUPS, PREF_NONE);
3314 compose_reply_set_subject(compose, msginfo);
3316 if (to_ml && compose->ml_post) return;
3317 if (!to_all || compose->account->protocol == A_NNTP) return;
3319 if (compose->replyto) {
3320 Xstrdup_a(replyto, compose->replyto, return);
3321 extract_address(replyto);
3323 if (msginfo->from) {
3324 Xstrdup_a(from, msginfo->from, return);
3325 extract_address(from);
3328 if (replyto && from)
3329 cc_list = address_list_append_with_comments(cc_list, from);
3330 if (to_all && msginfo->folder &&
3331 msginfo->folder->prefs->enable_default_reply_to)
3332 cc_list = address_list_append_with_comments(cc_list,
3333 msginfo->folder->prefs->default_reply_to);
3334 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3335 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3337 ac_email = g_utf8_strdown(compose->account->address, -1);
3340 for (cur = cc_list; cur != NULL; cur = cur->next) {
3341 gchar *addr = g_utf8_strdown(cur->data, -1);
3342 extract_address(addr);
3344 if (strcmp(ac_email, addr))
3345 compose_entry_append(compose, (gchar *)cur->data,
3346 COMPOSE_CC, PREF_NONE);
3348 debug_print("Cc address same as compose account's, ignoring\n");
3353 slist_free_strings(cc_list);
3354 g_slist_free(cc_list);
3360 #define SET_ENTRY(entry, str) \
3363 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3366 #define SET_ADDRESS(type, str) \
3369 compose_entry_append(compose, str, type, PREF_NONE); \
3372 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3374 cm_return_if_fail(msginfo != NULL);
3376 SET_ENTRY(subject_entry, msginfo->subject);
3377 SET_ENTRY(from_name, msginfo->from);
3378 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3379 SET_ADDRESS(COMPOSE_CC, compose->cc);
3380 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3381 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3382 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3383 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3385 compose_update_priority_menu_item(compose);
3386 compose_update_privacy_system_menu_item(compose, FALSE);
3387 compose_show_first_last_header(compose, TRUE);
3393 static void compose_insert_sig(Compose *compose, gboolean replace)
3395 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3396 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3398 GtkTextIter iter, iter_end;
3399 gint cur_pos, ins_pos;
3400 gboolean prev_autowrap;
3401 gboolean found = FALSE;
3402 gboolean exists = FALSE;
3404 cm_return_if_fail(compose->account != NULL);
3408 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3409 G_CALLBACK(compose_changed_cb),
3412 mark = gtk_text_buffer_get_insert(buffer);
3413 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3414 cur_pos = gtk_text_iter_get_offset (&iter);
3417 gtk_text_buffer_get_end_iter(buffer, &iter);
3419 exists = (compose->sig_str != NULL);
3422 GtkTextIter first_iter, start_iter, end_iter;
3424 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3426 if (!exists || compose->sig_str[0] == '\0')
3429 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3430 compose->signature_tag);
3433 /* include previous \n\n */
3434 gtk_text_iter_backward_chars(&first_iter, 1);
3435 start_iter = first_iter;
3436 end_iter = first_iter;
3438 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3439 compose->signature_tag);
3440 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3441 compose->signature_tag);
3443 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3449 g_free(compose->sig_str);
3450 compose->sig_str = account_get_signature_str(compose->account);
3452 cur_pos = gtk_text_iter_get_offset(&iter);
3454 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3455 g_free(compose->sig_str);
3456 compose->sig_str = NULL;
3458 if (compose->sig_inserted == FALSE)
3459 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3460 compose->sig_inserted = TRUE;
3462 cur_pos = gtk_text_iter_get_offset(&iter);
3463 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3465 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3466 gtk_text_iter_forward_chars(&iter, 1);
3467 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3468 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3470 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3471 cur_pos = gtk_text_buffer_get_char_count (buffer);
3474 /* put the cursor where it should be
3475 * either where the quote_fmt says, either where it was */
3476 if (compose->set_cursor_pos < 0)
3477 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3479 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3480 compose->set_cursor_pos);
3482 compose->set_cursor_pos = -1;
3483 gtk_text_buffer_place_cursor(buffer, &iter);
3484 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3485 G_CALLBACK(compose_changed_cb),
3491 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3494 GtkTextBuffer *buffer;
3497 const gchar *cur_encoding;
3498 gchar buf[BUFFSIZE];
3501 gboolean prev_autowrap;
3502 gboolean badtxt = FALSE;
3503 struct stat file_stat;
3506 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3508 /* get the size of the file we are about to insert */
3509 ret = g_stat(file, &file_stat);
3511 gchar *shortfile = g_path_get_basename(file);
3512 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3514 return COMPOSE_INSERT_NO_FILE;
3515 } else if (prefs_common.warn_large_insert == TRUE) {
3517 /* ask user for confirmation if the file is large */
3518 if (prefs_common.warn_large_insert_size < 0 ||
3519 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3523 msg = g_strdup_printf(_("You are about to insert a file of %s "
3524 "in the message body. Are you sure you want to do that?"),
3525 to_human_readable(file_stat.st_size));
3526 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3527 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3530 /* do we ask for confirmation next time? */
3531 if (aval & G_ALERTDISABLE) {
3532 /* no confirmation next time, disable feature in preferences */
3533 aval &= ~G_ALERTDISABLE;
3534 prefs_common.warn_large_insert = FALSE;
3537 /* abort file insertion if user canceled action */
3538 if (aval != G_ALERTALTERNATE) {
3539 return COMPOSE_INSERT_NO_FILE;
3545 if ((fp = g_fopen(file, "rb")) == NULL) {
3546 FILE_OP_ERROR(file, "fopen");
3547 return COMPOSE_INSERT_READ_ERROR;
3550 prev_autowrap = compose->autowrap;
3551 compose->autowrap = FALSE;
3553 text = GTK_TEXT_VIEW(compose->text);
3554 buffer = gtk_text_view_get_buffer(text);
3555 mark = gtk_text_buffer_get_insert(buffer);
3556 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3558 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3559 G_CALLBACK(text_inserted),
3562 cur_encoding = conv_get_locale_charset_str_no_utf8();
3564 while (fgets(buf, sizeof(buf), fp) != NULL) {
3567 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3568 str = g_strdup(buf);
3570 str = conv_codeset_strdup
3571 (buf, cur_encoding, CS_INTERNAL);
3574 /* strip <CR> if DOS/Windows file,
3575 replace <CR> with <LF> if Macintosh file. */
3578 if (len > 0 && str[len - 1] != '\n') {
3580 if (str[len] == '\r') str[len] = '\n';
3583 gtk_text_buffer_insert(buffer, &iter, str, -1);
3587 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3588 G_CALLBACK(text_inserted),
3590 compose->autowrap = prev_autowrap;
3591 if (compose->autowrap)
3592 compose_wrap_all(compose);
3597 return COMPOSE_INSERT_INVALID_CHARACTER;
3599 return COMPOSE_INSERT_SUCCESS;
3602 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3603 const gchar *filename,
3604 const gchar *content_type,
3605 const gchar *charset)
3613 GtkListStore *store;
3615 gboolean has_binary = FALSE;
3617 if (!is_file_exist(file)) {
3618 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3619 gboolean result = FALSE;
3620 if (file_from_uri && is_file_exist(file_from_uri)) {
3621 result = compose_attach_append(
3622 compose, file_from_uri,
3623 filename, content_type,
3626 g_free(file_from_uri);
3629 alertpanel_error("File %s doesn't exist\n", filename);
3632 if ((size = get_file_size(file)) < 0) {
3633 alertpanel_error("Can't get file size of %s\n", filename);
3637 alertpanel_error(_("File %s is empty."), filename);
3640 if ((fp = g_fopen(file, "rb")) == NULL) {
3641 alertpanel_error(_("Can't read %s."), filename);
3646 ainfo = g_new0(AttachInfo, 1);
3647 auto_ainfo = g_auto_pointer_new_with_free
3648 (ainfo, (GFreeFunc) compose_attach_info_free);
3649 ainfo->file = g_strdup(file);
3652 ainfo->content_type = g_strdup(content_type);
3653 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3655 MsgFlags flags = {0, 0};
3657 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3658 ainfo->encoding = ENC_7BIT;
3660 ainfo->encoding = ENC_8BIT;
3662 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3663 if (msginfo && msginfo->subject)
3664 name = g_strdup(msginfo->subject);
3666 name = g_path_get_basename(filename ? filename : file);
3668 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3670 procmsg_msginfo_free(msginfo);
3672 if (!g_ascii_strncasecmp(content_type, "text/", 5)) {
3673 ainfo->charset = g_strdup(charset);
3674 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3676 ainfo->encoding = ENC_BASE64;
3678 name = g_path_get_basename(filename ? filename : file);
3679 ainfo->name = g_strdup(name);
3683 ainfo->content_type = procmime_get_mime_type(file);
3684 if (!ainfo->content_type) {
3685 ainfo->content_type =
3686 g_strdup("application/octet-stream");
3687 ainfo->encoding = ENC_BASE64;
3688 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text/", 5))
3690 procmime_get_encoding_for_text_file(file, &has_binary);
3692 ainfo->encoding = ENC_BASE64;
3693 name = g_path_get_basename(filename ? filename : file);
3694 ainfo->name = g_strdup(name);
3698 if (ainfo->name != NULL
3699 && !strcmp(ainfo->name, ".")) {
3700 g_free(ainfo->name);
3704 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3705 g_free(ainfo->content_type);
3706 ainfo->content_type = g_strdup("application/octet-stream");
3707 g_free(ainfo->charset);
3708 ainfo->charset = NULL;
3711 ainfo->size = (goffset)size;
3712 size_text = to_human_readable((goffset)size);
3714 store = GTK_LIST_STORE(gtk_tree_view_get_model
3715 (GTK_TREE_VIEW(compose->attach_clist)));
3717 gtk_list_store_append(store, &iter);
3718 gtk_list_store_set(store, &iter,
3719 COL_MIMETYPE, ainfo->content_type,
3720 COL_SIZE, size_text,
3721 COL_NAME, ainfo->name,
3722 COL_CHARSET, ainfo->charset,
3724 COL_AUTODATA, auto_ainfo,
3727 g_auto_pointer_free(auto_ainfo);
3728 compose_attach_update_label(compose);
3732 static void compose_use_signing(Compose *compose, gboolean use_signing)
3734 compose->use_signing = use_signing;
3735 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3738 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3740 compose->use_encryption = use_encryption;
3741 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3744 #define NEXT_PART_NOT_CHILD(info) \
3746 node = info->node; \
3747 while (node->children) \
3748 node = g_node_last_child(node); \
3749 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3752 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3756 MimeInfo *firsttext = NULL;
3757 MimeInfo *encrypted = NULL;
3760 const gchar *partname = NULL;
3762 mimeinfo = procmime_scan_message(msginfo);
3763 if (!mimeinfo) return;
3765 if (mimeinfo->node->children == NULL) {
3766 procmime_mimeinfo_free_all(mimeinfo);
3770 /* find first content part */
3771 child = (MimeInfo *) mimeinfo->node->children->data;
3772 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3773 child = (MimeInfo *)child->node->children->data;
3776 if (child->type == MIMETYPE_TEXT) {
3778 debug_print("First text part found\n");
3779 } else if (compose->mode == COMPOSE_REEDIT &&
3780 child->type == MIMETYPE_APPLICATION &&
3781 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3782 encrypted = (MimeInfo *)child->node->parent->data;
3785 child = (MimeInfo *) mimeinfo->node->children->data;
3786 while (child != NULL) {
3789 if (child == encrypted) {
3790 /* skip this part of tree */
3791 NEXT_PART_NOT_CHILD(child);
3795 if (child->type == MIMETYPE_MULTIPART) {
3796 /* get the actual content */
3797 child = procmime_mimeinfo_next(child);
3801 if (child == firsttext) {
3802 child = procmime_mimeinfo_next(child);
3806 outfile = procmime_get_tmp_file_name(child);
3807 if ((err = procmime_get_part(outfile, child)) < 0)
3808 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3810 gchar *content_type;
3812 content_type = procmime_get_content_type_str(child->type, child->subtype);
3814 /* if we meet a pgp signature, we don't attach it, but
3815 * we force signing. */
3816 if ((strcmp(content_type, "application/pgp-signature") &&
3817 strcmp(content_type, "application/pkcs7-signature") &&
3818 strcmp(content_type, "application/x-pkcs7-signature"))
3819 || compose->mode == COMPOSE_REDIRECT) {
3820 partname = procmime_mimeinfo_get_parameter(child, "filename");
3821 if (partname == NULL)
3822 partname = procmime_mimeinfo_get_parameter(child, "name");
3823 if (partname == NULL)
3825 compose_attach_append(compose, outfile,
3826 partname, content_type,
3827 procmime_mimeinfo_get_parameter(child, "charset"));
3829 compose_force_signing(compose, compose->account, NULL);
3831 g_free(content_type);
3834 NEXT_PART_NOT_CHILD(child);
3836 procmime_mimeinfo_free_all(mimeinfo);
3839 #undef NEXT_PART_NOT_CHILD
3844 WAIT_FOR_INDENT_CHAR,
3845 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3848 /* return indent length, we allow:
3849 indent characters followed by indent characters or spaces/tabs,
3850 alphabets and numbers immediately followed by indent characters,
3851 and the repeating sequences of the above
3852 If quote ends with multiple spaces, only the first one is included. */
3853 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3854 const GtkTextIter *start, gint *len)
3856 GtkTextIter iter = *start;
3860 IndentState state = WAIT_FOR_INDENT_CHAR;
3863 gint alnum_count = 0;
3864 gint space_count = 0;
3867 if (prefs_common.quote_chars == NULL) {
3871 while (!gtk_text_iter_ends_line(&iter)) {
3872 wc = gtk_text_iter_get_char(&iter);
3873 if (g_unichar_iswide(wc))
3875 clen = g_unichar_to_utf8(wc, ch);
3879 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3880 is_space = g_unichar_isspace(wc);
3882 if (state == WAIT_FOR_INDENT_CHAR) {
3883 if (!is_indent && !g_unichar_isalnum(wc))
3886 quote_len += alnum_count + space_count + 1;
3887 alnum_count = space_count = 0;
3888 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3891 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3892 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3896 else if (is_indent) {
3897 quote_len += alnum_count + space_count + 1;
3898 alnum_count = space_count = 0;
3901 state = WAIT_FOR_INDENT_CHAR;
3905 gtk_text_iter_forward_char(&iter);
3908 if (quote_len > 0 && space_count > 0)
3914 if (quote_len > 0) {
3916 gtk_text_iter_forward_chars(&iter, quote_len);
3917 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3923 /* return >0 if the line is itemized */
3924 static int compose_itemized_length(GtkTextBuffer *buffer,
3925 const GtkTextIter *start)
3927 GtkTextIter iter = *start;
3932 if (gtk_text_iter_ends_line(&iter))
3937 wc = gtk_text_iter_get_char(&iter);
3938 if (!g_unichar_isspace(wc))
3940 gtk_text_iter_forward_char(&iter);
3941 if (gtk_text_iter_ends_line(&iter))
3945 clen = g_unichar_to_utf8(wc, ch);
3949 if (!strchr("*-+", ch[0]))
3952 gtk_text_iter_forward_char(&iter);
3953 if (gtk_text_iter_ends_line(&iter))
3955 wc = gtk_text_iter_get_char(&iter);
3956 if (g_unichar_isspace(wc)) {
3962 /* return the string at the start of the itemization */
3963 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3964 const GtkTextIter *start)
3966 GtkTextIter iter = *start;
3969 GString *item_chars = g_string_new("");
3972 if (gtk_text_iter_ends_line(&iter))
3977 wc = gtk_text_iter_get_char(&iter);
3978 if (!g_unichar_isspace(wc))
3980 gtk_text_iter_forward_char(&iter);
3981 if (gtk_text_iter_ends_line(&iter))
3983 g_string_append_unichar(item_chars, wc);
3986 str = item_chars->str;
3987 g_string_free(item_chars, FALSE);
3991 /* return the number of spaces at a line's start */
3992 static int compose_left_offset_length(GtkTextBuffer *buffer,
3993 const GtkTextIter *start)
3995 GtkTextIter iter = *start;
3998 if (gtk_text_iter_ends_line(&iter))
4002 wc = gtk_text_iter_get_char(&iter);
4003 if (!g_unichar_isspace(wc))
4006 gtk_text_iter_forward_char(&iter);
4007 if (gtk_text_iter_ends_line(&iter))
4011 gtk_text_iter_forward_char(&iter);
4012 if (gtk_text_iter_ends_line(&iter))
4017 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
4018 const GtkTextIter *start,
4019 GtkTextIter *break_pos,
4023 GtkTextIter iter = *start, line_end = *start;
4024 PangoLogAttr *attrs;
4031 gboolean can_break = FALSE;
4032 gboolean do_break = FALSE;
4033 gboolean was_white = FALSE;
4034 gboolean prev_dont_break = FALSE;
4036 gtk_text_iter_forward_to_line_end(&line_end);
4037 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
4038 len = g_utf8_strlen(str, -1);
4042 g_warning("compose_get_line_break_pos: len = 0!\n");
4046 /* g_print("breaking line: %d: %s (len = %d)\n",
4047 gtk_text_iter_get_line(&iter), str, len); */
4049 attrs = g_new(PangoLogAttr, len + 1);
4051 pango_default_break(str, -1, NULL, attrs, len + 1);
4055 /* skip quote and leading spaces */
4056 for (i = 0; *p != '\0' && i < len; i++) {
4059 wc = g_utf8_get_char(p);
4060 if (i >= quote_len && !g_unichar_isspace(wc))
4062 if (g_unichar_iswide(wc))
4064 else if (*p == '\t')
4068 p = g_utf8_next_char(p);
4071 for (; *p != '\0' && i < len; i++) {
4072 PangoLogAttr *attr = attrs + i;
4076 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
4079 was_white = attr->is_white;
4081 /* don't wrap URI */
4082 if ((uri_len = get_uri_len(p)) > 0) {
4084 if (pos > 0 && col > max_col) {
4094 wc = g_utf8_get_char(p);
4095 if (g_unichar_iswide(wc)) {
4097 if (prev_dont_break && can_break && attr->is_line_break)
4099 } else if (*p == '\t')
4103 if (pos > 0 && col > max_col) {
4108 if (*p == '-' || *p == '/')
4109 prev_dont_break = TRUE;
4111 prev_dont_break = FALSE;
4113 p = g_utf8_next_char(p);
4117 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4122 *break_pos = *start;
4123 gtk_text_iter_set_line_offset(break_pos, pos);
4128 static gboolean compose_join_next_line(Compose *compose,
4129 GtkTextBuffer *buffer,
4131 const gchar *quote_str)
4133 GtkTextIter iter_ = *iter, cur, prev, next, end;
4134 PangoLogAttr attrs[3];
4136 gchar *next_quote_str;
4139 gboolean keep_cursor = FALSE;
4141 if (!gtk_text_iter_forward_line(&iter_) ||
4142 gtk_text_iter_ends_line(&iter_)) {
4145 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4147 if ((quote_str || next_quote_str) &&
4148 strcmp2(quote_str, next_quote_str) != 0) {
4149 g_free(next_quote_str);
4152 g_free(next_quote_str);
4155 if (quote_len > 0) {
4156 gtk_text_iter_forward_chars(&end, quote_len);
4157 if (gtk_text_iter_ends_line(&end)) {
4162 /* don't join itemized lines */
4163 if (compose_itemized_length(buffer, &end) > 0) {
4167 /* don't join signature separator */
4168 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4171 /* delete quote str */
4173 gtk_text_buffer_delete(buffer, &iter_, &end);
4175 /* don't join line breaks put by the user */
4177 gtk_text_iter_backward_char(&cur);
4178 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4179 gtk_text_iter_forward_char(&cur);
4183 gtk_text_iter_forward_char(&cur);
4184 /* delete linebreak and extra spaces */
4185 while (gtk_text_iter_backward_char(&cur)) {
4186 wc1 = gtk_text_iter_get_char(&cur);
4187 if (!g_unichar_isspace(wc1))
4192 while (!gtk_text_iter_ends_line(&cur)) {
4193 wc1 = gtk_text_iter_get_char(&cur);
4194 if (!g_unichar_isspace(wc1))
4196 gtk_text_iter_forward_char(&cur);
4199 if (!gtk_text_iter_equal(&prev, &next)) {
4202 mark = gtk_text_buffer_get_insert(buffer);
4203 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4204 if (gtk_text_iter_equal(&prev, &cur))
4206 gtk_text_buffer_delete(buffer, &prev, &next);
4210 /* insert space if required */
4211 gtk_text_iter_backward_char(&prev);
4212 wc1 = gtk_text_iter_get_char(&prev);
4213 wc2 = gtk_text_iter_get_char(&next);
4214 gtk_text_iter_forward_char(&next);
4215 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4216 pango_default_break(str, -1, NULL, attrs, 3);
4217 if (!attrs[1].is_line_break ||
4218 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4219 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4221 gtk_text_iter_backward_char(&iter_);
4222 gtk_text_buffer_place_cursor(buffer, &iter_);
4231 #define ADD_TXT_POS(bp_, ep_, pti_) \
4232 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4233 last = last->next; \
4234 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4235 last->next = NULL; \
4237 g_warning("alloc error scanning URIs\n"); \
4240 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4242 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4243 GtkTextBuffer *buffer;
4244 GtkTextIter iter, break_pos, end_of_line;
4245 gchar *quote_str = NULL;
4247 gboolean wrap_quote = prefs_common.linewrap_quote;
4248 gboolean prev_autowrap = compose->autowrap;
4249 gint startq_offset = -1, noq_offset = -1;
4250 gint uri_start = -1, uri_stop = -1;
4251 gint nouri_start = -1, nouri_stop = -1;
4252 gint num_blocks = 0;
4253 gint quotelevel = -1;
4254 gboolean modified = force;
4255 gboolean removed = FALSE;
4256 gboolean modified_before_remove = FALSE;
4258 gboolean start = TRUE;
4259 gint itemized_len = 0, rem_item_len = 0;
4260 gchar *itemized_chars = NULL;
4261 gboolean item_continuation = FALSE;
4266 if (compose->draft_timeout_tag == -2) {
4270 compose->autowrap = FALSE;
4272 buffer = gtk_text_view_get_buffer(text);
4273 undo_wrapping(compose->undostruct, TRUE);
4278 mark = gtk_text_buffer_get_insert(buffer);
4279 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4283 if (compose->draft_timeout_tag == -2) {
4284 if (gtk_text_iter_ends_line(&iter)) {
4285 while (gtk_text_iter_ends_line(&iter) &&
4286 gtk_text_iter_forward_line(&iter))
4289 while (gtk_text_iter_backward_line(&iter)) {
4290 if (gtk_text_iter_ends_line(&iter)) {
4291 gtk_text_iter_forward_line(&iter);
4297 /* move to line start */
4298 gtk_text_iter_set_line_offset(&iter, 0);
4301 itemized_len = compose_itemized_length(buffer, &iter);
4303 if (!itemized_len) {
4304 itemized_len = compose_left_offset_length(buffer, &iter);
4305 item_continuation = TRUE;
4309 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4311 /* go until paragraph end (empty line) */
4312 while (start || !gtk_text_iter_ends_line(&iter)) {
4313 gchar *scanpos = NULL;
4314 /* parse table - in order of priority */
4316 const gchar *needle; /* token */
4318 /* token search function */
4319 gchar *(*search) (const gchar *haystack,
4320 const gchar *needle);
4321 /* part parsing function */
4322 gboolean (*parse) (const gchar *start,
4323 const gchar *scanpos,
4327 /* part to URI function */
4328 gchar *(*build_uri) (const gchar *bp,
4332 static struct table parser[] = {
4333 {"http://", strcasestr, get_uri_part, make_uri_string},
4334 {"https://", strcasestr, get_uri_part, make_uri_string},
4335 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4336 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4337 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4338 {"www.", strcasestr, get_uri_part, make_http_string},
4339 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4340 {"@", strcasestr, get_email_part, make_email_string}
4342 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4343 gint last_index = PARSE_ELEMS;
4345 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4349 if (!prev_autowrap && num_blocks == 0) {
4351 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4352 G_CALLBACK(text_inserted),
4355 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4358 uri_start = uri_stop = -1;
4360 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4363 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4364 if (startq_offset == -1)
4365 startq_offset = gtk_text_iter_get_offset(&iter);
4366 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4367 if (quotelevel > 2) {
4368 /* recycle colors */
4369 if (prefs_common.recycle_quote_colors)
4378 if (startq_offset == -1)
4379 noq_offset = gtk_text_iter_get_offset(&iter);
4383 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4386 if (gtk_text_iter_ends_line(&iter)) {
4388 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4389 prefs_common.linewrap_len,
4391 GtkTextIter prev, next, cur;
4392 if (prev_autowrap != FALSE || force) {
4393 compose->automatic_break = TRUE;
4395 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4396 compose->automatic_break = FALSE;
4397 if (itemized_len && compose->autoindent) {
4398 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4399 if (!item_continuation)
4400 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4402 } else if (quote_str && wrap_quote) {
4403 compose->automatic_break = TRUE;
4405 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4406 compose->automatic_break = FALSE;
4407 if (itemized_len && compose->autoindent) {
4408 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4409 if (!item_continuation)
4410 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4414 /* remove trailing spaces */
4416 rem_item_len = itemized_len;
4417 while (compose->autoindent && rem_item_len-- > 0)
4418 gtk_text_iter_backward_char(&cur);
4419 gtk_text_iter_backward_char(&cur);
4422 while (!gtk_text_iter_starts_line(&cur)) {
4425 gtk_text_iter_backward_char(&cur);
4426 wc = gtk_text_iter_get_char(&cur);
4427 if (!g_unichar_isspace(wc))
4431 if (!gtk_text_iter_equal(&prev, &next)) {
4432 gtk_text_buffer_delete(buffer, &prev, &next);
4434 gtk_text_iter_forward_char(&break_pos);
4438 gtk_text_buffer_insert(buffer, &break_pos,
4442 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4444 /* move iter to current line start */
4445 gtk_text_iter_set_line_offset(&iter, 0);
4452 /* move iter to next line start */
4458 if (!prev_autowrap && num_blocks > 0) {
4460 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4461 G_CALLBACK(text_inserted),
4465 while (!gtk_text_iter_ends_line(&end_of_line)) {
4466 gtk_text_iter_forward_char(&end_of_line);
4468 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4470 nouri_start = gtk_text_iter_get_offset(&iter);
4471 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4473 walk_pos = gtk_text_iter_get_offset(&iter);
4474 /* FIXME: this looks phony. scanning for anything in the parse table */
4475 for (n = 0; n < PARSE_ELEMS; n++) {
4478 tmp = parser[n].search(walk, parser[n].needle);
4480 if (scanpos == NULL || tmp < scanpos) {
4489 /* check if URI can be parsed */
4490 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4491 (const gchar **)&ep, FALSE)
4492 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4496 strlen(parser[last_index].needle);
4499 uri_start = walk_pos + (bp - o_walk);
4500 uri_stop = walk_pos + (ep - o_walk);
4504 gtk_text_iter_forward_line(&iter);
4507 if (startq_offset != -1) {
4508 GtkTextIter startquote, endquote;
4509 gtk_text_buffer_get_iter_at_offset(
4510 buffer, &startquote, startq_offset);
4513 switch (quotelevel) {
4515 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4516 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4517 gtk_text_buffer_apply_tag_by_name(
4518 buffer, "quote0", &startquote, &endquote);
4519 gtk_text_buffer_remove_tag_by_name(
4520 buffer, "quote1", &startquote, &endquote);
4521 gtk_text_buffer_remove_tag_by_name(
4522 buffer, "quote2", &startquote, &endquote);
4527 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4528 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4529 gtk_text_buffer_apply_tag_by_name(
4530 buffer, "quote1", &startquote, &endquote);
4531 gtk_text_buffer_remove_tag_by_name(
4532 buffer, "quote0", &startquote, &endquote);
4533 gtk_text_buffer_remove_tag_by_name(
4534 buffer, "quote2", &startquote, &endquote);
4539 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4540 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4541 gtk_text_buffer_apply_tag_by_name(
4542 buffer, "quote2", &startquote, &endquote);
4543 gtk_text_buffer_remove_tag_by_name(
4544 buffer, "quote0", &startquote, &endquote);
4545 gtk_text_buffer_remove_tag_by_name(
4546 buffer, "quote1", &startquote, &endquote);
4552 } else if (noq_offset != -1) {
4553 GtkTextIter startnoquote, endnoquote;
4554 gtk_text_buffer_get_iter_at_offset(
4555 buffer, &startnoquote, noq_offset);
4558 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4559 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4560 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4561 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4562 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4563 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4564 gtk_text_buffer_remove_tag_by_name(
4565 buffer, "quote0", &startnoquote, &endnoquote);
4566 gtk_text_buffer_remove_tag_by_name(
4567 buffer, "quote1", &startnoquote, &endnoquote);
4568 gtk_text_buffer_remove_tag_by_name(
4569 buffer, "quote2", &startnoquote, &endnoquote);
4575 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4576 GtkTextIter nouri_start_iter, nouri_end_iter;
4577 gtk_text_buffer_get_iter_at_offset(
4578 buffer, &nouri_start_iter, nouri_start);
4579 gtk_text_buffer_get_iter_at_offset(
4580 buffer, &nouri_end_iter, nouri_stop);
4581 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4582 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4583 gtk_text_buffer_remove_tag_by_name(
4584 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4585 modified_before_remove = modified;
4590 if (uri_start >= 0 && uri_stop > 0) {
4591 GtkTextIter uri_start_iter, uri_end_iter, back;
4592 gtk_text_buffer_get_iter_at_offset(
4593 buffer, &uri_start_iter, uri_start);
4594 gtk_text_buffer_get_iter_at_offset(
4595 buffer, &uri_end_iter, uri_stop);
4596 back = uri_end_iter;
4597 gtk_text_iter_backward_char(&back);
4598 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4599 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4600 gtk_text_buffer_apply_tag_by_name(
4601 buffer, "link", &uri_start_iter, &uri_end_iter);
4603 if (removed && !modified_before_remove) {
4609 // debug_print("not modified, out after %d lines\n", lines);
4613 // debug_print("modified, out after %d lines\n", lines);
4615 g_free(itemized_chars);
4618 undo_wrapping(compose->undostruct, FALSE);
4619 compose->autowrap = prev_autowrap;
4624 void compose_action_cb(void *data)
4626 Compose *compose = (Compose *)data;
4627 compose_wrap_all(compose);
4630 static void compose_wrap_all(Compose *compose)
4632 compose_wrap_all_full(compose, FALSE);
4635 static void compose_wrap_all_full(Compose *compose, gboolean force)
4637 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4638 GtkTextBuffer *buffer;
4640 gboolean modified = TRUE;
4642 buffer = gtk_text_view_get_buffer(text);
4644 gtk_text_buffer_get_start_iter(buffer, &iter);
4645 while (!gtk_text_iter_is_end(&iter) && modified)
4646 modified = compose_beautify_paragraph(compose, &iter, force);
4650 static void compose_set_title(Compose *compose)
4656 edited = compose->modified ? _(" [Edited]") : "";
4658 subject = gtk_editable_get_chars(
4659 GTK_EDITABLE(compose->subject_entry), 0, -1);
4661 #ifndef GENERIC_UMPC
4662 if (subject && strlen(subject))
4663 str = g_strdup_printf(_("%s - Compose message%s"),
4666 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4668 str = g_strdup(_("Compose message"));
4671 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4677 * compose_current_mail_account:
4679 * Find a current mail account (the currently selected account, or the
4680 * default account, if a news account is currently selected). If a
4681 * mail account cannot be found, display an error message.
4683 * Return value: Mail account, or NULL if not found.
4685 static PrefsAccount *
4686 compose_current_mail_account(void)
4690 if (cur_account && cur_account->protocol != A_NNTP)
4693 ac = account_get_default();
4694 if (!ac || ac->protocol == A_NNTP) {
4695 alertpanel_error(_("Account for sending mail is not specified.\n"
4696 "Please select a mail account before sending."));
4703 #define QUOTE_IF_REQUIRED(out, str) \
4705 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4709 len = strlen(str) + 3; \
4710 if ((__tmp = alloca(len)) == NULL) { \
4711 g_warning("can't allocate memory\n"); \
4712 g_string_free(header, TRUE); \
4715 g_snprintf(__tmp, len, "\"%s\"", str); \
4720 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4721 g_warning("can't allocate memory\n"); \
4722 g_string_free(header, TRUE); \
4725 strcpy(__tmp, str); \
4731 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4733 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4737 len = strlen(str) + 3; \
4738 if ((__tmp = alloca(len)) == NULL) { \
4739 g_warning("can't allocate memory\n"); \
4742 g_snprintf(__tmp, len, "\"%s\"", str); \
4747 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4748 g_warning("can't allocate memory\n"); \
4751 strcpy(__tmp, str); \
4757 static void compose_select_account(Compose *compose, PrefsAccount *account,
4760 gchar *from = NULL, *header;
4761 ComposeHeaderEntry *header_entry;
4763 cm_return_if_fail(account != NULL);
4765 compose->account = account;
4766 if (account->name && *account->name) {
4768 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4769 from = g_strdup_printf("%s <%s>",
4770 buf, account->address);
4771 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4773 from = g_strdup_printf("<%s>",
4775 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4780 compose_set_title(compose);
4782 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4783 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4785 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4786 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4787 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4789 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4791 activate_privacy_system(compose, account, FALSE);
4793 if (!init && compose->mode != COMPOSE_REDIRECT) {
4794 undo_block(compose->undostruct);
4795 compose_insert_sig(compose, TRUE);
4796 undo_unblock(compose->undostruct);
4799 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4800 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4802 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4803 if (account->protocol == A_NNTP) {
4804 if (!strcmp(header, _("To:")))
4805 combobox_select_by_text(
4806 GTK_COMBO_BOX(header_entry->combo),
4809 if (!strcmp(header, _("Newsgroups:")))
4810 combobox_select_by_text(
4811 GTK_COMBO_BOX(header_entry->combo),
4819 /* use account's dict info if set */
4820 if (compose->gtkaspell) {
4821 if (account->enable_default_dictionary)
4822 gtkaspell_change_dict(compose->gtkaspell,
4823 account->default_dictionary, FALSE);
4824 if (account->enable_default_alt_dictionary)
4825 gtkaspell_change_alt_dict(compose->gtkaspell,
4826 account->default_alt_dictionary);
4827 if (account->enable_default_dictionary
4828 || account->enable_default_alt_dictionary)
4829 compose_spell_menu_changed(compose);
4834 gboolean compose_check_for_valid_recipient(Compose *compose) {
4835 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4836 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4837 gboolean recipient_found = FALSE;
4841 /* free to and newsgroup list */
4842 slist_free_strings(compose->to_list);
4843 g_slist_free(compose->to_list);
4844 compose->to_list = NULL;
4846 slist_free_strings(compose->newsgroup_list);
4847 g_slist_free(compose->newsgroup_list);
4848 compose->newsgroup_list = NULL;
4850 /* search header entries for to and newsgroup entries */
4851 for (list = compose->header_list; list; list = list->next) {
4854 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4855 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4858 if (entry[0] != '\0') {
4859 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4860 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4861 compose->to_list = address_list_append(compose->to_list, entry);
4862 recipient_found = TRUE;
4865 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4866 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4867 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4868 recipient_found = TRUE;
4875 return recipient_found;
4878 static gboolean compose_check_for_set_recipients(Compose *compose)
4880 if (compose->account->set_autocc && compose->account->auto_cc) {
4881 gboolean found_other = FALSE;
4883 /* search header entries for to and newsgroup entries */
4884 for (list = compose->header_list; list; list = list->next) {
4887 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4888 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4891 if (strcmp(entry, compose->account->auto_cc)
4892 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4902 if (compose->batch) {
4903 gtk_widget_show_all(compose->window);
4905 aval = alertpanel(_("Send"),
4906 _("The only recipient is the default CC address. Send anyway?"),
4907 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4908 if (aval != G_ALERTALTERNATE)
4912 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4913 gboolean found_other = FALSE;
4915 /* search header entries for to and newsgroup entries */
4916 for (list = compose->header_list; list; list = list->next) {
4919 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4920 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4923 if (strcmp(entry, compose->account->auto_bcc)
4924 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4934 if (compose->batch) {
4935 gtk_widget_show_all(compose->window);
4937 aval = alertpanel(_("Send"),
4938 _("The only recipient is the default BCC address. Send anyway?"),
4939 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4940 if (aval != G_ALERTALTERNATE)
4947 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4951 if (compose_check_for_valid_recipient(compose) == FALSE) {
4952 if (compose->batch) {
4953 gtk_widget_show_all(compose->window);
4955 alertpanel_error(_("Recipient is not specified."));
4959 if (compose_check_for_set_recipients(compose) == FALSE) {
4963 if (!compose->batch) {
4964 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4965 if (*str == '\0' && check_everything == TRUE &&
4966 compose->mode != COMPOSE_REDIRECT) {
4968 gchar *button_label;
4971 if (compose->sending)
4972 button_label = _("+_Send");
4974 button_label = _("+_Queue");
4975 message = g_strdup_printf(_("Subject is empty. %s"),
4976 compose->sending?_("Send it anyway?"):
4977 _("Queue it anyway?"));
4979 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4980 GTK_STOCK_CANCEL, button_label, NULL);
4982 if (aval != G_ALERTALTERNATE)
4987 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4993 gint compose_send(Compose *compose)
4996 FolderItem *folder = NULL;
4998 gchar *msgpath = NULL;
4999 gboolean discard_window = FALSE;
5000 gchar *errstr = NULL;
5001 gchar *tmsgid = NULL;
5002 MainWindow *mainwin = mainwindow_get_mainwindow();
5003 gboolean queued_removed = FALSE;
5005 if (prefs_common.send_dialog_invisible
5006 || compose->batch == TRUE)
5007 discard_window = TRUE;
5009 compose_allow_user_actions (compose, FALSE);
5010 compose->sending = TRUE;
5012 if (compose_check_entries(compose, TRUE) == FALSE) {
5013 if (compose->batch) {
5014 gtk_widget_show_all(compose->window);
5020 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
5023 if (compose->batch) {
5024 gtk_widget_show_all(compose->window);
5027 alertpanel_error(_("Could not queue message for sending:\n\n"
5028 "Charset conversion failed."));
5029 } else if (val == -5) {
5030 alertpanel_error(_("Could not queue message for sending:\n\n"
5031 "Couldn't get recipient encryption key."));
5032 } else if (val == -6) {
5034 } else if (val == -3) {
5035 if (privacy_peek_error())
5036 alertpanel_error(_("Could not queue message for sending:\n\n"
5037 "Signature failed: %s"), privacy_get_error());
5038 } else if (val == -2 && errno != 0) {
5039 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
5041 alertpanel_error(_("Could not queue message for sending."));
5046 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
5047 if (discard_window) {
5048 compose->sending = FALSE;
5049 compose_close(compose);
5050 /* No more compose access in the normal codepath
5051 * after this point! */
5056 alertpanel_error(_("The message was queued but could not be "
5057 "sent.\nUse \"Send queued messages\" from "
5058 "the main window to retry."));
5059 if (!discard_window) {
5066 if (msgpath == NULL) {
5067 msgpath = folder_item_fetch_msg(folder, msgnum);
5068 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5071 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5072 claws_unlink(msgpath);
5075 if (!discard_window) {
5077 if (!queued_removed)
5078 folder_item_remove_msg(folder, msgnum);
5079 folder_item_scan(folder);
5081 /* make sure we delete that */
5082 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5084 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5085 folder_item_remove_msg(folder, tmp->msgnum);
5086 procmsg_msginfo_free(tmp);
5093 if (!queued_removed)
5094 folder_item_remove_msg(folder, msgnum);
5095 folder_item_scan(folder);
5097 /* make sure we delete that */
5098 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5100 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5101 folder_item_remove_msg(folder, tmp->msgnum);
5102 procmsg_msginfo_free(tmp);
5105 if (!discard_window) {
5106 compose->sending = FALSE;
5107 compose_allow_user_actions (compose, TRUE);
5108 compose_close(compose);
5112 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5113 "the main window to retry."), errstr);
5116 alertpanel_error_log(_("The message was queued but could not be "
5117 "sent.\nUse \"Send queued messages\" from "
5118 "the main window to retry."));
5120 if (!discard_window) {
5129 toolbar_main_set_sensitive(mainwin);
5130 main_window_set_menu_sensitive(mainwin);
5136 compose_allow_user_actions (compose, TRUE);
5137 compose->sending = FALSE;
5138 compose->modified = TRUE;
5139 toolbar_main_set_sensitive(mainwin);
5140 main_window_set_menu_sensitive(mainwin);
5145 static gboolean compose_use_attach(Compose *compose)
5147 GtkTreeModel *model = gtk_tree_view_get_model
5148 (GTK_TREE_VIEW(compose->attach_clist));
5149 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5152 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5155 gchar buf[BUFFSIZE];
5157 gboolean first_to_address;
5158 gboolean first_cc_address;
5160 ComposeHeaderEntry *headerentry;
5161 const gchar *headerentryname;
5162 const gchar *cc_hdr;
5163 const gchar *to_hdr;
5164 gboolean err = FALSE;
5166 debug_print("Writing redirect header\n");
5168 cc_hdr = prefs_common_translated_header_name("Cc:");
5169 to_hdr = prefs_common_translated_header_name("To:");
5171 first_to_address = TRUE;
5172 for (list = compose->header_list; list; list = list->next) {
5173 headerentry = ((ComposeHeaderEntry *)list->data);
5174 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5176 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5177 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5178 Xstrdup_a(str, entstr, return -1);
5180 if (str[0] != '\0') {
5181 compose_convert_header
5182 (compose, buf, sizeof(buf), str,
5183 strlen("Resent-To") + 2, TRUE);
5185 if (first_to_address) {
5186 err |= (fprintf(fp, "Resent-To: ") < 0);
5187 first_to_address = FALSE;
5189 err |= (fprintf(fp, ",") < 0);
5191 err |= (fprintf(fp, "%s", buf) < 0);
5195 if (!first_to_address) {
5196 err |= (fprintf(fp, "\n") < 0);
5199 first_cc_address = TRUE;
5200 for (list = compose->header_list; list; list = list->next) {
5201 headerentry = ((ComposeHeaderEntry *)list->data);
5202 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5204 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5205 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5206 Xstrdup_a(str, strg, return -1);
5208 if (str[0] != '\0') {
5209 compose_convert_header
5210 (compose, buf, sizeof(buf), str,
5211 strlen("Resent-Cc") + 2, TRUE);
5213 if (first_cc_address) {
5214 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5215 first_cc_address = FALSE;
5217 err |= (fprintf(fp, ",") < 0);
5219 err |= (fprintf(fp, "%s", buf) < 0);
5223 if (!first_cc_address) {
5224 err |= (fprintf(fp, "\n") < 0);
5227 return (err ? -1:0);
5230 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5232 gchar buf[BUFFSIZE];
5234 const gchar *entstr;
5235 /* struct utsname utsbuf; */
5236 gboolean err = FALSE;
5238 cm_return_val_if_fail(fp != NULL, -1);
5239 cm_return_val_if_fail(compose->account != NULL, -1);
5240 cm_return_val_if_fail(compose->account->address != NULL, -1);
5243 get_rfc822_date(buf, sizeof(buf));
5244 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5247 if (compose->account->name && *compose->account->name) {
5248 compose_convert_header
5249 (compose, buf, sizeof(buf), compose->account->name,
5250 strlen("From: "), TRUE);
5251 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5252 buf, compose->account->address) < 0);
5254 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5257 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5258 if (*entstr != '\0') {
5259 Xstrdup_a(str, entstr, return -1);
5262 compose_convert_header(compose, buf, sizeof(buf), str,
5263 strlen("Subject: "), FALSE);
5264 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5268 /* Resent-Message-ID */
5269 if (compose->account->set_domain && compose->account->domain) {
5270 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5271 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5272 g_snprintf(buf, sizeof(buf), "%s",
5273 strchr(compose->account->address, '@') ?
5274 strchr(compose->account->address, '@')+1 :
5275 compose->account->address);
5277 g_snprintf(buf, sizeof(buf), "%s", "");
5280 if (compose->account->gen_msgid) {
5282 if (compose->account->msgid_with_addr) {
5283 addr = compose->account->address;
5285 generate_msgid(buf, sizeof(buf), addr);
5286 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5287 compose->msgid = g_strdup(buf);
5289 compose->msgid = NULL;
5292 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5295 /* separator between header and body */
5296 err |= (fputs("\n", fp) == EOF);
5298 return (err ? -1:0);
5301 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5305 gchar buf[BUFFSIZE];
5307 gboolean skip = FALSE;
5308 gboolean err = FALSE;
5309 gchar *not_included[]={
5310 "Return-Path:", "Delivered-To:", "Received:",
5311 "Subject:", "X-UIDL:", "AF:",
5312 "NF:", "PS:", "SRH:",
5313 "SFN:", "DSR:", "MID:",
5314 "CFG:", "PT:", "S:",
5315 "RQ:", "SSV:", "NSV:",
5316 "SSH:", "R:", "MAID:",
5317 "NAID:", "RMID:", "FMID:",
5318 "SCF:", "RRCPT:", "NG:",
5319 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5320 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5321 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5322 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5323 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5326 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5327 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5331 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5333 for (i = 0; not_included[i] != NULL; i++) {
5334 if (g_ascii_strncasecmp(buf, not_included[i],
5335 strlen(not_included[i])) == 0) {
5342 if (fputs(buf, fdest) == -1)
5345 if (!prefs_common.redirect_keep_from) {
5346 if (g_ascii_strncasecmp(buf, "From:",
5347 strlen("From:")) == 0) {
5348 err |= (fputs(" (by way of ", fdest) == EOF);
5349 if (compose->account->name
5350 && *compose->account->name) {
5351 compose_convert_header
5352 (compose, buf, sizeof(buf),
5353 compose->account->name,
5356 err |= (fprintf(fdest, "%s <%s>",
5358 compose->account->address) < 0);
5360 err |= (fprintf(fdest, "%s",
5361 compose->account->address) < 0);
5362 err |= (fputs(")", fdest) == EOF);
5366 if (fputs("\n", fdest) == -1)
5373 if (compose_redirect_write_headers(compose, fdest))
5376 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5377 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5390 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5392 GtkTextBuffer *buffer;
5393 GtkTextIter start, end;
5396 const gchar *out_codeset;
5397 EncodingType encoding = ENC_UNKNOWN;
5398 MimeInfo *mimemsg, *mimetext;
5400 const gchar *src_codeset = CS_INTERNAL;
5401 gchar *from_addr = NULL;
5402 gchar *from_name = NULL;
5404 if (action == COMPOSE_WRITE_FOR_SEND)
5405 attach_parts = TRUE;
5407 /* create message MimeInfo */
5408 mimemsg = procmime_mimeinfo_new();
5409 mimemsg->type = MIMETYPE_MESSAGE;
5410 mimemsg->subtype = g_strdup("rfc822");
5411 mimemsg->content = MIMECONTENT_MEM;
5412 mimemsg->tmp = TRUE; /* must free content later */
5413 mimemsg->data.mem = compose_get_header(compose);
5415 /* Create text part MimeInfo */
5416 /* get all composed text */
5417 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5418 gtk_text_buffer_get_start_iter(buffer, &start);
5419 gtk_text_buffer_get_end_iter(buffer, &end);
5420 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5422 out_codeset = conv_get_charset_str(compose->out_encoding);
5424 if (!out_codeset && is_ascii_str(chars)) {
5425 out_codeset = CS_US_ASCII;
5426 } else if (prefs_common.outgoing_fallback_to_ascii &&
5427 is_ascii_str(chars)) {
5428 out_codeset = CS_US_ASCII;
5429 encoding = ENC_7BIT;
5433 gchar *test_conv_global_out = NULL;
5434 gchar *test_conv_reply = NULL;
5436 /* automatic mode. be automatic. */
5437 codeconv_set_strict(TRUE);
5439 out_codeset = conv_get_outgoing_charset_str();
5441 debug_print("trying to convert to %s\n", out_codeset);
5442 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5445 if (!test_conv_global_out && compose->orig_charset
5446 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5447 out_codeset = compose->orig_charset;
5448 debug_print("failure; trying to convert to %s\n", out_codeset);
5449 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5452 if (!test_conv_global_out && !test_conv_reply) {
5454 out_codeset = CS_INTERNAL;
5455 debug_print("failure; finally using %s\n", out_codeset);
5457 g_free(test_conv_global_out);
5458 g_free(test_conv_reply);
5459 codeconv_set_strict(FALSE);
5462 if (encoding == ENC_UNKNOWN) {
5463 if (prefs_common.encoding_method == CTE_BASE64)
5464 encoding = ENC_BASE64;
5465 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5466 encoding = ENC_QUOTED_PRINTABLE;
5467 else if (prefs_common.encoding_method == CTE_8BIT)
5468 encoding = ENC_8BIT;
5470 encoding = procmime_get_encoding_for_charset(out_codeset);
5473 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5474 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5476 if (action == COMPOSE_WRITE_FOR_SEND) {
5477 codeconv_set_strict(TRUE);
5478 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5479 codeconv_set_strict(FALSE);
5485 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5486 "to the specified %s charset.\n"
5487 "Send it as %s?"), out_codeset, src_codeset);
5488 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5489 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5492 if (aval != G_ALERTALTERNATE) {
5497 out_codeset = src_codeset;
5503 out_codeset = src_codeset;
5508 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5509 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5510 strstr(buf, "\nFrom ") != NULL) {
5511 encoding = ENC_QUOTED_PRINTABLE;
5515 mimetext = procmime_mimeinfo_new();
5516 mimetext->content = MIMECONTENT_MEM;
5517 mimetext->tmp = TRUE; /* must free content later */
5518 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5519 * and free the data, which we need later. */
5520 mimetext->data.mem = g_strdup(buf);
5521 mimetext->type = MIMETYPE_TEXT;
5522 mimetext->subtype = g_strdup("plain");
5523 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5524 g_strdup(out_codeset));
5526 /* protect trailing spaces when signing message */
5527 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5528 privacy_system_can_sign(compose->privacy_system)) {
5529 encoding = ENC_QUOTED_PRINTABLE;
5532 debug_print("main text: %zd bytes encoded as %s in %d\n",
5533 strlen(buf), out_codeset, encoding);
5535 /* check for line length limit */
5536 if (action == COMPOSE_WRITE_FOR_SEND &&
5537 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5538 check_line_length(buf, 1000, &line) < 0) {
5542 msg = g_strdup_printf
5543 (_("Line %d exceeds the line length limit (998 bytes).\n"
5544 "The contents of the message might be broken on the way to the delivery.\n"
5546 "Send it anyway?"), line + 1);
5547 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5549 if (aval != G_ALERTALTERNATE) {
5555 if (encoding != ENC_UNKNOWN)
5556 procmime_encode_content(mimetext, encoding);
5558 /* append attachment parts */
5559 if (compose_use_attach(compose) && attach_parts) {
5560 MimeInfo *mimempart;
5561 gchar *boundary = NULL;
5562 mimempart = procmime_mimeinfo_new();
5563 mimempart->content = MIMECONTENT_EMPTY;
5564 mimempart->type = MIMETYPE_MULTIPART;
5565 mimempart->subtype = g_strdup("mixed");
5569 boundary = generate_mime_boundary(NULL);
5570 } while (strstr(buf, boundary) != NULL);
5572 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5575 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5577 g_node_append(mimempart->node, mimetext->node);
5578 g_node_append(mimemsg->node, mimempart->node);
5580 if (compose_add_attachments(compose, mimempart) < 0)
5583 g_node_append(mimemsg->node, mimetext->node);
5587 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5588 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5589 /* extract name and address */
5590 if (strstr(spec, " <") && strstr(spec, ">")) {
5591 from_addr = g_strdup(strrchr(spec, '<')+1);
5592 *(strrchr(from_addr, '>')) = '\0';
5593 from_name = g_strdup(spec);
5594 *(strrchr(from_name, '<')) = '\0';
5601 /* sign message if sending */
5602 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5603 privacy_system_can_sign(compose->privacy_system))
5604 if (!privacy_sign(compose->privacy_system, mimemsg,
5605 compose->account, from_addr)) {
5612 procmime_write_mimeinfo(mimemsg, fp);
5614 procmime_mimeinfo_free_all(mimemsg);
5619 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5621 GtkTextBuffer *buffer;
5622 GtkTextIter start, end;
5627 if ((fp = g_fopen(file, "wb")) == NULL) {
5628 FILE_OP_ERROR(file, "fopen");
5632 /* chmod for security */
5633 if (change_file_mode_rw(fp, file) < 0) {
5634 FILE_OP_ERROR(file, "chmod");
5635 g_warning("can't change file mode\n");
5638 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5639 gtk_text_buffer_get_start_iter(buffer, &start);
5640 gtk_text_buffer_get_end_iter(buffer, &end);
5641 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5643 chars = conv_codeset_strdup
5644 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5647 if (!chars) return -1;
5650 len = strlen(chars);
5651 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5652 FILE_OP_ERROR(file, "fwrite");
5661 if (fclose(fp) == EOF) {
5662 FILE_OP_ERROR(file, "fclose");
5669 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5672 MsgInfo *msginfo = compose->targetinfo;
5674 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5675 if (!msginfo) return -1;
5677 if (!force && MSG_IS_LOCKED(msginfo->flags))
5680 item = msginfo->folder;
5681 cm_return_val_if_fail(item != NULL, -1);
5683 if (procmsg_msg_exist(msginfo) &&
5684 (folder_has_parent_of_type(item, F_QUEUE) ||
5685 folder_has_parent_of_type(item, F_DRAFT)
5686 || msginfo == compose->autosaved_draft)) {
5687 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5688 g_warning("can't remove the old message\n");
5691 debug_print("removed reedit target %d\n", msginfo->msgnum);
5698 static void compose_remove_draft(Compose *compose)
5701 MsgInfo *msginfo = compose->targetinfo;
5702 drafts = account_get_special_folder(compose->account, F_DRAFT);
5704 if (procmsg_msg_exist(msginfo)) {
5705 folder_item_remove_msg(drafts, msginfo->msgnum);
5710 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5711 gboolean remove_reedit_target)
5713 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5716 static gboolean compose_warn_encryption(Compose *compose)
5718 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5719 AlertValue val = G_ALERTALTERNATE;
5721 if (warning == NULL)
5724 val = alertpanel_full(_("Encryption warning"), warning,
5725 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5726 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5727 if (val & G_ALERTDISABLE) {
5728 val &= ~G_ALERTDISABLE;
5729 if (val == G_ALERTALTERNATE)
5730 privacy_inhibit_encrypt_warning(compose->privacy_system,
5734 if (val == G_ALERTALTERNATE) {
5741 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5742 gchar **msgpath, gboolean check_subject,
5743 gboolean remove_reedit_target)
5750 static gboolean lock = FALSE;
5751 PrefsAccount *mailac = NULL, *newsac = NULL;
5752 gboolean err = FALSE;
5754 debug_print("queueing message...\n");
5755 cm_return_val_if_fail(compose->account != NULL, -1);
5759 if (compose_check_entries(compose, check_subject) == FALSE) {
5761 if (compose->batch) {
5762 gtk_widget_show_all(compose->window);
5767 if (!compose->to_list && !compose->newsgroup_list) {
5768 g_warning("can't get recipient list.");
5773 if (compose->to_list) {
5774 if (compose->account->protocol != A_NNTP)
5775 mailac = compose->account;
5776 else if (cur_account && cur_account->protocol != A_NNTP)
5777 mailac = cur_account;
5778 else if (!(mailac = compose_current_mail_account())) {
5780 alertpanel_error(_("No account for sending mails available!"));
5785 if (compose->newsgroup_list) {
5786 if (compose->account->protocol == A_NNTP)
5787 newsac = compose->account;
5790 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5795 /* write queue header */
5796 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5797 G_DIR_SEPARATOR, compose, (guint) rand());
5798 debug_print("queuing to %s\n", tmp);
5799 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5800 FILE_OP_ERROR(tmp, "fopen");
5806 if (change_file_mode_rw(fp, tmp) < 0) {
5807 FILE_OP_ERROR(tmp, "chmod");
5808 g_warning("can't change file mode\n");
5811 /* queueing variables */
5812 err |= (fprintf(fp, "AF:\n") < 0);
5813 err |= (fprintf(fp, "NF:0\n") < 0);
5814 err |= (fprintf(fp, "PS:10\n") < 0);
5815 err |= (fprintf(fp, "SRH:1\n") < 0);
5816 err |= (fprintf(fp, "SFN:\n") < 0);
5817 err |= (fprintf(fp, "DSR:\n") < 0);
5819 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5821 err |= (fprintf(fp, "MID:\n") < 0);
5822 err |= (fprintf(fp, "CFG:\n") < 0);
5823 err |= (fprintf(fp, "PT:0\n") < 0);
5824 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5825 err |= (fprintf(fp, "RQ:\n") < 0);
5827 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5829 err |= (fprintf(fp, "SSV:\n") < 0);
5831 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5833 err |= (fprintf(fp, "NSV:\n") < 0);
5834 err |= (fprintf(fp, "SSH:\n") < 0);
5835 /* write recepient list */
5836 if (compose->to_list) {
5837 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5838 for (cur = compose->to_list->next; cur != NULL;
5840 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5841 err |= (fprintf(fp, "\n") < 0);
5843 /* write newsgroup list */
5844 if (compose->newsgroup_list) {
5845 err |= (fprintf(fp, "NG:") < 0);
5846 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5847 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5848 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5849 err |= (fprintf(fp, "\n") < 0);
5851 /* Sylpheed account IDs */
5853 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5855 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5858 if (compose->privacy_system != NULL) {
5859 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5860 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5861 if (compose->use_encryption) {
5863 if (!compose_warn_encryption(compose)) {
5870 if (mailac && mailac->encrypt_to_self) {
5871 GSList *tmp_list = g_slist_copy(compose->to_list);
5872 tmp_list = g_slist_append(tmp_list, compose->account->address);
5873 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5874 g_slist_free(tmp_list);
5876 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5878 if (encdata != NULL) {
5879 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5880 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5881 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5883 } /* else we finally dont want to encrypt */
5885 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5886 /* and if encdata was null, it means there's been a problem in
5898 /* Save copy folder */
5899 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5900 gchar *savefolderid;
5902 savefolderid = compose_get_save_to(compose);
5903 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5904 g_free(savefolderid);
5906 /* Save copy folder */
5907 if (compose->return_receipt) {
5908 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5910 /* Message-ID of message replying to */
5911 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5914 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5915 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5918 /* Message-ID of message forwarding to */
5919 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5922 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5923 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5927 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5928 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5930 /* end of headers */
5931 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5933 if (compose->redirect_filename != NULL) {
5934 if (compose_redirect_write_to_file(compose, fp) < 0) {
5943 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5948 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5952 g_warning("failed to write queue message\n");
5959 if (fclose(fp) == EOF) {
5960 FILE_OP_ERROR(tmp, "fclose");
5967 if (item && *item) {
5970 queue = account_get_special_folder(compose->account, F_QUEUE);
5973 g_warning("can't find queue folder\n");
5979 folder_item_scan(queue);
5980 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5981 g_warning("can't queue the message\n");
5988 if (msgpath == NULL) {
5994 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5995 compose_remove_reedit_target(compose, FALSE);
5998 if ((msgnum != NULL) && (item != NULL)) {
6006 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
6009 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
6011 struct stat statbuf;
6012 gchar *type, *subtype;
6013 GtkTreeModel *model;
6016 model = gtk_tree_view_get_model(tree_view);
6018 if (!gtk_tree_model_get_iter_first(model, &iter))
6021 gtk_tree_model_get(model, &iter,
6025 if (!is_file_exist(ainfo->file)) {
6026 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
6027 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
6028 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
6030 if (val == G_ALERTDEFAULT) {
6035 mimepart = procmime_mimeinfo_new();
6036 mimepart->content = MIMECONTENT_FILE;
6037 mimepart->data.filename = g_strdup(ainfo->file);
6038 mimepart->tmp = FALSE; /* or we destroy our attachment */
6039 mimepart->offset = 0;
6041 g_stat(ainfo->file, &statbuf);
6042 mimepart->length = statbuf.st_size;
6044 type = g_strdup(ainfo->content_type);
6046 if (!strchr(type, '/')) {
6048 type = g_strdup("application/octet-stream");
6051 subtype = strchr(type, '/') + 1;
6052 *(subtype - 1) = '\0';
6053 mimepart->type = procmime_get_media_type(type);
6054 mimepart->subtype = g_strdup(subtype);
6057 if (mimepart->type == MIMETYPE_MESSAGE &&
6058 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
6059 mimepart->disposition = DISPOSITIONTYPE_INLINE;
6060 } else if (mimepart->type == MIMETYPE_TEXT) {
6061 if (!ainfo->name && compose->mode == COMPOSE_FORWARD_INLINE) {
6062 /* Text parts with no name come from multipart/alternative
6063 * forwards. Make sure the recipient won't look at the
6064 * original HTML part by mistake. */
6065 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6066 ainfo->name = g_strdup_printf(_("Original %s part"),
6070 g_hash_table_insert(mimepart->typeparameters,
6071 g_strdup("charset"), g_strdup(ainfo->charset));
6073 if (ainfo->name && mimepart->type != MIMETYPE_MESSAGE) {
6074 if (mimepart->type == MIMETYPE_APPLICATION &&
6075 !strcmp2(mimepart->subtype, "octet-stream"))
6076 g_hash_table_insert(mimepart->typeparameters,
6077 g_strdup("name"), g_strdup(ainfo->name));
6078 g_hash_table_insert(mimepart->dispositionparameters,
6079 g_strdup("filename"), g_strdup(ainfo->name));
6080 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6083 if (mimepart->type == MIMETYPE_MESSAGE
6084 || mimepart->type == MIMETYPE_MULTIPART)
6085 ainfo->encoding = ENC_BINARY;
6086 else if (compose->use_signing) {
6087 if (ainfo->encoding == ENC_7BIT)
6088 ainfo->encoding = ENC_QUOTED_PRINTABLE;
6089 else if (ainfo->encoding == ENC_8BIT)
6090 ainfo->encoding = ENC_BASE64;
6095 procmime_encode_content(mimepart, ainfo->encoding);
6097 g_node_append(parent->node, mimepart->node);
6098 } while (gtk_tree_model_iter_next(model, &iter));
6103 #define IS_IN_CUSTOM_HEADER(header) \
6104 (compose->account->add_customhdr && \
6105 custom_header_find(compose->account->customhdr_list, header) != NULL)
6107 static void compose_add_headerfield_from_headerlist(Compose *compose,
6109 const gchar *fieldname,
6110 const gchar *seperator)
6112 gchar *str, *fieldname_w_colon;
6113 gboolean add_field = FALSE;
6115 ComposeHeaderEntry *headerentry;
6116 const gchar *headerentryname;
6117 const gchar *trans_fieldname;
6120 if (IS_IN_CUSTOM_HEADER(fieldname))
6123 debug_print("Adding %s-fields\n", fieldname);
6125 fieldstr = g_string_sized_new(64);
6127 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6128 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6130 for (list = compose->header_list; list; list = list->next) {
6131 headerentry = ((ComposeHeaderEntry *)list->data);
6132 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6134 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6135 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6137 if (str[0] != '\0') {
6139 g_string_append(fieldstr, seperator);
6140 g_string_append(fieldstr, str);
6149 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6150 compose_convert_header
6151 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6152 strlen(fieldname) + 2, TRUE);
6153 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6157 g_free(fieldname_w_colon);
6158 g_string_free(fieldstr, TRUE);
6163 static gchar *compose_get_manual_headers_info(Compose *compose)
6165 GString *sh_header = g_string_new(" ");
6167 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6169 for (list = compose->header_list; list; list = list->next) {
6170 ComposeHeaderEntry *headerentry;
6173 gchar *headername_wcolon;
6174 const gchar *headername_trans;
6176 gboolean standard_header = FALSE;
6178 headerentry = ((ComposeHeaderEntry *)list->data);
6180 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6182 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6187 if (!strstr(tmp, ":")) {
6188 headername_wcolon = g_strconcat(tmp, ":", NULL);
6189 headername = g_strdup(tmp);
6191 headername_wcolon = g_strdup(tmp);
6192 headername = g_strdup(strtok(tmp, ":"));
6196 string = std_headers;
6197 while (*string != NULL) {
6198 headername_trans = prefs_common_translated_header_name(*string);
6199 if (!strcmp(headername_trans, headername_wcolon))
6200 standard_header = TRUE;
6203 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6204 g_string_append_printf(sh_header, "%s ", headername);
6206 g_free(headername_wcolon);
6208 g_string_truncate(sh_header, strlen(sh_header->str) - 1); /* remove last space */
6209 return g_string_free(sh_header, FALSE);
6212 static gchar *compose_get_header(Compose *compose)
6214 gchar buf[BUFFSIZE];
6215 const gchar *entry_str;
6219 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6221 gchar *from_name = NULL, *from_address = NULL;
6224 cm_return_val_if_fail(compose->account != NULL, NULL);
6225 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6227 header = g_string_sized_new(64);
6230 get_rfc822_date(buf, sizeof(buf));
6231 g_string_append_printf(header, "Date: %s\n", buf);
6235 if (compose->account->name && *compose->account->name) {
6237 QUOTE_IF_REQUIRED(buf, compose->account->name);
6238 tmp = g_strdup_printf("%s <%s>",
6239 buf, compose->account->address);
6241 tmp = g_strdup_printf("%s",
6242 compose->account->address);
6244 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6245 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6247 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6248 from_address = g_strdup(compose->account->address);
6250 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6251 /* extract name and address */
6252 if (strstr(spec, " <") && strstr(spec, ">")) {
6253 from_address = g_strdup(strrchr(spec, '<')+1);
6254 *(strrchr(from_address, '>')) = '\0';
6255 from_name = g_strdup(spec);
6256 *(strrchr(from_name, '<')) = '\0';
6259 from_address = g_strdup(spec);
6266 if (from_name && *from_name) {
6267 compose_convert_header
6268 (compose, buf, sizeof(buf), from_name,
6269 strlen("From: "), TRUE);
6270 QUOTE_IF_REQUIRED(name, buf);
6272 g_string_append_printf(header, "From: %s <%s>\n",
6273 name, from_address);
6275 g_string_append_printf(header, "From: %s\n", from_address);
6278 g_free(from_address);
6281 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6284 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6287 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6291 * If this account is a NNTP account remove Bcc header from
6292 * message body since it otherwise will be publicly shown
6294 if (compose->account->protocol != A_NNTP)
6295 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6298 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6300 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6303 compose_convert_header(compose, buf, sizeof(buf), str,
6304 strlen("Subject: "), FALSE);
6305 g_string_append_printf(header, "Subject: %s\n", buf);
6311 if (compose->account->set_domain && compose->account->domain) {
6312 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6313 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6314 g_snprintf(buf, sizeof(buf), "%s",
6315 strchr(compose->account->address, '@') ?
6316 strchr(compose->account->address, '@')+1 :
6317 compose->account->address);
6319 g_snprintf(buf, sizeof(buf), "%s", "");
6322 if (compose->account->gen_msgid) {
6324 if (compose->account->msgid_with_addr) {
6325 addr = compose->account->address;
6327 generate_msgid(buf, sizeof(buf), addr);
6328 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6329 compose->msgid = g_strdup(buf);
6331 compose->msgid = NULL;
6334 if (compose->remove_references == FALSE) {
6336 if (compose->inreplyto && compose->to_list)
6337 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6340 if (compose->references)
6341 g_string_append_printf(header, "References: %s\n", compose->references);
6345 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6348 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6351 if (compose->account->organization &&
6352 strlen(compose->account->organization) &&
6353 !IS_IN_CUSTOM_HEADER("Organization")) {
6354 compose_convert_header(compose, buf, sizeof(buf),
6355 compose->account->organization,
6356 strlen("Organization: "), FALSE);
6357 g_string_append_printf(header, "Organization: %s\n", buf);
6360 /* Program version and system info */
6361 if (compose->account->gen_xmailer &&
6362 g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6363 !compose->newsgroup_list) {
6364 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6366 gtk_major_version, gtk_minor_version, gtk_micro_version,
6369 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6370 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6372 gtk_major_version, gtk_minor_version, gtk_micro_version,
6376 /* custom headers */
6377 if (compose->account->add_customhdr) {
6380 for (cur = compose->account->customhdr_list; cur != NULL;
6382 CustomHeader *chdr = (CustomHeader *)cur->data;
6384 if (custom_header_is_allowed(chdr->name)
6385 && chdr->value != NULL
6386 && *(chdr->value) != '\0') {
6387 compose_convert_header
6388 (compose, buf, sizeof(buf),
6390 strlen(chdr->name) + 2, FALSE);
6391 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6396 /* Automatic Faces and X-Faces */
6397 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6398 g_string_append_printf(header, "X-Face: %s\n", buf);
6400 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6401 g_string_append_printf(header, "X-Face: %s\n", buf);
6403 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6404 g_string_append_printf(header, "Face: %s\n", buf);
6406 else if (get_default_face (buf, sizeof(buf)) == 0) {
6407 g_string_append_printf(header, "Face: %s\n", buf);
6411 switch (compose->priority) {
6412 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6413 "X-Priority: 1 (Highest)\n");
6415 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6416 "X-Priority: 2 (High)\n");
6418 case PRIORITY_NORMAL: break;
6419 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6420 "X-Priority: 4 (Low)\n");
6422 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6423 "X-Priority: 5 (Lowest)\n");
6425 default: debug_print("compose: priority unknown : %d\n",
6429 /* Request Return Receipt */
6430 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6431 if (compose->return_receipt) {
6432 if (compose->account->name
6433 && *compose->account->name) {
6434 compose_convert_header(compose, buf, sizeof(buf),
6435 compose->account->name,
6436 strlen("Disposition-Notification-To: "),
6438 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6440 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6444 /* get special headers */
6445 for (list = compose->header_list; list; list = list->next) {
6446 ComposeHeaderEntry *headerentry;
6449 gchar *headername_wcolon;
6450 const gchar *headername_trans;
6453 gboolean standard_header = FALSE;
6455 headerentry = ((ComposeHeaderEntry *)list->data);
6457 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6459 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6464 if (!strstr(tmp, ":")) {
6465 headername_wcolon = g_strconcat(tmp, ":", NULL);
6466 headername = g_strdup(tmp);
6468 headername_wcolon = g_strdup(tmp);
6469 headername = g_strdup(strtok(tmp, ":"));
6473 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6474 Xstrdup_a(headervalue, entry_str, return NULL);
6475 subst_char(headervalue, '\r', ' ');
6476 subst_char(headervalue, '\n', ' ');
6477 string = std_headers;
6478 while (*string != NULL) {
6479 headername_trans = prefs_common_translated_header_name(*string);
6480 if (!strcmp(headername_trans, headername_wcolon))
6481 standard_header = TRUE;
6484 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6485 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6488 g_free(headername_wcolon);
6492 g_string_free(header, FALSE);
6497 #undef IS_IN_CUSTOM_HEADER
6499 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6500 gint header_len, gboolean addr_field)
6502 gchar *tmpstr = NULL;
6503 const gchar *out_codeset = NULL;
6505 cm_return_if_fail(src != NULL);
6506 cm_return_if_fail(dest != NULL);
6508 if (len < 1) return;
6510 tmpstr = g_strdup(src);
6512 subst_char(tmpstr, '\n', ' ');
6513 subst_char(tmpstr, '\r', ' ');
6516 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6517 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6518 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6523 codeconv_set_strict(TRUE);
6524 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6525 conv_get_charset_str(compose->out_encoding));
6526 codeconv_set_strict(FALSE);
6528 if (!dest || *dest == '\0') {
6529 gchar *test_conv_global_out = NULL;
6530 gchar *test_conv_reply = NULL;
6532 /* automatic mode. be automatic. */
6533 codeconv_set_strict(TRUE);
6535 out_codeset = conv_get_outgoing_charset_str();
6537 debug_print("trying to convert to %s\n", out_codeset);
6538 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6541 if (!test_conv_global_out && compose->orig_charset
6542 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6543 out_codeset = compose->orig_charset;
6544 debug_print("failure; trying to convert to %s\n", out_codeset);
6545 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6548 if (!test_conv_global_out && !test_conv_reply) {
6550 out_codeset = CS_INTERNAL;
6551 debug_print("finally using %s\n", out_codeset);
6553 g_free(test_conv_global_out);
6554 g_free(test_conv_reply);
6555 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6557 codeconv_set_strict(FALSE);
6562 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6566 cm_return_if_fail(user_data != NULL);
6568 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6569 g_strstrip(address);
6570 if (*address != '\0') {
6571 gchar *name = procheader_get_fromname(address);
6572 extract_address(address);
6573 addressbook_add_contact(name, address, NULL, NULL);
6578 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6580 GtkWidget *menuitem;
6583 cm_return_if_fail(menu != NULL);
6584 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6586 menuitem = gtk_separator_menu_item_new();
6587 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6588 gtk_widget_show(menuitem);
6590 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6591 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6593 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6594 g_strstrip(address);
6595 if (*address == '\0') {
6596 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6599 g_signal_connect(G_OBJECT(menuitem), "activate",
6600 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6601 gtk_widget_show(menuitem);
6604 static void compose_create_header_entry(Compose *compose)
6606 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6613 const gchar *header = NULL;
6614 ComposeHeaderEntry *headerentry;
6615 gboolean standard_header = FALSE;
6616 GtkListStore *model;
6618 #if !(GTK_CHECK_VERSION(2,12,0))
6619 GtkTooltips *tips = compose->tooltips;
6622 headerentry = g_new0(ComposeHeaderEntry, 1);
6625 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6626 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6627 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6629 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6631 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6633 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6634 COMPOSE_NEWSGROUPS);
6635 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6637 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6638 COMPOSE_FOLLOWUPTO);
6640 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6641 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6642 G_CALLBACK(compose_grab_focus_cb), compose);
6643 gtk_widget_show(combo);
6644 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6645 compose->header_nextrow, compose->header_nextrow+1,
6646 GTK_SHRINK, GTK_FILL, 0, 0);
6647 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6648 const gchar *last_header_entry = gtk_entry_get_text(
6649 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6651 while (*string != NULL) {
6652 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6653 standard_header = TRUE;
6656 if (standard_header)
6657 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6659 if (!compose->header_last || !standard_header) {
6660 switch(compose->account->protocol) {
6662 header = prefs_common_translated_header_name("Newsgroups:");
6665 header = prefs_common_translated_header_name("To:");
6670 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6672 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6673 G_CALLBACK(compose_grab_focus_cb), compose);
6675 /* Entry field with cleanup button */
6676 button = gtk_button_new();
6677 gtk_button_set_image(GTK_BUTTON(button),
6678 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6679 gtk_widget_show(button);
6680 CLAWS_SET_TIP(button,
6681 _("Delete entry contents"));
6682 entry = gtk_entry_new();
6683 gtk_widget_show(entry);
6684 CLAWS_SET_TIP(entry,
6685 _("Use <tab> to autocomplete from addressbook"));
6686 hbox = gtk_hbox_new (FALSE, 0);
6687 gtk_widget_show(hbox);
6688 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6689 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6690 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6691 compose->header_nextrow, compose->header_nextrow+1,
6692 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6694 g_signal_connect(G_OBJECT(entry), "key-press-event",
6695 G_CALLBACK(compose_headerentry_key_press_event_cb),
6697 g_signal_connect(G_OBJECT(entry), "changed",
6698 G_CALLBACK(compose_headerentry_changed_cb),
6700 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6701 G_CALLBACK(compose_grab_focus_cb), compose);
6703 g_signal_connect(G_OBJECT(button), "clicked",
6704 G_CALLBACK(compose_headerentry_button_clicked_cb),
6708 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6709 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6710 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6711 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6712 G_CALLBACK(compose_header_drag_received_cb),
6714 g_signal_connect(G_OBJECT(entry), "drag-drop",
6715 G_CALLBACK(compose_drag_drop),
6717 g_signal_connect(G_OBJECT(entry), "populate-popup",
6718 G_CALLBACK(compose_entry_popup_extend),
6721 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6723 headerentry->compose = compose;
6724 headerentry->combo = combo;
6725 headerentry->entry = entry;
6726 headerentry->button = button;
6727 headerentry->hbox = hbox;
6728 headerentry->headernum = compose->header_nextrow;
6729 headerentry->type = PREF_NONE;
6731 compose->header_nextrow++;
6732 compose->header_last = headerentry;
6733 compose->header_list =
6734 g_slist_append(compose->header_list,
6738 static void compose_add_header_entry(Compose *compose, const gchar *header,
6739 gchar *text, ComposePrefType pref_type)
6741 ComposeHeaderEntry *last_header = compose->header_last;
6742 gchar *tmp = g_strdup(text), *email;
6743 gboolean replyto_hdr;
6745 replyto_hdr = (!strcasecmp(header,
6746 prefs_common_translated_header_name("Reply-To:")) ||
6748 prefs_common_translated_header_name("Followup-To:")) ||
6750 prefs_common_translated_header_name("In-Reply-To:")));
6752 extract_address(tmp);
6753 email = g_utf8_strdown(tmp, -1);
6755 if (replyto_hdr == FALSE &&
6756 g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6758 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6759 header, text, (gint) pref_type);
6765 if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6766 gtk_entry_set_text(GTK_ENTRY(
6767 gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6769 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6770 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6771 last_header->type = pref_type;
6773 if (replyto_hdr == FALSE)
6774 g_hash_table_insert(compose->email_hashtable, email,
6775 GUINT_TO_POINTER(1));
6782 static void compose_destroy_headerentry(Compose *compose,
6783 ComposeHeaderEntry *headerentry)
6785 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6788 extract_address(text);
6789 email = g_utf8_strdown(text, -1);
6790 g_hash_table_remove(compose->email_hashtable, email);
6794 gtk_widget_destroy(headerentry->combo);
6795 gtk_widget_destroy(headerentry->entry);
6796 gtk_widget_destroy(headerentry->button);
6797 gtk_widget_destroy(headerentry->hbox);
6798 g_free(headerentry);
6801 static void compose_remove_header_entries(Compose *compose)
6804 for (list = compose->header_list; list; list = list->next)
6805 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6807 compose->header_last = NULL;
6808 g_slist_free(compose->header_list);
6809 compose->header_list = NULL;
6810 compose->header_nextrow = 1;
6811 compose_create_header_entry(compose);
6814 static GtkWidget *compose_create_header(Compose *compose)
6816 GtkWidget *from_optmenu_hbox;
6817 GtkWidget *header_scrolledwin;
6818 GtkWidget *header_table;
6822 /* header labels and entries */
6823 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6824 gtk_widget_show(header_scrolledwin);
6825 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6827 header_table = gtk_table_new(2, 2, FALSE);
6828 gtk_widget_show(header_table);
6829 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6830 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6831 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6834 /* option menu for selecting accounts */
6835 from_optmenu_hbox = compose_account_option_menu_create(compose);
6836 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6837 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6840 compose->header_table = header_table;
6841 compose->header_list = NULL;
6842 compose->header_nextrow = count;
6844 compose_create_header_entry(compose);
6846 compose->table = NULL;
6848 return header_scrolledwin ;
6851 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6853 Compose *compose = (Compose *)data;
6854 GdkEventButton event;
6857 event.time = gtk_get_current_event_time();
6859 return attach_button_pressed(compose->attach_clist, &event, compose);
6862 static GtkWidget *compose_create_attach(Compose *compose)
6864 GtkWidget *attach_scrwin;
6865 GtkWidget *attach_clist;
6867 GtkListStore *store;
6868 GtkCellRenderer *renderer;
6869 GtkTreeViewColumn *column;
6870 GtkTreeSelection *selection;
6872 /* attachment list */
6873 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6874 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6875 GTK_POLICY_AUTOMATIC,
6876 GTK_POLICY_AUTOMATIC);
6877 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6879 store = gtk_list_store_new(N_ATTACH_COLS,
6885 G_TYPE_AUTO_POINTER,
6887 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6888 (GTK_TREE_MODEL(store)));
6889 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6890 g_object_unref(store);
6892 renderer = gtk_cell_renderer_text_new();
6893 column = gtk_tree_view_column_new_with_attributes
6894 (_("Mime type"), renderer, "text",
6895 COL_MIMETYPE, NULL);
6896 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6898 renderer = gtk_cell_renderer_text_new();
6899 column = gtk_tree_view_column_new_with_attributes
6900 (_("Size"), renderer, "text",
6902 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6904 renderer = gtk_cell_renderer_text_new();
6905 column = gtk_tree_view_column_new_with_attributes
6906 (_("Name"), renderer, "text",
6908 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6910 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6911 prefs_common.use_stripes_everywhere);
6912 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6913 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6915 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6916 G_CALLBACK(attach_selected), compose);
6917 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6918 G_CALLBACK(attach_button_pressed), compose);
6920 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6921 G_CALLBACK(popup_attach_button_pressed), compose);
6923 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6924 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6925 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6926 G_CALLBACK(popup_attach_button_pressed), compose);
6928 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6929 G_CALLBACK(attach_key_pressed), compose);
6932 gtk_drag_dest_set(attach_clist,
6933 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6934 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6935 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6936 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6937 G_CALLBACK(compose_attach_drag_received_cb),
6939 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6940 G_CALLBACK(compose_drag_drop),
6943 compose->attach_scrwin = attach_scrwin;
6944 compose->attach_clist = attach_clist;
6946 return attach_scrwin;
6949 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6950 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6952 static GtkWidget *compose_create_others(Compose *compose)
6955 GtkWidget *savemsg_checkbtn;
6956 GtkWidget *savemsg_combo;
6957 GtkWidget *savemsg_select;
6960 gchar *folderidentifier;
6962 /* Table for settings */
6963 table = gtk_table_new(3, 1, FALSE);
6964 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6965 gtk_widget_show(table);
6966 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6969 /* Save Message to folder */
6970 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6971 gtk_widget_show(savemsg_checkbtn);
6972 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6973 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6974 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6976 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6977 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6979 savemsg_combo = gtk_combo_box_entry_new_text();
6980 compose->savemsg_checkbtn = savemsg_checkbtn;
6981 compose->savemsg_combo = savemsg_combo;
6982 gtk_widget_show(savemsg_combo);
6984 if (prefs_common.compose_save_to_history)
6985 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6986 prefs_common.compose_save_to_history);
6988 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6989 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6990 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6991 G_CALLBACK(compose_grab_focus_cb), compose);
6992 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6993 folderidentifier = folder_item_get_identifier(account_get_special_folder
6994 (compose->account, F_OUTBOX));
6995 compose_set_save_to(compose, folderidentifier);
6996 g_free(folderidentifier);
6999 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
7000 gtk_widget_show(savemsg_select);
7001 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7002 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
7003 G_CALLBACK(compose_savemsg_select_cb),
7011 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
7013 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
7014 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
7017 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
7022 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
7025 path = folder_item_get_identifier(dest);
7027 compose_set_save_to(compose, path);
7031 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
7032 GdkAtom clip, GtkTextIter *insert_place);
7035 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
7039 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7041 if (event->button == 3) {
7043 GtkTextIter sel_start, sel_end;
7044 gboolean stuff_selected;
7046 /* move the cursor to allow GtkAspell to check the word
7047 * under the mouse */
7048 if (event->x && event->y) {
7049 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7050 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7052 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7055 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
7056 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7059 stuff_selected = gtk_text_buffer_get_selection_bounds(
7061 &sel_start, &sel_end);
7063 gtk_text_buffer_place_cursor (buffer, &iter);
7064 /* reselect stuff */
7066 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
7067 gtk_text_buffer_select_range(buffer,
7068 &sel_start, &sel_end);
7070 return FALSE; /* pass the event so that the right-click goes through */
7073 if (event->button == 2) {
7078 /* get the middle-click position to paste at the correct place */
7079 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7080 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7082 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7085 entry_paste_clipboard(compose, text,
7086 prefs_common.linewrap_pastes,
7087 GDK_SELECTION_PRIMARY, &iter);
7095 static void compose_spell_menu_changed(void *data)
7097 Compose *compose = (Compose *)data;
7099 GtkWidget *menuitem;
7100 GtkWidget *parent_item;
7101 GtkMenu *menu = GTK_MENU(gtk_menu_new());
7104 if (compose->gtkaspell == NULL)
7107 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
7108 "/Menu/Spelling/Options");
7110 /* setting the submenu removes /Spelling/Options from the factory
7111 * so we need to save it */
7113 if (parent_item == NULL) {
7114 parent_item = compose->aspell_options_menu;
7115 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
7117 compose->aspell_options_menu = parent_item;
7119 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
7121 spell_menu = g_slist_reverse(spell_menu);
7122 for (items = spell_menu;
7123 items; items = items->next) {
7124 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
7125 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
7126 gtk_widget_show(GTK_WIDGET(menuitem));
7128 g_slist_free(spell_menu);
7130 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7131 gtk_widget_show(parent_item);
7134 static void compose_dict_changed(void *data)
7136 Compose *compose = (Compose *) data;
7138 if(compose->gtkaspell &&
7139 compose->gtkaspell->recheck_when_changing_dict == FALSE)
7142 gtkaspell_highlight_all(compose->gtkaspell);
7143 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7147 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7149 Compose *compose = (Compose *)data;
7150 GdkEventButton event;
7153 event.time = gtk_get_current_event_time();
7157 return text_clicked(compose->text, &event, compose);
7160 static gboolean compose_force_window_origin = TRUE;
7161 static Compose *compose_create(PrefsAccount *account,
7170 GtkWidget *handlebox;
7172 GtkWidget *notebook;
7174 GtkWidget *attach_hbox;
7175 GtkWidget *attach_lab1;
7176 GtkWidget *attach_lab2;
7181 GtkWidget *subject_hbox;
7182 GtkWidget *subject_frame;
7183 GtkWidget *subject_entry;
7187 GtkWidget *edit_vbox;
7188 GtkWidget *ruler_hbox;
7190 GtkWidget *scrolledwin;
7192 GtkTextBuffer *buffer;
7193 GtkClipboard *clipboard;
7195 UndoMain *undostruct;
7197 gchar *titles[N_ATTACH_COLS];
7198 GtkWidget *popupmenu;
7199 GtkWidget *tmpl_menu;
7200 GtkActionGroup *action_group = NULL;
7203 GtkAspell * gtkaspell = NULL;
7206 static GdkGeometry geometry;
7208 cm_return_val_if_fail(account != NULL, NULL);
7210 debug_print("Creating compose window...\n");
7211 compose = g_new0(Compose, 1);
7213 titles[COL_MIMETYPE] = _("MIME type");
7214 titles[COL_SIZE] = _("Size");
7215 titles[COL_NAME] = _("Name");
7216 titles[COL_CHARSET] = _("Charset");
7218 compose->batch = batch;
7219 compose->account = account;
7220 compose->folder = folder;
7222 compose->mutex = g_mutex_new();
7223 compose->set_cursor_pos = -1;
7225 #if !(GTK_CHECK_VERSION(2,12,0))
7226 compose->tooltips = tips;
7229 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7231 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7232 gtk_widget_set_size_request(window, prefs_common.compose_width,
7233 prefs_common.compose_height);
7235 if (!geometry.max_width) {
7236 geometry.max_width = gdk_screen_width();
7237 geometry.max_height = gdk_screen_height();
7240 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7241 &geometry, GDK_HINT_MAX_SIZE);
7242 if (!geometry.min_width) {
7243 geometry.min_width = 600;
7244 geometry.min_height = 440;
7246 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7247 &geometry, GDK_HINT_MIN_SIZE);
7249 #ifndef GENERIC_UMPC
7250 if (compose_force_window_origin)
7251 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7252 prefs_common.compose_y);
7254 g_signal_connect(G_OBJECT(window), "delete_event",
7255 G_CALLBACK(compose_delete_cb), compose);
7256 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7257 gtk_widget_realize(window);
7259 gtkut_widget_set_composer_icon(window);
7261 vbox = gtk_vbox_new(FALSE, 0);
7262 gtk_container_add(GTK_CONTAINER(window), vbox);
7264 compose->ui_manager = gtk_ui_manager_new();
7265 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7266 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7267 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7268 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7269 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7270 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7271 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7272 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7273 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7274 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7277 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7279 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7282 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7283 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7285 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7287 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7288 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7289 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7292 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7293 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7294 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7295 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7296 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7297 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7298 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7299 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7300 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7301 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM)
7302 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7303 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7306 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7307 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7308 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7310 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7311 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7312 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7314 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7315 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7316 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7317 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7319 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7321 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7322 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7323 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7324 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7325 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7326 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7327 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7328 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7329 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7330 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7331 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7332 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7333 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7334 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7335 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7337 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7339 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7340 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7341 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7342 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7343 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7345 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7347 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7351 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7352 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7353 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7354 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7355 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7356 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7360 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7361 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7362 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7363 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7364 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7366 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7367 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7368 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7369 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7370 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7373 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7374 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7375 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7376 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7377 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7378 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7379 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7381 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7382 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7383 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7384 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7385 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7387 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7389 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7390 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7391 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7392 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7393 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7395 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7396 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)
7397 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)
7398 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7400 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7402 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7403 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)
7404 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)
7406 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7408 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7409 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)
7410 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7412 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7413 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)
7414 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7416 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7418 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7419 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)
7420 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7421 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7422 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7424 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7425 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)
7426 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)
7427 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7428 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7430 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7431 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7432 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7433 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7434 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7435 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7437 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7438 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7439 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)
7441 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7442 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7443 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7447 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7448 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7449 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7450 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7451 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7452 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7455 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7457 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7458 gtk_widget_show_all(menubar);
7460 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7462 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7464 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7467 if (prefs_common.toolbar_detachable) {
7468 handlebox = gtk_handle_box_new();
7470 handlebox = gtk_hbox_new(FALSE, 0);
7472 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7474 gtk_widget_realize(handlebox);
7476 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7479 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7483 vbox2 = gtk_vbox_new(FALSE, 2);
7484 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7485 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7488 notebook = gtk_notebook_new();
7489 gtk_widget_set_size_request(notebook, -1, prefs_common.compose_notebook_height);
7490 gtk_widget_show(notebook);
7492 /* header labels and entries */
7493 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7494 compose_create_header(compose),
7495 gtk_label_new_with_mnemonic(_("Hea_der")));
7496 /* attachment list */
7497 attach_hbox = gtk_hbox_new(FALSE, 0);
7498 gtk_widget_show(attach_hbox);
7500 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7501 gtk_widget_show(attach_lab1);
7502 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7504 attach_lab2 = gtk_label_new("");
7505 gtk_widget_show(attach_lab2);
7506 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7508 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7509 compose_create_attach(compose),
7512 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7513 compose_create_others(compose),
7514 gtk_label_new_with_mnemonic(_("Othe_rs")));
7517 subject_hbox = gtk_hbox_new(FALSE, 0);
7518 gtk_widget_show(subject_hbox);
7520 subject_frame = gtk_frame_new(NULL);
7521 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7522 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7523 gtk_widget_show(subject_frame);
7525 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7526 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7527 gtk_widget_show(subject);
7529 label = gtk_label_new(_("Subject:"));
7530 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7531 gtk_widget_show(label);
7534 subject_entry = claws_spell_entry_new();
7536 subject_entry = gtk_entry_new();
7538 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7539 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7540 G_CALLBACK(compose_grab_focus_cb), compose);
7541 gtk_widget_show(subject_entry);
7542 compose->subject_entry = subject_entry;
7543 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7545 edit_vbox = gtk_vbox_new(FALSE, 0);
7547 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7550 ruler_hbox = gtk_hbox_new(FALSE, 0);
7551 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7553 ruler = gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL);
7554 gtk_shruler_set_range(GTK_SHRULER(ruler), 0.0, 100.0, 1.0);
7555 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7559 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7560 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7561 GTK_POLICY_AUTOMATIC,
7562 GTK_POLICY_AUTOMATIC);
7563 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7565 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7567 text = gtk_text_view_new();
7568 if (prefs_common.show_compose_margin) {
7569 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7570 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7572 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7573 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7574 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7575 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7576 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7578 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7579 g_signal_connect(G_OBJECT(notebook), "size_allocate",
7580 G_CALLBACK(compose_notebook_size_alloc), compose);
7581 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7582 G_CALLBACK(compose_edit_size_alloc),
7584 g_signal_connect(G_OBJECT(buffer), "changed",
7585 G_CALLBACK(compose_changed_cb), compose);
7586 g_signal_connect(G_OBJECT(text), "grab_focus",
7587 G_CALLBACK(compose_grab_focus_cb), compose);
7588 g_signal_connect(G_OBJECT(buffer), "insert_text",
7589 G_CALLBACK(text_inserted), compose);
7590 g_signal_connect(G_OBJECT(text), "button_press_event",
7591 G_CALLBACK(text_clicked), compose);
7593 g_signal_connect(G_OBJECT(text), "popup-menu",
7594 G_CALLBACK(compose_popup_menu), compose);
7596 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7597 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7598 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7599 G_CALLBACK(compose_popup_menu), compose);
7601 g_signal_connect(G_OBJECT(subject_entry), "changed",
7602 G_CALLBACK(compose_changed_cb), compose);
7605 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7606 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7607 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7608 g_signal_connect(G_OBJECT(text), "drag_data_received",
7609 G_CALLBACK(compose_insert_drag_received_cb),
7611 g_signal_connect(G_OBJECT(text), "drag-drop",
7612 G_CALLBACK(compose_drag_drop),
7614 gtk_widget_show_all(vbox);
7616 /* pane between attach clist and text */
7617 paned = gtk_vpaned_new();
7618 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7620 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7621 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7623 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7625 gtk_paned_add1(GTK_PANED(paned), notebook);
7626 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7627 gtk_widget_show_all(paned);
7630 if (prefs_common.textfont) {
7631 PangoFontDescription *font_desc;
7633 font_desc = pango_font_description_from_string
7634 (prefs_common.textfont);
7636 gtk_widget_modify_font(text, font_desc);
7637 pango_font_description_free(font_desc);
7641 gtk_action_group_add_actions(action_group, compose_popup_entries,
7642 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7643 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7644 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7645 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7646 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7647 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7648 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7650 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7652 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7653 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7654 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7656 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7658 undostruct = undo_init(text);
7659 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7662 address_completion_start(window);
7664 compose->window = window;
7665 compose->vbox = vbox;
7666 compose->menubar = menubar;
7667 compose->handlebox = handlebox;
7669 compose->vbox2 = vbox2;
7671 compose->paned = paned;
7673 compose->attach_label = attach_lab2;
7675 compose->notebook = notebook;
7676 compose->edit_vbox = edit_vbox;
7677 compose->ruler_hbox = ruler_hbox;
7678 compose->ruler = ruler;
7679 compose->scrolledwin = scrolledwin;
7680 compose->text = text;
7682 compose->focused_editable = NULL;
7684 compose->popupmenu = popupmenu;
7686 compose->tmpl_menu = tmpl_menu;
7688 compose->mode = mode;
7689 compose->rmode = mode;
7691 compose->targetinfo = NULL;
7692 compose->replyinfo = NULL;
7693 compose->fwdinfo = NULL;
7695 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7696 g_str_equal, (GDestroyNotify) g_free, NULL);
7698 compose->replyto = NULL;
7700 compose->bcc = NULL;
7701 compose->followup_to = NULL;
7703 compose->ml_post = NULL;
7705 compose->inreplyto = NULL;
7706 compose->references = NULL;
7707 compose->msgid = NULL;
7708 compose->boundary = NULL;
7710 compose->autowrap = prefs_common.autowrap;
7711 compose->autoindent = prefs_common.auto_indent;
7712 compose->use_signing = FALSE;
7713 compose->use_encryption = FALSE;
7714 compose->privacy_system = NULL;
7716 compose->modified = FALSE;
7718 compose->return_receipt = FALSE;
7720 compose->to_list = NULL;
7721 compose->newsgroup_list = NULL;
7723 compose->undostruct = undostruct;
7725 compose->sig_str = NULL;
7727 compose->exteditor_file = NULL;
7728 compose->exteditor_pid = -1;
7729 compose->exteditor_tag = -1;
7730 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7733 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7734 if (mode != COMPOSE_REDIRECT) {
7735 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7736 strcmp(prefs_common.dictionary, "")) {
7737 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7738 prefs_common.alt_dictionary,
7739 conv_get_locale_charset_str(),
7740 prefs_common.misspelled_col,
7741 prefs_common.check_while_typing,
7742 prefs_common.recheck_when_changing_dict,
7743 prefs_common.use_alternate,
7744 prefs_common.use_both_dicts,
7745 GTK_TEXT_VIEW(text),
7746 GTK_WINDOW(compose->window),
7747 compose_dict_changed,
7748 compose_spell_menu_changed,
7751 alertpanel_error(_("Spell checker could not "
7753 gtkaspell_checkers_strerror());
7754 gtkaspell_checkers_reset_error();
7756 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7760 compose->gtkaspell = gtkaspell;
7761 compose_spell_menu_changed(compose);
7762 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7765 compose_select_account(compose, account, TRUE);
7767 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7768 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7770 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7771 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7773 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7774 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7776 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7777 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7779 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7780 if (account->protocol != A_NNTP)
7781 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7782 prefs_common_translated_header_name("To:"));
7784 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7785 prefs_common_translated_header_name("Newsgroups:"));
7787 addressbook_set_target_compose(compose);
7789 if (mode != COMPOSE_REDIRECT)
7790 compose_set_template_menu(compose);
7792 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7795 compose_list = g_list_append(compose_list, compose);
7797 if (!prefs_common.show_ruler)
7798 gtk_widget_hide(ruler_hbox);
7800 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7803 compose->priority = PRIORITY_NORMAL;
7804 compose_update_priority_menu_item(compose);
7806 compose_set_out_encoding(compose);
7809 compose_update_actions_menu(compose);
7811 /* Privacy Systems menu */
7812 compose_update_privacy_systems_menu(compose);
7814 activate_privacy_system(compose, account, TRUE);
7815 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7817 gtk_widget_realize(window);
7819 gtk_widget_show(window);
7821 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7822 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7829 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7834 GtkWidget *optmenubox;
7837 GtkWidget *from_name = NULL;
7838 #if !(GTK_CHECK_VERSION(2,12,0))
7839 GtkTooltips *tips = compose->tooltips;
7842 gint num = 0, def_menu = 0;
7844 accounts = account_get_list();
7845 cm_return_val_if_fail(accounts != NULL, NULL);
7847 optmenubox = gtk_event_box_new();
7848 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7849 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7851 hbox = gtk_hbox_new(FALSE, 6);
7852 from_name = gtk_entry_new();
7854 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7855 G_CALLBACK(compose_grab_focus_cb), compose);
7857 for (; accounts != NULL; accounts = accounts->next, num++) {
7858 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7859 gchar *name, *from = NULL;
7861 if (ac == compose->account) def_menu = num;
7863 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7866 if (ac == compose->account) {
7867 if (ac->name && *ac->name) {
7869 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7870 from = g_strdup_printf("%s <%s>",
7872 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7874 from = g_strdup_printf("%s",
7876 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7879 COMBOBOX_ADD(menu, name, ac->account_id);
7884 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7886 g_signal_connect(G_OBJECT(optmenu), "changed",
7887 G_CALLBACK(account_activated),
7889 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7890 G_CALLBACK(compose_entry_popup_extend),
7893 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7894 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7896 CLAWS_SET_TIP(optmenubox,
7897 _("Account to use for this email"));
7898 CLAWS_SET_TIP(from_name,
7899 _("Sender address to be used"));
7901 compose->account_combo = optmenu;
7902 compose->from_name = from_name;
7907 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7909 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7910 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7911 Compose *compose = (Compose *) data;
7913 compose->priority = value;
7917 static void compose_reply_change_mode(Compose *compose,
7920 gboolean was_modified = compose->modified;
7922 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7924 cm_return_if_fail(compose->replyinfo != NULL);
7926 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7928 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7930 if (action == COMPOSE_REPLY_TO_ALL)
7932 if (action == COMPOSE_REPLY_TO_SENDER)
7934 if (action == COMPOSE_REPLY_TO_LIST)
7937 compose_remove_header_entries(compose);
7938 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7939 if (compose->account->set_autocc && compose->account->auto_cc)
7940 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7942 if (compose->account->set_autobcc && compose->account->auto_bcc)
7943 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7945 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7946 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7947 compose_show_first_last_header(compose, TRUE);
7948 compose->modified = was_modified;
7949 compose_set_title(compose);
7952 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7954 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7955 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7956 Compose *compose = (Compose *) data;
7959 compose_reply_change_mode(compose, value);
7962 static void compose_update_priority_menu_item(Compose * compose)
7964 GtkWidget *menuitem = NULL;
7965 switch (compose->priority) {
7966 case PRIORITY_HIGHEST:
7967 menuitem = gtk_ui_manager_get_widget
7968 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7971 menuitem = gtk_ui_manager_get_widget
7972 (compose->ui_manager, "/Menu/Options/Priority/High");
7974 case PRIORITY_NORMAL:
7975 menuitem = gtk_ui_manager_get_widget
7976 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7979 menuitem = gtk_ui_manager_get_widget
7980 (compose->ui_manager, "/Menu/Options/Priority/Low");
7982 case PRIORITY_LOWEST:
7983 menuitem = gtk_ui_manager_get_widget
7984 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7987 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7990 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7992 Compose *compose = (Compose *) data;
7994 gboolean can_sign = FALSE, can_encrypt = FALSE;
7996 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7998 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
8001 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
8002 g_free(compose->privacy_system);
8003 compose->privacy_system = NULL;
8004 if (systemid != NULL) {
8005 compose->privacy_system = g_strdup(systemid);
8007 can_sign = privacy_system_can_sign(systemid);
8008 can_encrypt = privacy_system_can_encrypt(systemid);
8011 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
8013 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8014 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8017 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
8019 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8020 GtkWidget *menuitem = NULL;
8021 GList *children, *amenu;
8022 gboolean can_sign = FALSE, can_encrypt = FALSE;
8023 gboolean found = FALSE;
8025 if (compose->privacy_system != NULL) {
8027 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8028 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
8029 cm_return_if_fail(menuitem != NULL);
8031 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
8034 while (amenu != NULL) {
8035 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
8036 if (systemid != NULL) {
8037 if (strcmp(systemid, compose->privacy_system) == 0 &&
8038 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8039 menuitem = GTK_WIDGET(amenu->data);
8041 can_sign = privacy_system_can_sign(systemid);
8042 can_encrypt = privacy_system_can_encrypt(systemid);
8046 } else if (strlen(compose->privacy_system) == 0 &&
8047 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8048 menuitem = GTK_WIDGET(amenu->data);
8051 can_encrypt = FALSE;
8056 amenu = amenu->next;
8058 g_list_free(children);
8059 if (menuitem != NULL)
8060 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8062 if (warn && !found && strlen(compose->privacy_system)) {
8063 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8064 "will not be able to sign or encrypt this message."),
8065 compose->privacy_system);
8069 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8070 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8073 static void compose_set_out_encoding(Compose *compose)
8075 CharSet out_encoding;
8076 const gchar *branch = NULL;
8077 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
8079 switch(out_encoding) {
8080 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8081 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
8082 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
8083 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
8084 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
8085 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
8086 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
8087 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
8088 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
8089 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
8090 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
8091 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
8092 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
8093 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
8094 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
8095 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
8096 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
8097 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
8098 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
8099 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
8100 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
8101 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
8102 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
8103 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
8104 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
8105 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
8106 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
8107 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
8108 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
8109 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
8110 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
8111 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
8112 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8114 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8117 static void compose_set_template_menu(Compose *compose)
8119 GSList *tmpl_list, *cur;
8123 tmpl_list = template_get_config();
8125 menu = gtk_menu_new();
8127 gtk_menu_set_accel_group (GTK_MENU (menu),
8128 gtk_ui_manager_get_accel_group(compose->ui_manager));
8129 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8130 Template *tmpl = (Template *)cur->data;
8131 gchar *accel_path = NULL;
8132 item = gtk_menu_item_new_with_label(tmpl->name);
8133 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8134 g_signal_connect(G_OBJECT(item), "activate",
8135 G_CALLBACK(compose_template_activate_cb),
8137 g_object_set_data(G_OBJECT(item), "template", tmpl);
8138 gtk_widget_show(item);
8139 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8140 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8144 gtk_widget_show(menu);
8145 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8148 void compose_update_actions_menu(Compose *compose)
8150 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8153 static void compose_update_privacy_systems_menu(Compose *compose)
8155 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8156 GSList *systems, *cur;
8158 GtkWidget *system_none;
8160 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8161 GtkWidget *privacy_menu = gtk_menu_new();
8163 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8164 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8166 g_signal_connect(G_OBJECT(system_none), "activate",
8167 G_CALLBACK(compose_set_privacy_system_cb), compose);
8169 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8170 gtk_widget_show(system_none);
8172 systems = privacy_get_system_ids();
8173 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8174 gchar *systemid = cur->data;
8176 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8177 widget = gtk_radio_menu_item_new_with_label(group,
8178 privacy_system_get_name(systemid));
8179 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8180 g_strdup(systemid), g_free);
8181 g_signal_connect(G_OBJECT(widget), "activate",
8182 G_CALLBACK(compose_set_privacy_system_cb), compose);
8184 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8185 gtk_widget_show(widget);
8188 g_slist_free(systems);
8189 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8190 gtk_widget_show_all(privacy_menu);
8191 gtk_widget_show_all(privacy_menuitem);
8194 void compose_reflect_prefs_all(void)
8199 for (cur = compose_list; cur != NULL; cur = cur->next) {
8200 compose = (Compose *)cur->data;
8201 compose_set_template_menu(compose);
8205 void compose_reflect_prefs_pixmap_theme(void)
8210 for (cur = compose_list; cur != NULL; cur = cur->next) {
8211 compose = (Compose *)cur->data;
8212 toolbar_update(TOOLBAR_COMPOSE, compose);
8216 static const gchar *compose_quote_char_from_context(Compose *compose)
8218 const gchar *qmark = NULL;
8220 cm_return_val_if_fail(compose != NULL, NULL);
8222 switch (compose->mode) {
8223 /* use forward-specific quote char */
8224 case COMPOSE_FORWARD:
8225 case COMPOSE_FORWARD_AS_ATTACH:
8226 case COMPOSE_FORWARD_INLINE:
8227 if (compose->folder && compose->folder->prefs &&
8228 compose->folder->prefs->forward_with_format)
8229 qmark = compose->folder->prefs->forward_quotemark;
8230 else if (compose->account->forward_with_format)
8231 qmark = compose->account->forward_quotemark;
8233 qmark = prefs_common.fw_quotemark;
8236 /* use reply-specific quote char in all other modes */
8238 if (compose->folder && compose->folder->prefs &&
8239 compose->folder->prefs->reply_with_format)
8240 qmark = compose->folder->prefs->reply_quotemark;
8241 else if (compose->account->reply_with_format)
8242 qmark = compose->account->reply_quotemark;
8244 qmark = prefs_common.quotemark;
8248 if (qmark == NULL || *qmark == '\0')
8254 static void compose_template_apply(Compose *compose, Template *tmpl,
8258 GtkTextBuffer *buffer;
8262 gchar *parsed_str = NULL;
8263 gint cursor_pos = 0;
8264 const gchar *err_msg = _("The body of the template has an error at line %d.");
8267 /* process the body */
8269 text = GTK_TEXT_VIEW(compose->text);
8270 buffer = gtk_text_view_get_buffer(text);
8273 qmark = compose_quote_char_from_context(compose);
8275 if (compose->replyinfo != NULL) {
8278 gtk_text_buffer_set_text(buffer, "", -1);
8279 mark = gtk_text_buffer_get_insert(buffer);
8280 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8282 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8283 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8285 } else if (compose->fwdinfo != NULL) {
8288 gtk_text_buffer_set_text(buffer, "", -1);
8289 mark = gtk_text_buffer_get_insert(buffer);
8290 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8292 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8293 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8296 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8298 GtkTextIter start, end;
8301 gtk_text_buffer_get_start_iter(buffer, &start);
8302 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8303 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8305 /* clear the buffer now */
8307 gtk_text_buffer_set_text(buffer, "", -1);
8309 parsed_str = compose_quote_fmt(compose, dummyinfo,
8310 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8311 procmsg_msginfo_free( dummyinfo );
8317 gtk_text_buffer_set_text(buffer, "", -1);
8318 mark = gtk_text_buffer_get_insert(buffer);
8319 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8322 if (replace && parsed_str && compose->account->auto_sig)
8323 compose_insert_sig(compose, FALSE);
8325 if (replace && parsed_str) {
8326 gtk_text_buffer_get_start_iter(buffer, &iter);
8327 gtk_text_buffer_place_cursor(buffer, &iter);
8331 cursor_pos = quote_fmt_get_cursor_pos();
8332 compose->set_cursor_pos = cursor_pos;
8333 if (cursor_pos == -1)
8335 gtk_text_buffer_get_start_iter(buffer, &iter);
8336 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8337 gtk_text_buffer_place_cursor(buffer, &iter);
8340 /* process the other fields */
8342 compose_template_apply_fields(compose, tmpl);
8343 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8344 quote_fmt_reset_vartable();
8345 compose_changed_cb(NULL, compose);
8348 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8349 gtkaspell_highlight_all(compose->gtkaspell);
8353 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8355 MsgInfo* dummyinfo = NULL;
8356 MsgInfo *msginfo = NULL;
8359 if (compose->replyinfo != NULL)
8360 msginfo = compose->replyinfo;
8361 else if (compose->fwdinfo != NULL)
8362 msginfo = compose->fwdinfo;
8364 dummyinfo = compose_msginfo_new_from_compose(compose);
8365 msginfo = dummyinfo;
8368 if (tmpl->from && *tmpl->from != '\0') {
8370 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8371 compose->gtkaspell);
8373 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8375 quote_fmt_scan_string(tmpl->from);
8378 buf = quote_fmt_get_buffer();
8380 alertpanel_error(_("Template From format error."));
8382 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8386 if (tmpl->to && *tmpl->to != '\0') {
8388 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8389 compose->gtkaspell);
8391 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8393 quote_fmt_scan_string(tmpl->to);
8396 buf = quote_fmt_get_buffer();
8398 alertpanel_error(_("Template To format error."));
8400 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8404 if (tmpl->cc && *tmpl->cc != '\0') {
8406 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8407 compose->gtkaspell);
8409 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8411 quote_fmt_scan_string(tmpl->cc);
8414 buf = quote_fmt_get_buffer();
8416 alertpanel_error(_("Template Cc format error."));
8418 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8422 if (tmpl->bcc && *tmpl->bcc != '\0') {
8424 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8425 compose->gtkaspell);
8427 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8429 quote_fmt_scan_string(tmpl->bcc);
8432 buf = quote_fmt_get_buffer();
8434 alertpanel_error(_("Template Bcc format error."));
8436 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8440 /* process the subject */
8441 if (tmpl->subject && *tmpl->subject != '\0') {
8443 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8444 compose->gtkaspell);
8446 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8448 quote_fmt_scan_string(tmpl->subject);
8451 buf = quote_fmt_get_buffer();
8453 alertpanel_error(_("Template subject format error."));
8455 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8459 procmsg_msginfo_free( dummyinfo );
8462 static void compose_destroy(Compose *compose)
8464 GtkAllocation allocation;
8465 GtkTextBuffer *buffer;
8466 GtkClipboard *clipboard;
8468 compose_list = g_list_remove(compose_list, compose);
8470 if (compose->updating) {
8471 debug_print("danger, not destroying anything now\n");
8472 compose->deferred_destroy = TRUE;
8475 /* NOTE: address_completion_end() does nothing with the window
8476 * however this may change. */
8477 address_completion_end(compose->window);
8479 slist_free_strings(compose->to_list);
8480 g_slist_free(compose->to_list);
8481 slist_free_strings(compose->newsgroup_list);
8482 g_slist_free(compose->newsgroup_list);
8483 slist_free_strings(compose->header_list);
8484 g_slist_free(compose->header_list);
8486 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8488 g_hash_table_destroy(compose->email_hashtable);
8490 procmsg_msginfo_free(compose->targetinfo);
8491 procmsg_msginfo_free(compose->replyinfo);
8492 procmsg_msginfo_free(compose->fwdinfo);
8494 g_free(compose->replyto);
8495 g_free(compose->cc);
8496 g_free(compose->bcc);
8497 g_free(compose->newsgroups);
8498 g_free(compose->followup_to);
8500 g_free(compose->ml_post);
8502 g_free(compose->inreplyto);
8503 g_free(compose->references);
8504 g_free(compose->msgid);
8505 g_free(compose->boundary);
8507 g_free(compose->redirect_filename);
8508 if (compose->undostruct)
8509 undo_destroy(compose->undostruct);
8511 g_free(compose->sig_str);
8513 g_free(compose->exteditor_file);
8515 g_free(compose->orig_charset);
8517 g_free(compose->privacy_system);
8519 if (addressbook_get_target_compose() == compose)
8520 addressbook_set_target_compose(NULL);
8523 if (compose->gtkaspell) {
8524 gtkaspell_delete(compose->gtkaspell);
8525 compose->gtkaspell = NULL;
8529 if (!compose->batch) {
8530 gtk_widget_get_allocation(compose->window, &allocation);
8531 prefs_common.compose_width = allocation.width;
8532 prefs_common.compose_height = allocation.height;
8535 if (!gtk_widget_get_parent(compose->paned))
8536 gtk_widget_destroy(compose->paned);
8537 gtk_widget_destroy(compose->popupmenu);
8539 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8540 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8541 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8543 gtk_widget_destroy(compose->window);
8544 toolbar_destroy(compose->toolbar);
8545 g_free(compose->toolbar);
8546 g_mutex_free(compose->mutex);
8550 static void compose_attach_info_free(AttachInfo *ainfo)
8552 g_free(ainfo->file);
8553 g_free(ainfo->content_type);
8554 g_free(ainfo->name);
8555 g_free(ainfo->charset);
8559 static void compose_attach_update_label(Compose *compose)
8564 GtkTreeModel *model;
8569 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8570 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8571 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8575 while(gtk_tree_model_iter_next(model, &iter))
8578 text = g_strdup_printf("(%d)", i);
8579 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8583 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8585 Compose *compose = (Compose *)data;
8586 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8587 GtkTreeSelection *selection;
8589 GtkTreeModel *model;
8591 selection = gtk_tree_view_get_selection(tree_view);
8592 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8597 for (cur = sel; cur != NULL; cur = cur->next) {
8598 GtkTreePath *path = cur->data;
8599 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8602 gtk_tree_path_free(path);
8605 for (cur = sel; cur != NULL; cur = cur->next) {
8606 GtkTreeRowReference *ref = cur->data;
8607 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8610 if (gtk_tree_model_get_iter(model, &iter, path))
8611 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8613 gtk_tree_path_free(path);
8614 gtk_tree_row_reference_free(ref);
8618 compose_attach_update_label(compose);
8621 static struct _AttachProperty
8624 GtkWidget *mimetype_entry;
8625 GtkWidget *encoding_optmenu;
8626 GtkWidget *path_entry;
8627 GtkWidget *filename_entry;
8629 GtkWidget *cancel_btn;
8632 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8634 gtk_tree_path_free((GtkTreePath *)ptr);
8637 static void compose_attach_property(GtkAction *action, gpointer data)
8639 Compose *compose = (Compose *)data;
8640 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8642 GtkComboBox *optmenu;
8643 GtkTreeSelection *selection;
8645 GtkTreeModel *model;
8648 static gboolean cancelled;
8650 /* only if one selected */
8651 selection = gtk_tree_view_get_selection(tree_view);
8652 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8655 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8659 path = (GtkTreePath *) sel->data;
8660 gtk_tree_model_get_iter(model, &iter, path);
8661 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8664 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8670 if (!attach_prop.window)
8671 compose_attach_property_create(&cancelled);
8672 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8673 gtk_widget_grab_focus(attach_prop.ok_btn);
8674 gtk_widget_show(attach_prop.window);
8675 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8677 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8678 if (ainfo->encoding == ENC_UNKNOWN)
8679 combobox_select_by_data(optmenu, ENC_BASE64);
8681 combobox_select_by_data(optmenu, ainfo->encoding);
8683 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8684 ainfo->content_type ? ainfo->content_type : "");
8685 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8686 ainfo->file ? ainfo->file : "");
8687 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8688 ainfo->name ? ainfo->name : "");
8691 const gchar *entry_text;
8693 gchar *cnttype = NULL;
8700 gtk_widget_hide(attach_prop.window);
8701 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8706 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8707 if (*entry_text != '\0') {
8710 text = g_strstrip(g_strdup(entry_text));
8711 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8712 cnttype = g_strdup(text);
8715 alertpanel_error(_("Invalid MIME type."));
8721 ainfo->encoding = combobox_get_active_data(optmenu);
8723 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8724 if (*entry_text != '\0') {
8725 if (is_file_exist(entry_text) &&
8726 (size = get_file_size(entry_text)) > 0)
8727 file = g_strdup(entry_text);
8730 (_("File doesn't exist or is empty."));
8736 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8737 if (*entry_text != '\0') {
8738 g_free(ainfo->name);
8739 ainfo->name = g_strdup(entry_text);
8743 g_free(ainfo->content_type);
8744 ainfo->content_type = cnttype;
8747 g_free(ainfo->file);
8751 ainfo->size = (goffset)size;
8753 /* update tree store */
8754 text = to_human_readable(ainfo->size);
8755 gtk_tree_model_get_iter(model, &iter, path);
8756 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8757 COL_MIMETYPE, ainfo->content_type,
8759 COL_NAME, ainfo->name,
8760 COL_CHARSET, ainfo->charset,
8766 gtk_tree_path_free(path);
8769 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8771 label = gtk_label_new(str); \
8772 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8773 GTK_FILL, 0, 0, 0); \
8774 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8776 entry = gtk_entry_new(); \
8777 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8778 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8781 static void compose_attach_property_create(gboolean *cancelled)
8787 GtkWidget *mimetype_entry;
8790 GtkListStore *optmenu_menu;
8791 GtkWidget *path_entry;
8792 GtkWidget *filename_entry;
8795 GtkWidget *cancel_btn;
8796 GList *mime_type_list, *strlist;
8799 debug_print("Creating attach_property window...\n");
8801 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8802 gtk_widget_set_size_request(window, 480, -1);
8803 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8804 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8805 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8806 g_signal_connect(G_OBJECT(window), "delete_event",
8807 G_CALLBACK(attach_property_delete_event),
8809 g_signal_connect(G_OBJECT(window), "key_press_event",
8810 G_CALLBACK(attach_property_key_pressed),
8813 vbox = gtk_vbox_new(FALSE, 8);
8814 gtk_container_add(GTK_CONTAINER(window), vbox);
8816 table = gtk_table_new(4, 2, FALSE);
8817 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8818 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8819 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8821 label = gtk_label_new(_("MIME type"));
8822 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8824 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8825 mimetype_entry = gtk_combo_box_entry_new_text();
8826 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8827 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8829 /* stuff with list */
8830 mime_type_list = procmime_get_mime_type_list();
8832 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8833 MimeType *type = (MimeType *) mime_type_list->data;
8836 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8838 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8841 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8842 (GCompareFunc)strcmp2);
8845 for (mime_type_list = strlist; mime_type_list != NULL;
8846 mime_type_list = mime_type_list->next) {
8847 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8848 g_free(mime_type_list->data);
8850 g_list_free(strlist);
8851 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8852 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8854 label = gtk_label_new(_("Encoding"));
8855 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8857 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8859 hbox = gtk_hbox_new(FALSE, 0);
8860 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8861 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8863 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8864 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8866 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8867 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8868 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8869 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8870 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8872 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8874 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8875 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8877 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8878 &ok_btn, GTK_STOCK_OK,
8880 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8881 gtk_widget_grab_default(ok_btn);
8883 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8884 G_CALLBACK(attach_property_ok),
8886 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8887 G_CALLBACK(attach_property_cancel),
8890 gtk_widget_show_all(vbox);
8892 attach_prop.window = window;
8893 attach_prop.mimetype_entry = mimetype_entry;
8894 attach_prop.encoding_optmenu = optmenu;
8895 attach_prop.path_entry = path_entry;
8896 attach_prop.filename_entry = filename_entry;
8897 attach_prop.ok_btn = ok_btn;
8898 attach_prop.cancel_btn = cancel_btn;
8901 #undef SET_LABEL_AND_ENTRY
8903 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8909 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8915 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8916 gboolean *cancelled)
8924 static gboolean attach_property_key_pressed(GtkWidget *widget,
8926 gboolean *cancelled)
8928 if (event && event->keyval == GDK_KEY_Escape) {
8932 if (event && event->keyval == GDK_KEY_Return) {
8940 static void compose_exec_ext_editor(Compose *compose)
8947 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8948 G_DIR_SEPARATOR, compose);
8950 if (pipe(pipe_fds) < 0) {
8956 if ((pid = fork()) < 0) {
8963 /* close the write side of the pipe */
8966 compose->exteditor_file = g_strdup(tmp);
8967 compose->exteditor_pid = pid;
8969 compose_set_ext_editor_sensitive(compose, FALSE);
8972 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8974 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8976 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8980 } else { /* process-monitoring process */
8986 /* close the read side of the pipe */
8989 if (compose_write_body_to_file(compose, tmp) < 0) {
8990 fd_write_all(pipe_fds[1], "2\n", 2);
8994 pid_ed = compose_exec_ext_editor_real(tmp);
8996 fd_write_all(pipe_fds[1], "1\n", 2);
9000 /* wait until editor is terminated */
9001 waitpid(pid_ed, NULL, 0);
9003 fd_write_all(pipe_fds[1], "0\n", 2);
9010 #endif /* G_OS_UNIX */
9014 static gint compose_exec_ext_editor_real(const gchar *file)
9021 cm_return_val_if_fail(file != NULL, -1);
9023 if ((pid = fork()) < 0) {
9028 if (pid != 0) return pid;
9030 /* grandchild process */
9032 if (setpgid(0, getppid()))
9035 if (prefs_common_get_ext_editor_cmd() &&
9036 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
9037 *(p + 1) == 's' && !strchr(p + 2, '%')) {
9038 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
9040 if (prefs_common_get_ext_editor_cmd())
9041 g_warning("External editor command-line is invalid: '%s'\n",
9042 prefs_common_get_ext_editor_cmd());
9043 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
9046 cmdline = strsplit_with_quote(buf, " ", 1024);
9047 execvp(cmdline[0], cmdline);
9050 g_strfreev(cmdline);
9055 static gboolean compose_ext_editor_kill(Compose *compose)
9057 pid_t pgid = compose->exteditor_pid * -1;
9060 ret = kill(pgid, 0);
9062 if (ret == 0 || (ret == -1 && EPERM == errno)) {
9066 msg = g_strdup_printf
9067 (_("The external editor is still working.\n"
9068 "Force terminating the process?\n"
9069 "process group id: %d"), -pgid);
9070 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
9071 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
9075 if (val == G_ALERTALTERNATE) {
9076 g_source_remove(compose->exteditor_tag);
9077 g_io_channel_shutdown(compose->exteditor_ch,
9079 g_io_channel_unref(compose->exteditor_ch);
9081 if (kill(pgid, SIGTERM) < 0) perror("kill");
9082 waitpid(compose->exteditor_pid, NULL, 0);
9084 g_warning("Terminated process group id: %d", -pgid);
9085 g_warning("Temporary file: %s",
9086 compose->exteditor_file);
9088 compose_set_ext_editor_sensitive(compose, TRUE);
9090 g_free(compose->exteditor_file);
9091 compose->exteditor_file = NULL;
9092 compose->exteditor_pid = -1;
9093 compose->exteditor_ch = NULL;
9094 compose->exteditor_tag = -1;
9102 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
9106 Compose *compose = (Compose *)data;
9109 debug_print("Compose: input from monitoring process\n");
9111 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
9113 g_io_channel_shutdown(source, FALSE, NULL);
9114 g_io_channel_unref(source);
9116 waitpid(compose->exteditor_pid, NULL, 0);
9118 if (buf[0] == '0') { /* success */
9119 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9120 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9122 gtk_text_buffer_set_text(buffer, "", -1);
9123 compose_insert_file(compose, compose->exteditor_file);
9124 compose_changed_cb(NULL, compose);
9125 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9127 if (claws_unlink(compose->exteditor_file) < 0)
9128 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9129 } else if (buf[0] == '1') { /* failed */
9130 g_warning("Couldn't exec external editor\n");
9131 if (claws_unlink(compose->exteditor_file) < 0)
9132 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9133 } else if (buf[0] == '2') {
9134 g_warning("Couldn't write to file\n");
9135 } else if (buf[0] == '3') {
9136 g_warning("Pipe read failed\n");
9139 compose_set_ext_editor_sensitive(compose, TRUE);
9141 g_free(compose->exteditor_file);
9142 compose->exteditor_file = NULL;
9143 compose->exteditor_pid = -1;
9144 compose->exteditor_ch = NULL;
9145 compose->exteditor_tag = -1;
9150 static void compose_set_ext_editor_sensitive(Compose *compose,
9153 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9154 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9155 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9156 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9157 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9158 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9159 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9161 gtk_widget_set_sensitive(compose->text, sensitive);
9162 if (compose->toolbar->send_btn)
9163 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
9164 if (compose->toolbar->sendl_btn)
9165 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
9166 if (compose->toolbar->draft_btn)
9167 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
9168 if (compose->toolbar->insert_btn)
9169 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
9170 if (compose->toolbar->sig_btn)
9171 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
9172 if (compose->toolbar->exteditor_btn)
9173 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9174 if (compose->toolbar->linewrap_current_btn)
9175 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9176 if (compose->toolbar->linewrap_all_btn)
9177 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9179 #endif /* G_OS_UNIX */
9182 * compose_undo_state_changed:
9184 * Change the sensivity of the menuentries undo and redo
9186 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9187 gint redo_state, gpointer data)
9189 Compose *compose = (Compose *)data;
9191 switch (undo_state) {
9192 case UNDO_STATE_TRUE:
9193 if (!undostruct->undo_state) {
9194 undostruct->undo_state = TRUE;
9195 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9198 case UNDO_STATE_FALSE:
9199 if (undostruct->undo_state) {
9200 undostruct->undo_state = FALSE;
9201 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9204 case UNDO_STATE_UNCHANGED:
9206 case UNDO_STATE_REFRESH:
9207 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9210 g_warning("Undo state not recognized");
9214 switch (redo_state) {
9215 case UNDO_STATE_TRUE:
9216 if (!undostruct->redo_state) {
9217 undostruct->redo_state = TRUE;
9218 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9221 case UNDO_STATE_FALSE:
9222 if (undostruct->redo_state) {
9223 undostruct->redo_state = FALSE;
9224 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9227 case UNDO_STATE_UNCHANGED:
9229 case UNDO_STATE_REFRESH:
9230 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9233 g_warning("Redo state not recognized");
9238 /* callback functions */
9240 static void compose_notebook_size_alloc(GtkNotebook *notebook,
9241 GtkAllocation *allocation,
9244 prefs_common.compose_notebook_height = allocation->height;
9247 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9248 * includes "non-client" (windows-izm) in calculation, so this calculation
9249 * may not be accurate.
9251 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9252 GtkAllocation *allocation,
9253 GtkSHRuler *shruler)
9255 if (prefs_common.show_ruler) {
9256 gint char_width = 0, char_height = 0;
9257 gint line_width_in_chars;
9259 gtkut_get_font_size(GTK_WIDGET(widget),
9260 &char_width, &char_height);
9261 line_width_in_chars =
9262 (allocation->width - allocation->x) / char_width;
9264 /* got the maximum */
9265 gtk_shruler_set_range(GTK_SHRULER(shruler),
9266 0.0, line_width_in_chars, 0);
9275 ComposePrefType type;
9276 gboolean entry_marked;
9279 static void account_activated(GtkComboBox *optmenu, gpointer data)
9281 Compose *compose = (Compose *)data;
9284 gchar *folderidentifier;
9285 gint account_id = 0;
9288 GSList *list, *saved_list = NULL;
9289 HeaderEntryState *state;
9290 GtkRcStyle *style = NULL;
9291 static GdkColor yellow;
9292 static gboolean color_set = FALSE;
9294 /* Get ID of active account in the combo box */
9295 menu = gtk_combo_box_get_model(optmenu);
9296 gtk_combo_box_get_active_iter(optmenu, &iter);
9297 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9299 ac = account_find_from_id(account_id);
9300 cm_return_if_fail(ac != NULL);
9302 if (ac != compose->account) {
9303 compose_select_account(compose, ac, FALSE);
9305 for (list = compose->header_list; list; list = list->next) {
9306 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9308 if (hentry->type == PREF_ACCOUNT || !list->next) {
9309 compose_destroy_headerentry(compose, hentry);
9313 state = g_malloc0(sizeof(HeaderEntryState));
9314 state->header = gtk_editable_get_chars(GTK_EDITABLE(
9315 gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9316 state->entry = gtk_editable_get_chars(
9317 GTK_EDITABLE(hentry->entry), 0, -1);
9318 state->type = hentry->type;
9321 gdk_color_parse("#f5f6be", &yellow);
9322 color_set = gdk_colormap_alloc_color(
9323 gdk_colormap_get_system(),
9324 &yellow, FALSE, TRUE);
9327 style = gtk_widget_get_modifier_style(hentry->entry);
9328 state->entry_marked = gdk_color_equal(&yellow,
9329 &style->base[GTK_STATE_NORMAL]);
9331 saved_list = g_slist_append(saved_list, state);
9332 compose_destroy_headerentry(compose, hentry);
9335 compose->header_last = NULL;
9336 g_slist_free(compose->header_list);
9337 compose->header_list = NULL;
9338 compose->header_nextrow = 1;
9339 compose_create_header_entry(compose);
9341 if (ac->set_autocc && ac->auto_cc)
9342 compose_entry_append(compose, ac->auto_cc,
9343 COMPOSE_CC, PREF_ACCOUNT);
9345 if (ac->set_autobcc && ac->auto_bcc)
9346 compose_entry_append(compose, ac->auto_bcc,
9347 COMPOSE_BCC, PREF_ACCOUNT);
9349 if (ac->set_autoreplyto && ac->auto_replyto)
9350 compose_entry_append(compose, ac->auto_replyto,
9351 COMPOSE_REPLYTO, PREF_ACCOUNT);
9353 for (list = saved_list; list; list = list->next) {
9354 state = (HeaderEntryState *) list->data;
9356 compose_add_header_entry(compose, state->header,
9357 state->entry, state->type);
9358 if (state->entry_marked)
9359 compose_entry_mark_default_to(compose, state->entry);
9361 g_free(state->header);
9362 g_free(state->entry);
9365 g_slist_free(saved_list);
9367 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9368 (ac->protocol == A_NNTP) ?
9369 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9372 /* Set message save folder */
9373 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9374 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9376 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9377 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9379 compose_set_save_to(compose, NULL);
9380 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9381 folderidentifier = folder_item_get_identifier(account_get_special_folder
9382 (compose->account, F_OUTBOX));
9383 compose_set_save_to(compose, folderidentifier);
9384 g_free(folderidentifier);
9388 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9389 GtkTreeViewColumn *column, Compose *compose)
9391 compose_attach_property(NULL, compose);
9394 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9397 Compose *compose = (Compose *)data;
9398 GtkTreeSelection *attach_selection;
9399 gint attach_nr_selected;
9401 if (!event) return FALSE;
9403 if (event->button == 3) {
9404 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9405 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9407 if (attach_nr_selected > 0)
9409 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9410 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9412 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9413 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9416 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9417 NULL, NULL, event->button, event->time);
9424 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9427 Compose *compose = (Compose *)data;
9429 if (!event) return FALSE;
9431 switch (event->keyval) {
9432 case GDK_KEY_Delete:
9433 compose_attach_remove_selected(NULL, compose);
9439 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9441 toolbar_comp_set_sensitive(compose, allow);
9442 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9443 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9445 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9447 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9448 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9449 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9451 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9455 static void compose_send_cb(GtkAction *action, gpointer data)
9457 Compose *compose = (Compose *)data;
9459 if (prefs_common.work_offline &&
9460 !inc_offline_should_override(TRUE,
9461 _("Claws Mail needs network access in order "
9462 "to send this email.")))
9465 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9466 g_source_remove(compose->draft_timeout_tag);
9467 compose->draft_timeout_tag = -1;
9470 compose_send(compose);
9473 static void compose_send_later_cb(GtkAction *action, gpointer data)
9475 Compose *compose = (Compose *)data;
9479 compose_allow_user_actions(compose, FALSE);
9480 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9481 compose_allow_user_actions(compose, TRUE);
9485 compose_close(compose);
9486 } else if (val == -1) {
9487 alertpanel_error(_("Could not queue message."));
9488 } else if (val == -2) {
9489 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9490 } else if (val == -3) {
9491 if (privacy_peek_error())
9492 alertpanel_error(_("Could not queue message for sending:\n\n"
9493 "Signature failed: %s"), privacy_get_error());
9494 } else if (val == -4) {
9495 alertpanel_error(_("Could not queue message for sending:\n\n"
9496 "Charset conversion failed."));
9497 } else if (val == -5) {
9498 alertpanel_error(_("Could not queue message for sending:\n\n"
9499 "Couldn't get recipient encryption key."));
9500 } else if (val == -6) {
9503 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9506 #define DRAFTED_AT_EXIT "drafted_at_exit"
9507 static void compose_register_draft(MsgInfo *info)
9509 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9510 DRAFTED_AT_EXIT, NULL);
9511 FILE *fp = g_fopen(filepath, "ab");
9514 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9522 gboolean compose_draft (gpointer data, guint action)
9524 Compose *compose = (Compose *)data;
9529 MsgFlags flag = {0, 0};
9530 static gboolean lock = FALSE;
9531 MsgInfo *newmsginfo;
9533 gboolean target_locked = FALSE;
9534 gboolean err = FALSE;
9536 if (lock) return FALSE;
9538 if (compose->sending)
9541 draft = account_get_special_folder(compose->account, F_DRAFT);
9542 cm_return_val_if_fail(draft != NULL, FALSE);
9544 if (!g_mutex_trylock(compose->mutex)) {
9545 /* we don't want to lock the mutex once it's available,
9546 * because as the only other part of compose.c locking
9547 * it is compose_close - which means once unlocked,
9548 * the compose struct will be freed */
9549 debug_print("couldn't lock mutex, probably sending\n");
9555 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9556 G_DIR_SEPARATOR, compose);
9557 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9558 FILE_OP_ERROR(tmp, "fopen");
9562 /* chmod for security */
9563 if (change_file_mode_rw(fp, tmp) < 0) {
9564 FILE_OP_ERROR(tmp, "chmod");
9565 g_warning("can't change file mode\n");
9568 /* Save draft infos */
9569 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9570 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9572 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9573 gchar *savefolderid;
9575 savefolderid = compose_get_save_to(compose);
9576 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9577 g_free(savefolderid);
9579 if (compose->return_receipt) {
9580 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9582 if (compose->privacy_system) {
9583 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9584 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9585 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9588 /* Message-ID of message replying to */
9589 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9592 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9593 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9596 /* Message-ID of message forwarding to */
9597 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9600 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9601 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9605 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9606 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9608 sheaders = compose_get_manual_headers_info(compose);
9609 err |= (fprintf(fp, "X-Claws-Manual-Headers:%s\n", sheaders) < 0);
9612 /* end of headers */
9613 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9620 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9624 if (fclose(fp) == EOF) {
9628 if (compose->targetinfo) {
9629 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9630 flag.perm_flags = target_locked?MSG_LOCKED:0;
9632 flag.tmp_flags = MSG_DRAFT;
9634 folder_item_scan(draft);
9635 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9636 MsgInfo *tmpinfo = NULL;
9637 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9638 if (compose->msgid) {
9639 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9642 msgnum = tmpinfo->msgnum;
9643 procmsg_msginfo_free(tmpinfo);
9644 debug_print("got draft msgnum %d from scanning\n", msgnum);
9646 debug_print("didn't get draft msgnum after scanning\n");
9649 debug_print("got draft msgnum %d from adding\n", msgnum);
9655 if (action != COMPOSE_AUTO_SAVE) {
9656 if (action != COMPOSE_DRAFT_FOR_EXIT)
9657 alertpanel_error(_("Could not save draft."));
9660 gtkut_window_popup(compose->window);
9661 val = alertpanel_full(_("Could not save draft"),
9662 _("Could not save draft.\n"
9663 "Do you want to cancel exit or discard this email?"),
9664 _("_Cancel exit"), _("_Discard email"), NULL,
9665 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9666 if (val == G_ALERTALTERNATE) {
9668 g_mutex_unlock(compose->mutex); /* must be done before closing */
9669 compose_close(compose);
9673 g_mutex_unlock(compose->mutex); /* must be done before closing */
9682 if (compose->mode == COMPOSE_REEDIT) {
9683 compose_remove_reedit_target(compose, TRUE);
9686 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9689 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9691 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9693 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9694 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9695 procmsg_msginfo_set_flags(newmsginfo, 0,
9696 MSG_HAS_ATTACHMENT);
9698 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9699 compose_register_draft(newmsginfo);
9701 procmsg_msginfo_free(newmsginfo);
9704 folder_item_scan(draft);
9706 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9708 g_mutex_unlock(compose->mutex); /* must be done before closing */
9709 compose_close(compose);
9715 path = folder_item_fetch_msg(draft, msgnum);
9717 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9720 if (g_stat(path, &s) < 0) {
9721 FILE_OP_ERROR(path, "stat");
9727 procmsg_msginfo_free(compose->targetinfo);
9728 compose->targetinfo = procmsg_msginfo_new();
9729 compose->targetinfo->msgnum = msgnum;
9730 compose->targetinfo->size = (goffset)s.st_size;
9731 compose->targetinfo->mtime = s.st_mtime;
9732 compose->targetinfo->folder = draft;
9734 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9735 compose->mode = COMPOSE_REEDIT;
9737 if (action == COMPOSE_AUTO_SAVE) {
9738 compose->autosaved_draft = compose->targetinfo;
9740 compose->modified = FALSE;
9741 compose_set_title(compose);
9745 g_mutex_unlock(compose->mutex);
9749 void compose_clear_exit_drafts(void)
9751 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9752 DRAFTED_AT_EXIT, NULL);
9753 if (is_file_exist(filepath))
9754 claws_unlink(filepath);
9759 void compose_reopen_exit_drafts(void)
9761 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9762 DRAFTED_AT_EXIT, NULL);
9763 FILE *fp = g_fopen(filepath, "rb");
9767 while (fgets(buf, sizeof(buf), fp)) {
9768 gchar **parts = g_strsplit(buf, "\t", 2);
9769 const gchar *folder = parts[0];
9770 int msgnum = parts[1] ? atoi(parts[1]):-1;
9772 if (folder && *folder && msgnum > -1) {
9773 FolderItem *item = folder_find_item_from_identifier(folder);
9774 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9776 compose_reedit(info, FALSE);
9783 compose_clear_exit_drafts();
9786 static void compose_save_cb(GtkAction *action, gpointer data)
9788 Compose *compose = (Compose *)data;
9789 compose_draft(compose, COMPOSE_KEEP_EDITING);
9790 compose->rmode = COMPOSE_REEDIT;
9793 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9795 if (compose && file_list) {
9798 for ( tmp = file_list; tmp; tmp = tmp->next) {
9799 gchar *file = (gchar *) tmp->data;
9800 gchar *utf8_filename = conv_filename_to_utf8(file);
9801 compose_attach_append(compose, file, utf8_filename, NULL, NULL);
9802 compose_changed_cb(NULL, compose);
9807 g_free(utf8_filename);
9812 static void compose_attach_cb(GtkAction *action, gpointer data)
9814 Compose *compose = (Compose *)data;
9817 if (compose->redirect_filename != NULL)
9820 file_list = filesel_select_multiple_files_open(_("Select file"));
9823 compose_attach_from_list(compose, file_list, TRUE);
9824 g_list_free(file_list);
9828 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9830 Compose *compose = (Compose *)data;
9832 gint files_inserted = 0;
9834 file_list = filesel_select_multiple_files_open(_("Select file"));
9839 for ( tmp = file_list; tmp; tmp = tmp->next) {
9840 gchar *file = (gchar *) tmp->data;
9841 gchar *filedup = g_strdup(file);
9842 gchar *shortfile = g_path_get_basename(filedup);
9843 ComposeInsertResult res;
9844 /* insert the file if the file is short or if the user confirmed that
9845 he/she wants to insert the large file */
9846 res = compose_insert_file(compose, file);
9847 if (res == COMPOSE_INSERT_READ_ERROR) {
9848 alertpanel_error(_("File '%s' could not be read."), shortfile);
9849 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9850 alertpanel_error(_("File '%s' contained invalid characters\n"
9851 "for the current encoding, insertion may be incorrect."),
9853 } else if (res == COMPOSE_INSERT_SUCCESS)
9860 g_list_free(file_list);
9864 if (files_inserted > 0 && compose->gtkaspell &&
9865 compose->gtkaspell->check_while_typing)
9866 gtkaspell_highlight_all(compose->gtkaspell);
9870 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9872 Compose *compose = (Compose *)data;
9874 compose_insert_sig(compose, FALSE);
9877 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9881 Compose *compose = (Compose *)data;
9883 gtkut_widget_get_uposition(widget, &x, &y);
9884 if (!compose->batch) {
9885 prefs_common.compose_x = x;
9886 prefs_common.compose_y = y;
9888 if (compose->sending || compose->updating)
9890 compose_close_cb(NULL, compose);
9894 void compose_close_toolbar(Compose *compose)
9896 compose_close_cb(NULL, compose);
9899 static void compose_close_cb(GtkAction *action, gpointer data)
9901 Compose *compose = (Compose *)data;
9905 if (compose->exteditor_tag != -1) {
9906 if (!compose_ext_editor_kill(compose))
9911 if (compose->modified) {
9912 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9913 if (!g_mutex_trylock(compose->mutex)) {
9914 /* we don't want to lock the mutex once it's available,
9915 * because as the only other part of compose.c locking
9916 * it is compose_close - which means once unlocked,
9917 * the compose struct will be freed */
9918 debug_print("couldn't lock mutex, probably sending\n");
9922 val = alertpanel(_("Discard message"),
9923 _("This message has been modified. Discard it?"),
9924 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9926 val = alertpanel(_("Save changes"),
9927 _("This message has been modified. Save the latest changes?"),
9928 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9930 g_mutex_unlock(compose->mutex);
9932 case G_ALERTDEFAULT:
9933 if (prefs_common.autosave && !reedit)
9934 compose_remove_draft(compose);
9936 case G_ALERTALTERNATE:
9937 compose_draft(data, COMPOSE_QUIT_EDITING);
9944 compose_close(compose);
9947 static void compose_print_cb(GtkAction *action, gpointer data)
9949 Compose *compose = (Compose *) data;
9951 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9952 if (compose->targetinfo)
9953 messageview_print(compose->targetinfo, FALSE, -1, -1, 0);
9956 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9958 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9959 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9960 Compose *compose = (Compose *) data;
9963 compose->out_encoding = (CharSet)value;
9966 static void compose_address_cb(GtkAction *action, gpointer data)
9968 Compose *compose = (Compose *)data;
9970 addressbook_open(compose);
9973 static void about_show_cb(GtkAction *action, gpointer data)
9978 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9980 Compose *compose = (Compose *)data;
9985 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9986 cm_return_if_fail(tmpl != NULL);
9988 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9990 val = alertpanel(_("Apply template"), msg,
9991 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9994 if (val == G_ALERTDEFAULT)
9995 compose_template_apply(compose, tmpl, TRUE);
9996 else if (val == G_ALERTALTERNATE)
9997 compose_template_apply(compose, tmpl, FALSE);
10000 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
10002 Compose *compose = (Compose *)data;
10004 compose_exec_ext_editor(compose);
10007 static void compose_undo_cb(GtkAction *action, gpointer data)
10009 Compose *compose = (Compose *)data;
10010 gboolean prev_autowrap = compose->autowrap;
10012 compose->autowrap = FALSE;
10013 undo_undo(compose->undostruct);
10014 compose->autowrap = prev_autowrap;
10017 static void compose_redo_cb(GtkAction *action, gpointer data)
10019 Compose *compose = (Compose *)data;
10020 gboolean prev_autowrap = compose->autowrap;
10022 compose->autowrap = FALSE;
10023 undo_redo(compose->undostruct);
10024 compose->autowrap = prev_autowrap;
10027 static void entry_cut_clipboard(GtkWidget *entry)
10029 if (GTK_IS_EDITABLE(entry))
10030 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
10031 else if (GTK_IS_TEXT_VIEW(entry))
10032 gtk_text_buffer_cut_clipboard(
10033 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10034 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
10038 static void entry_copy_clipboard(GtkWidget *entry)
10040 if (GTK_IS_EDITABLE(entry))
10041 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
10042 else if (GTK_IS_TEXT_VIEW(entry))
10043 gtk_text_buffer_copy_clipboard(
10044 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10045 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
10048 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
10049 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
10051 if (GTK_IS_TEXT_VIEW(entry)) {
10052 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10053 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
10054 GtkTextIter start_iter, end_iter;
10056 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
10058 if (contents == NULL)
10061 /* we shouldn't delete the selection when middle-click-pasting, or we
10062 * can't mid-click-paste our own selection */
10063 if (clip != GDK_SELECTION_PRIMARY) {
10064 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
10065 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
10068 if (insert_place == NULL) {
10069 /* if insert_place isn't specified, insert at the cursor.
10070 * used for Ctrl-V pasting */
10071 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10072 start = gtk_text_iter_get_offset(&start_iter);
10073 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
10075 /* if insert_place is specified, paste here.
10076 * used for mid-click-pasting */
10077 start = gtk_text_iter_get_offset(insert_place);
10078 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
10079 if (prefs_common.primary_paste_unselects)
10080 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
10084 /* paste unwrapped: mark the paste so it's not wrapped later */
10085 end = start + strlen(contents);
10086 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
10087 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
10088 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
10089 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
10090 /* rewrap paragraph now (after a mid-click-paste) */
10091 mark_start = gtk_text_buffer_get_insert(buffer);
10092 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10093 gtk_text_iter_backward_char(&start_iter);
10094 compose_beautify_paragraph(compose, &start_iter, TRUE);
10096 } else if (GTK_IS_EDITABLE(entry))
10097 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
10099 compose->modified = TRUE;
10102 static void entry_allsel(GtkWidget *entry)
10104 if (GTK_IS_EDITABLE(entry))
10105 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
10106 else if (GTK_IS_TEXT_VIEW(entry)) {
10107 GtkTextIter startiter, enditer;
10108 GtkTextBuffer *textbuf;
10110 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10111 gtk_text_buffer_get_start_iter(textbuf, &startiter);
10112 gtk_text_buffer_get_end_iter(textbuf, &enditer);
10114 gtk_text_buffer_move_mark_by_name(textbuf,
10115 "selection_bound", &startiter);
10116 gtk_text_buffer_move_mark_by_name(textbuf,
10117 "insert", &enditer);
10121 static void compose_cut_cb(GtkAction *action, gpointer data)
10123 Compose *compose = (Compose *)data;
10124 if (compose->focused_editable
10125 #ifndef GENERIC_UMPC
10126 && gtk_widget_has_focus(compose->focused_editable)
10129 entry_cut_clipboard(compose->focused_editable);
10132 static void compose_copy_cb(GtkAction *action, gpointer data)
10134 Compose *compose = (Compose *)data;
10135 if (compose->focused_editable
10136 #ifndef GENERIC_UMPC
10137 && gtk_widget_has_focus(compose->focused_editable)
10140 entry_copy_clipboard(compose->focused_editable);
10143 static void compose_paste_cb(GtkAction *action, gpointer data)
10145 Compose *compose = (Compose *)data;
10146 gint prev_autowrap;
10147 GtkTextBuffer *buffer;
10149 if (compose->focused_editable &&
10150 gtk_widget_has_focus(compose->focused_editable))
10151 entry_paste_clipboard(compose, compose->focused_editable,
10152 prefs_common.linewrap_pastes,
10153 GDK_SELECTION_CLIPBOARD, NULL);
10157 if (gtk_widget_has_focus(compose->text) &&
10158 compose->gtkaspell &&
10159 compose->gtkaspell->check_while_typing)
10160 gtkaspell_highlight_all(compose->gtkaspell);
10164 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10166 Compose *compose = (Compose *)data;
10167 gint wrap_quote = prefs_common.linewrap_quote;
10168 if (compose->focused_editable
10169 #ifndef GENERIC_UMPC
10170 && gtk_widget_has_focus(compose->focused_editable)
10173 /* let text_insert() (called directly or at a later time
10174 * after the gtk_editable_paste_clipboard) know that
10175 * text is to be inserted as a quotation. implemented
10176 * by using a simple refcount... */
10177 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10178 G_OBJECT(compose->focused_editable),
10179 "paste_as_quotation"));
10180 g_object_set_data(G_OBJECT(compose->focused_editable),
10181 "paste_as_quotation",
10182 GINT_TO_POINTER(paste_as_quotation + 1));
10183 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10184 entry_paste_clipboard(compose, compose->focused_editable,
10185 prefs_common.linewrap_pastes,
10186 GDK_SELECTION_CLIPBOARD, NULL);
10187 prefs_common.linewrap_quote = wrap_quote;
10191 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10193 Compose *compose = (Compose *)data;
10194 gint prev_autowrap;
10195 GtkTextBuffer *buffer;
10197 if (compose->focused_editable
10198 #ifndef GENERIC_UMPC
10199 && gtk_widget_has_focus(compose->focused_editable)
10202 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10203 GDK_SELECTION_CLIPBOARD, NULL);
10207 if (gtk_widget_has_focus(compose->text) &&
10208 compose->gtkaspell &&
10209 compose->gtkaspell->check_while_typing)
10210 gtkaspell_highlight_all(compose->gtkaspell);
10214 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10216 Compose *compose = (Compose *)data;
10217 gint prev_autowrap;
10218 GtkTextBuffer *buffer;
10220 if (compose->focused_editable
10221 #ifndef GENERIC_UMPC
10222 && gtk_widget_has_focus(compose->focused_editable)
10225 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10226 GDK_SELECTION_CLIPBOARD, NULL);
10230 if (gtk_widget_has_focus(compose->text) &&
10231 compose->gtkaspell &&
10232 compose->gtkaspell->check_while_typing)
10233 gtkaspell_highlight_all(compose->gtkaspell);
10237 static void compose_allsel_cb(GtkAction *action, gpointer data)
10239 Compose *compose = (Compose *)data;
10240 if (compose->focused_editable
10241 #ifndef GENERIC_UMPC
10242 && gtk_widget_has_focus(compose->focused_editable)
10245 entry_allsel(compose->focused_editable);
10248 static void textview_move_beginning_of_line (GtkTextView *text)
10250 GtkTextBuffer *buffer;
10254 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10256 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10257 mark = gtk_text_buffer_get_insert(buffer);
10258 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10259 gtk_text_iter_set_line_offset(&ins, 0);
10260 gtk_text_buffer_place_cursor(buffer, &ins);
10263 static void textview_move_forward_character (GtkTextView *text)
10265 GtkTextBuffer *buffer;
10269 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10271 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10272 mark = gtk_text_buffer_get_insert(buffer);
10273 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10274 if (gtk_text_iter_forward_cursor_position(&ins))
10275 gtk_text_buffer_place_cursor(buffer, &ins);
10278 static void textview_move_backward_character (GtkTextView *text)
10280 GtkTextBuffer *buffer;
10284 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10286 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10287 mark = gtk_text_buffer_get_insert(buffer);
10288 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10289 if (gtk_text_iter_backward_cursor_position(&ins))
10290 gtk_text_buffer_place_cursor(buffer, &ins);
10293 static void textview_move_forward_word (GtkTextView *text)
10295 GtkTextBuffer *buffer;
10300 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10302 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10303 mark = gtk_text_buffer_get_insert(buffer);
10304 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10305 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10306 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10307 gtk_text_iter_backward_word_start(&ins);
10308 gtk_text_buffer_place_cursor(buffer, &ins);
10312 static void textview_move_backward_word (GtkTextView *text)
10314 GtkTextBuffer *buffer;
10319 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10321 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10322 mark = gtk_text_buffer_get_insert(buffer);
10323 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10324 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10325 if (gtk_text_iter_backward_word_starts(&ins, 1))
10326 gtk_text_buffer_place_cursor(buffer, &ins);
10329 static void textview_move_end_of_line (GtkTextView *text)
10331 GtkTextBuffer *buffer;
10335 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10337 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10338 mark = gtk_text_buffer_get_insert(buffer);
10339 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10340 if (gtk_text_iter_forward_to_line_end(&ins))
10341 gtk_text_buffer_place_cursor(buffer, &ins);
10344 static void textview_move_next_line (GtkTextView *text)
10346 GtkTextBuffer *buffer;
10351 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10353 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10354 mark = gtk_text_buffer_get_insert(buffer);
10355 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10356 offset = gtk_text_iter_get_line_offset(&ins);
10357 if (gtk_text_iter_forward_line(&ins)) {
10358 gtk_text_iter_set_line_offset(&ins, offset);
10359 gtk_text_buffer_place_cursor(buffer, &ins);
10363 static void textview_move_previous_line (GtkTextView *text)
10365 GtkTextBuffer *buffer;
10370 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10372 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10373 mark = gtk_text_buffer_get_insert(buffer);
10374 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10375 offset = gtk_text_iter_get_line_offset(&ins);
10376 if (gtk_text_iter_backward_line(&ins)) {
10377 gtk_text_iter_set_line_offset(&ins, offset);
10378 gtk_text_buffer_place_cursor(buffer, &ins);
10382 static void textview_delete_forward_character (GtkTextView *text)
10384 GtkTextBuffer *buffer;
10386 GtkTextIter ins, end_iter;
10388 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10390 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10391 mark = gtk_text_buffer_get_insert(buffer);
10392 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10394 if (gtk_text_iter_forward_char(&end_iter)) {
10395 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10399 static void textview_delete_backward_character (GtkTextView *text)
10401 GtkTextBuffer *buffer;
10403 GtkTextIter ins, end_iter;
10405 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10407 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10408 mark = gtk_text_buffer_get_insert(buffer);
10409 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10411 if (gtk_text_iter_backward_char(&end_iter)) {
10412 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10416 static void textview_delete_forward_word (GtkTextView *text)
10418 GtkTextBuffer *buffer;
10420 GtkTextIter ins, end_iter;
10422 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10424 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10425 mark = gtk_text_buffer_get_insert(buffer);
10426 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10428 if (gtk_text_iter_forward_word_end(&end_iter)) {
10429 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10433 static void textview_delete_backward_word (GtkTextView *text)
10435 GtkTextBuffer *buffer;
10437 GtkTextIter ins, end_iter;
10439 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10441 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10442 mark = gtk_text_buffer_get_insert(buffer);
10443 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10445 if (gtk_text_iter_backward_word_start(&end_iter)) {
10446 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10450 static void textview_delete_line (GtkTextView *text)
10452 GtkTextBuffer *buffer;
10454 GtkTextIter ins, start_iter, end_iter;
10456 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10458 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10459 mark = gtk_text_buffer_get_insert(buffer);
10460 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10463 gtk_text_iter_set_line_offset(&start_iter, 0);
10466 if (gtk_text_iter_ends_line(&end_iter)){
10467 if (!gtk_text_iter_forward_char(&end_iter))
10468 gtk_text_iter_backward_char(&start_iter);
10471 gtk_text_iter_forward_to_line_end(&end_iter);
10472 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10475 static void textview_delete_to_line_end (GtkTextView *text)
10477 GtkTextBuffer *buffer;
10479 GtkTextIter ins, end_iter;
10481 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10483 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10484 mark = gtk_text_buffer_get_insert(buffer);
10485 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10487 if (gtk_text_iter_ends_line(&end_iter))
10488 gtk_text_iter_forward_char(&end_iter);
10490 gtk_text_iter_forward_to_line_end(&end_iter);
10491 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10494 #define DO_ACTION(name, act) { \
10495 if(!strcmp(name, a_name)) { \
10499 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10501 const gchar *a_name = gtk_action_get_name(action);
10502 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10503 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10504 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10505 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10506 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10507 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10508 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10509 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10510 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10511 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10512 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10513 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10514 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10515 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10519 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10521 Compose *compose = (Compose *)data;
10522 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10523 ComposeCallAdvancedAction action = -1;
10525 action = compose_call_advanced_action_from_path(gaction);
10528 void (*do_action) (GtkTextView *text);
10529 } action_table[] = {
10530 {textview_move_beginning_of_line},
10531 {textview_move_forward_character},
10532 {textview_move_backward_character},
10533 {textview_move_forward_word},
10534 {textview_move_backward_word},
10535 {textview_move_end_of_line},
10536 {textview_move_next_line},
10537 {textview_move_previous_line},
10538 {textview_delete_forward_character},
10539 {textview_delete_backward_character},
10540 {textview_delete_forward_word},
10541 {textview_delete_backward_word},
10542 {textview_delete_line},
10543 {textview_delete_to_line_end}
10546 if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
10548 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10549 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10550 if (action_table[action].do_action)
10551 action_table[action].do_action(text);
10553 g_warning("Not implemented yet.");
10557 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10559 GtkAllocation allocation;
10563 if (GTK_IS_EDITABLE(widget)) {
10564 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10565 gtk_editable_set_position(GTK_EDITABLE(widget),
10568 if ((parent = gtk_widget_get_parent(widget))
10569 && (parent = gtk_widget_get_parent(parent))
10570 && (parent = gtk_widget_get_parent(parent))) {
10571 if (GTK_IS_SCROLLED_WINDOW(parent)) {
10572 gtk_widget_get_allocation(widget, &allocation);
10573 gint y = allocation.y;
10574 gint height = allocation.height;
10575 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10576 (GTK_SCROLLED_WINDOW(parent));
10578 gfloat value = gtk_adjustment_get_value(shown);
10579 gfloat upper = gtk_adjustment_get_upper(shown);
10580 gfloat page_size = gtk_adjustment_get_page_size(shown);
10581 if (y < (int)value) {
10582 gtk_adjustment_set_value(shown, y - 1);
10584 if ((y + height) > ((int)value + (int)page_size)) {
10585 if ((y - height - 1) < ((int)upper - (int)page_size)) {
10586 gtk_adjustment_set_value(shown,
10587 y + height - (int)page_size - 1);
10589 gtk_adjustment_set_value(shown,
10590 (int)upper - (int)page_size - 1);
10597 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10598 compose->focused_editable = widget;
10600 #ifdef GENERIC_UMPC
10601 if (GTK_IS_TEXT_VIEW(widget)
10602 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10603 g_object_ref(compose->notebook);
10604 g_object_ref(compose->edit_vbox);
10605 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10606 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10607 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10608 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10609 g_object_unref(compose->notebook);
10610 g_object_unref(compose->edit_vbox);
10611 g_signal_handlers_block_by_func(G_OBJECT(widget),
10612 G_CALLBACK(compose_grab_focus_cb),
10614 gtk_widget_grab_focus(widget);
10615 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10616 G_CALLBACK(compose_grab_focus_cb),
10618 } else if (!GTK_IS_TEXT_VIEW(widget)
10619 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10620 g_object_ref(compose->notebook);
10621 g_object_ref(compose->edit_vbox);
10622 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10623 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10624 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10625 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10626 g_object_unref(compose->notebook);
10627 g_object_unref(compose->edit_vbox);
10628 g_signal_handlers_block_by_func(G_OBJECT(widget),
10629 G_CALLBACK(compose_grab_focus_cb),
10631 gtk_widget_grab_focus(widget);
10632 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10633 G_CALLBACK(compose_grab_focus_cb),
10639 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10641 compose->modified = TRUE;
10642 // compose_beautify_paragraph(compose, NULL, TRUE);
10643 #ifndef GENERIC_UMPC
10644 compose_set_title(compose);
10648 static void compose_wrap_cb(GtkAction *action, gpointer data)
10650 Compose *compose = (Compose *)data;
10651 compose_beautify_paragraph(compose, NULL, TRUE);
10654 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10656 Compose *compose = (Compose *)data;
10657 compose_wrap_all_full(compose, TRUE);
10660 static void compose_find_cb(GtkAction *action, gpointer data)
10662 Compose *compose = (Compose *)data;
10664 message_search_compose(compose);
10667 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10670 Compose *compose = (Compose *)data;
10671 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10672 if (compose->autowrap)
10673 compose_wrap_all_full(compose, TRUE);
10674 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10677 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10680 Compose *compose = (Compose *)data;
10681 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10684 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10686 Compose *compose = (Compose *)data;
10688 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10691 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10693 Compose *compose = (Compose *)data;
10695 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10698 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10700 g_free(compose->privacy_system);
10702 compose->privacy_system = g_strdup(account->default_privacy_system);
10703 compose_update_privacy_system_menu_item(compose, warn);
10706 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10708 Compose *compose = (Compose *)data;
10710 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10711 gtk_widget_show(compose->ruler_hbox);
10712 prefs_common.show_ruler = TRUE;
10714 gtk_widget_hide(compose->ruler_hbox);
10715 gtk_widget_queue_resize(compose->edit_vbox);
10716 prefs_common.show_ruler = FALSE;
10720 static void compose_attach_drag_received_cb (GtkWidget *widget,
10721 GdkDragContext *context,
10724 GtkSelectionData *data,
10727 gpointer user_data)
10729 Compose *compose = (Compose *)user_data;
10733 type = gtk_selection_data_get_data_type(data);
10734 if (((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
10736 || (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND"))
10738 ) && gtk_drag_get_source_widget(context) !=
10739 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10740 list = uri_list_extract_filenames(
10741 (const gchar *)gtk_selection_data_get_data(data));
10742 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10743 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10744 compose_attach_append
10745 (compose, (const gchar *)tmp->data,
10746 utf8_filename, NULL, NULL);
10747 g_free(utf8_filename);
10749 if (list) compose_changed_cb(NULL, compose);
10750 list_free_strings(list);
10752 } else if (gtk_drag_get_source_widget(context)
10753 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10754 /* comes from our summaryview */
10755 SummaryView * summaryview = NULL;
10756 GSList * list = NULL, *cur = NULL;
10758 if (mainwindow_get_mainwindow())
10759 summaryview = mainwindow_get_mainwindow()->summaryview;
10762 list = summary_get_selected_msg_list(summaryview);
10764 for (cur = list; cur; cur = cur->next) {
10765 MsgInfo *msginfo = (MsgInfo *)cur->data;
10766 gchar *file = NULL;
10768 file = procmsg_get_message_file_full(msginfo,
10771 compose_attach_append(compose, (const gchar *)file,
10772 (const gchar *)file, "message/rfc822", NULL);
10776 g_slist_free(list);
10780 static gboolean compose_drag_drop(GtkWidget *widget,
10781 GdkDragContext *drag_context,
10783 guint time, gpointer user_data)
10785 /* not handling this signal makes compose_insert_drag_received_cb
10790 static void compose_insert_drag_received_cb (GtkWidget *widget,
10791 GdkDragContext *drag_context,
10794 GtkSelectionData *data,
10797 gpointer user_data)
10799 Compose *compose = (Compose *)user_data;
10803 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10805 type = gtk_selection_data_get_data_type(data);
10807 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
10809 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND")) {
10811 AlertValue val = G_ALERTDEFAULT;
10812 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
10814 list = uri_list_extract_filenames(ddata);
10815 if (list == NULL && strstr(ddata, "://")) {
10816 /* Assume a list of no files, and data has ://, is a remote link */
10817 gchar *tmpdata = g_strstrip(g_strdup(ddata));
10818 gchar *tmpfile = get_tmp_file();
10819 str_write_to_file(tmpdata, tmpfile);
10821 compose_insert_file(compose, tmpfile);
10822 claws_unlink(tmpfile);
10824 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10825 compose_beautify_paragraph(compose, NULL, TRUE);
10828 switch (prefs_common.compose_dnd_mode) {
10829 case COMPOSE_DND_ASK:
10830 val = alertpanel_full(_("Insert or attach?"),
10831 _("Do you want to insert the contents of the file(s) "
10832 "into the message body, or attach it to the email?"),
10833 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10834 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10836 case COMPOSE_DND_INSERT:
10837 val = G_ALERTALTERNATE;
10839 case COMPOSE_DND_ATTACH:
10840 val = G_ALERTOTHER;
10843 /* unexpected case */
10844 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10847 if (val & G_ALERTDISABLE) {
10848 val &= ~G_ALERTDISABLE;
10849 /* remember what action to perform by default, only if we don't click Cancel */
10850 if (val == G_ALERTALTERNATE)
10851 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10852 else if (val == G_ALERTOTHER)
10853 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10856 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10857 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10858 list_free_strings(list);
10861 } else if (val == G_ALERTOTHER) {
10862 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10863 list_free_strings(list);
10868 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10869 compose_insert_file(compose, (const gchar *)tmp->data);
10871 list_free_strings(list);
10873 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10878 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10881 static void compose_header_drag_received_cb (GtkWidget *widget,
10882 GdkDragContext *drag_context,
10885 GtkSelectionData *data,
10888 gpointer user_data)
10890 GtkEditable *entry = (GtkEditable *)user_data;
10891 const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
10893 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10896 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10897 gchar *decoded=g_new(gchar, strlen(email));
10900 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
10901 gtk_editable_delete_text(entry, 0, -1);
10902 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10903 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10907 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10910 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10912 Compose *compose = (Compose *)data;
10914 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10915 compose->return_receipt = TRUE;
10917 compose->return_receipt = FALSE;
10920 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10922 Compose *compose = (Compose *)data;
10924 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10925 compose->remove_references = TRUE;
10927 compose->remove_references = FALSE;
10930 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10931 ComposeHeaderEntry *headerentry)
10933 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10937 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10938 GdkEventKey *event,
10939 ComposeHeaderEntry *headerentry)
10941 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10942 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10943 !(event->state & GDK_MODIFIER_MASK) &&
10944 (event->keyval == GDK_KEY_BackSpace) &&
10945 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10946 gtk_container_remove
10947 (GTK_CONTAINER(headerentry->compose->header_table),
10948 headerentry->combo);
10949 gtk_container_remove
10950 (GTK_CONTAINER(headerentry->compose->header_table),
10951 headerentry->entry);
10952 headerentry->compose->header_list =
10953 g_slist_remove(headerentry->compose->header_list,
10955 g_free(headerentry);
10956 } else if (event->keyval == GDK_KEY_Tab) {
10957 if (headerentry->compose->header_last == headerentry) {
10958 /* Override default next focus, and give it to subject_entry
10959 * instead of notebook tabs
10961 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10962 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10969 static gboolean scroll_postpone(gpointer data)
10971 Compose *compose = (Compose *)data;
10973 cm_return_val_if_fail(!compose->batch, FALSE);
10975 GTK_EVENTS_FLUSH();
10976 compose_show_first_last_header(compose, FALSE);
10980 static void compose_headerentry_changed_cb(GtkWidget *entry,
10981 ComposeHeaderEntry *headerentry)
10983 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10984 compose_create_header_entry(headerentry->compose);
10985 g_signal_handlers_disconnect_matched
10986 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10987 0, 0, NULL, NULL, headerentry);
10989 if (!headerentry->compose->batch)
10990 g_timeout_add(0, scroll_postpone, headerentry->compose);
10994 static gboolean compose_defer_auto_save_draft(Compose *compose)
10996 compose->draft_timeout_tag = -1;
10997 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
11001 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
11003 GtkAdjustment *vadj;
11005 cm_return_if_fail(compose);
11006 cm_return_if_fail(!compose->batch);
11007 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
11008 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
11009 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11010 gtk_widget_get_parent(compose->header_table)));
11011 gtk_adjustment_set_value(vadj, (show_first ?
11012 gtk_adjustment_get_lower(vadj) :
11013 (gtk_adjustment_get_upper(vadj) -
11014 gtk_adjustment_get_page_size(vadj))));
11015 gtk_adjustment_changed(vadj);
11018 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
11019 const gchar *text, gint len, Compose *compose)
11021 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
11022 (G_OBJECT(compose->text), "paste_as_quotation"));
11025 cm_return_if_fail(text != NULL);
11027 g_signal_handlers_block_by_func(G_OBJECT(buffer),
11028 G_CALLBACK(text_inserted),
11030 if (paste_as_quotation) {
11032 const gchar *qmark;
11034 GtkTextIter start_iter;
11037 len = strlen(text);
11039 new_text = g_strndup(text, len);
11041 qmark = compose_quote_char_from_context(compose);
11043 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11044 gtk_text_buffer_place_cursor(buffer, iter);
11046 pos = gtk_text_iter_get_offset(iter);
11048 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
11049 _("Quote format error at line %d."));
11050 quote_fmt_reset_vartable();
11052 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
11053 GINT_TO_POINTER(paste_as_quotation - 1));
11055 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11056 gtk_text_buffer_place_cursor(buffer, iter);
11057 gtk_text_buffer_delete_mark(buffer, mark);
11059 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
11060 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
11061 compose_beautify_paragraph(compose, &start_iter, FALSE);
11062 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
11063 gtk_text_buffer_delete_mark(buffer, mark);
11065 if (strcmp(text, "\n") || compose->automatic_break
11066 || gtk_text_iter_starts_line(iter)) {
11067 GtkTextIter before_ins;
11068 gtk_text_buffer_insert(buffer, iter, text, len);
11069 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
11070 before_ins = *iter;
11071 gtk_text_iter_backward_chars(&before_ins, len);
11072 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
11075 /* check if the preceding is just whitespace or quote */
11076 GtkTextIter start_line;
11077 gchar *tmp = NULL, *quote = NULL;
11078 gint quote_len = 0, is_normal = 0;
11079 start_line = *iter;
11080 gtk_text_iter_set_line_offset(&start_line, 0);
11081 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
11084 if (*tmp == '\0') {
11087 quote = compose_get_quote_str(buffer, &start_line, "e_len);
11095 gtk_text_buffer_insert(buffer, iter, text, len);
11097 gtk_text_buffer_insert_with_tags_by_name(buffer,
11098 iter, text, len, "no_join", NULL);
11103 if (!paste_as_quotation) {
11104 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11105 compose_beautify_paragraph(compose, iter, FALSE);
11106 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11107 gtk_text_buffer_delete_mark(buffer, mark);
11110 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
11111 G_CALLBACK(text_inserted),
11113 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
11115 if (prefs_common.autosave &&
11116 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
11117 compose->draft_timeout_tag != -2 /* disabled while loading */)
11118 compose->draft_timeout_tag = g_timeout_add
11119 (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
11123 static void compose_check_all(GtkAction *action, gpointer data)
11125 Compose *compose = (Compose *)data;
11126 if (!compose->gtkaspell)
11129 if (gtk_widget_has_focus(compose->subject_entry))
11130 claws_spell_entry_check_all(
11131 CLAWS_SPELL_ENTRY(compose->subject_entry));
11133 gtkaspell_check_all(compose->gtkaspell);
11136 static void compose_highlight_all(GtkAction *action, gpointer data)
11138 Compose *compose = (Compose *)data;
11139 if (compose->gtkaspell) {
11140 claws_spell_entry_recheck_all(
11141 CLAWS_SPELL_ENTRY(compose->subject_entry));
11142 gtkaspell_highlight_all(compose->gtkaspell);
11146 static void compose_check_backwards(GtkAction *action, gpointer data)
11148 Compose *compose = (Compose *)data;
11149 if (!compose->gtkaspell) {
11150 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11154 if (gtk_widget_has_focus(compose->subject_entry))
11155 claws_spell_entry_check_backwards(
11156 CLAWS_SPELL_ENTRY(compose->subject_entry));
11158 gtkaspell_check_backwards(compose->gtkaspell);
11161 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11163 Compose *compose = (Compose *)data;
11164 if (!compose->gtkaspell) {
11165 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11169 if (gtk_widget_has_focus(compose->subject_entry))
11170 claws_spell_entry_check_forwards_go(
11171 CLAWS_SPELL_ENTRY(compose->subject_entry));
11173 gtkaspell_check_forwards_go(compose->gtkaspell);
11178 *\brief Guess originating forward account from MsgInfo and several
11179 * "common preference" settings. Return NULL if no guess.
11181 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11183 PrefsAccount *account = NULL;
11185 cm_return_val_if_fail(msginfo, NULL);
11186 cm_return_val_if_fail(msginfo->folder, NULL);
11187 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11189 if (msginfo->folder->prefs->enable_default_account)
11190 account = account_find_from_id(msginfo->folder->prefs->default_account);
11193 account = msginfo->folder->folder->account;
11195 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11197 Xstrdup_a(to, msginfo->to, return NULL);
11198 extract_address(to);
11199 account = account_find_from_address(to, FALSE);
11202 if (!account && prefs_common.forward_account_autosel) {
11203 gchar cc[BUFFSIZE];
11204 if (!procheader_get_header_from_msginfo
11205 (msginfo, cc,sizeof cc , "Cc:")) {
11206 gchar *buf = cc + strlen("Cc:");
11207 extract_address(buf);
11208 account = account_find_from_address(buf, FALSE);
11212 if (!account && prefs_common.forward_account_autosel) {
11213 gchar deliveredto[BUFFSIZE];
11214 if (!procheader_get_header_from_msginfo
11215 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
11216 gchar *buf = deliveredto + strlen("Delivered-To:");
11217 extract_address(buf);
11218 account = account_find_from_address(buf, FALSE);
11225 gboolean compose_close(Compose *compose)
11229 if (!g_mutex_trylock(compose->mutex)) {
11230 /* we have to wait for the (possibly deferred by auto-save)
11231 * drafting to be done, before destroying the compose under
11233 debug_print("waiting for drafting to finish...\n");
11234 compose_allow_user_actions(compose, FALSE);
11235 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11238 cm_return_val_if_fail(compose, FALSE);
11239 gtkut_widget_get_uposition(compose->window, &x, &y);
11240 if (!compose->batch) {
11241 prefs_common.compose_x = x;
11242 prefs_common.compose_y = y;
11244 g_mutex_unlock(compose->mutex);
11245 compose_destroy(compose);
11250 * Add entry field for each address in list.
11251 * \param compose E-Mail composition object.
11252 * \param listAddress List of (formatted) E-Mail addresses.
11254 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11257 node = listAddress;
11259 addr = ( gchar * ) node->data;
11260 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11261 node = g_list_next( node );
11265 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11266 guint action, gboolean opening_multiple)
11268 gchar *body = NULL;
11269 GSList *new_msglist = NULL;
11270 MsgInfo *tmp_msginfo = NULL;
11271 gboolean originally_enc = FALSE;
11272 gboolean originally_sig = FALSE;
11273 Compose *compose = NULL;
11274 gchar *s_system = NULL;
11276 cm_return_if_fail(msgview != NULL);
11278 cm_return_if_fail(msginfo_list != NULL);
11280 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11281 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11282 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11284 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11285 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11286 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11287 orig_msginfo, mimeinfo);
11288 if (tmp_msginfo != NULL) {
11289 new_msglist = g_slist_append(NULL, tmp_msginfo);
11291 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11292 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11293 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11295 tmp_msginfo->folder = orig_msginfo->folder;
11296 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11297 if (orig_msginfo->tags) {
11298 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11299 tmp_msginfo->folder->tags_dirty = TRUE;
11305 if (!opening_multiple)
11306 body = messageview_get_selection(msgview);
11309 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11310 procmsg_msginfo_free(tmp_msginfo);
11311 g_slist_free(new_msglist);
11313 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11315 if (compose && originally_enc) {
11316 compose_force_encryption(compose, compose->account, FALSE, s_system);
11319 if (compose && originally_sig && compose->account->default_sign_reply) {
11320 compose_force_signing(compose, compose->account, s_system);
11324 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11327 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11330 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11331 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11332 GSList *cur = msginfo_list;
11333 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11334 "messages. Opening the windows "
11335 "could take some time. Do you "
11336 "want to continue?"),
11337 g_slist_length(msginfo_list));
11338 if (g_slist_length(msginfo_list) > 9
11339 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11340 != G_ALERTALTERNATE) {
11345 /* We'll open multiple compose windows */
11346 /* let the WM place the next windows */
11347 compose_force_window_origin = FALSE;
11348 for (; cur; cur = cur->next) {
11350 tmplist.data = cur->data;
11351 tmplist.next = NULL;
11352 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11354 compose_force_window_origin = TRUE;
11356 /* forwarding multiple mails as attachments is done via a
11357 * single compose window */
11358 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11362 void compose_check_for_email_account(Compose *compose)
11364 PrefsAccount *ac = NULL, *curr = NULL;
11370 if (compose->account && compose->account->protocol == A_NNTP) {
11371 ac = account_get_cur_account();
11372 if (ac->protocol == A_NNTP) {
11373 list = account_get_list();
11375 for( ; list != NULL ; list = g_list_next(list)) {
11376 curr = (PrefsAccount *) list->data;
11377 if (curr->protocol != A_NNTP) {
11383 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11388 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo,
11389 const gchar *address)
11391 GSList *msginfo_list = NULL;
11392 gchar *body = messageview_get_selection(msgview);
11395 msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11397 compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11398 compose_check_for_email_account(compose);
11399 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11400 compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11401 compose_reply_set_subject(compose, msginfo);
11404 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11407 void compose_set_position(Compose *compose, gint pos)
11409 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11411 gtkut_text_view_set_position(text, pos);
11414 gboolean compose_search_string(Compose *compose,
11415 const gchar *str, gboolean case_sens)
11417 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11419 return gtkut_text_view_search_string(text, str, case_sens);
11422 gboolean compose_search_string_backward(Compose *compose,
11423 const gchar *str, gboolean case_sens)
11425 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11427 return gtkut_text_view_search_string_backward(text, str, case_sens);
11430 /* allocate a msginfo structure and populate its data from a compose data structure */
11431 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11433 MsgInfo *newmsginfo;
11435 gchar buf[BUFFSIZE];
11437 cm_return_val_if_fail( compose != NULL, NULL );
11439 newmsginfo = procmsg_msginfo_new();
11442 get_rfc822_date(buf, sizeof(buf));
11443 newmsginfo->date = g_strdup(buf);
11446 if (compose->from_name) {
11447 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11448 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11452 if (compose->subject_entry)
11453 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11455 /* to, cc, reply-to, newsgroups */
11456 for (list = compose->header_list; list; list = list->next) {
11457 gchar *header = gtk_editable_get_chars(
11459 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11460 gchar *entry = gtk_editable_get_chars(
11461 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11463 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11464 if ( newmsginfo->to == NULL ) {
11465 newmsginfo->to = g_strdup(entry);
11466 } else if (entry && *entry) {
11467 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11468 g_free(newmsginfo->to);
11469 newmsginfo->to = tmp;
11472 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11473 if ( newmsginfo->cc == NULL ) {
11474 newmsginfo->cc = g_strdup(entry);
11475 } else if (entry && *entry) {
11476 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11477 g_free(newmsginfo->cc);
11478 newmsginfo->cc = tmp;
11481 if ( strcasecmp(header,
11482 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11483 if ( newmsginfo->newsgroups == NULL ) {
11484 newmsginfo->newsgroups = g_strdup(entry);
11485 } else if (entry && *entry) {
11486 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11487 g_free(newmsginfo->newsgroups);
11488 newmsginfo->newsgroups = tmp;
11496 /* other data is unset */
11502 /* update compose's dictionaries from folder dict settings */
11503 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11504 FolderItem *folder_item)
11506 cm_return_if_fail(compose != NULL);
11508 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11509 FolderItemPrefs *prefs = folder_item->prefs;
11511 if (prefs->enable_default_dictionary)
11512 gtkaspell_change_dict(compose->gtkaspell,
11513 prefs->default_dictionary, FALSE);
11514 if (folder_item->prefs->enable_default_alt_dictionary)
11515 gtkaspell_change_alt_dict(compose->gtkaspell,
11516 prefs->default_alt_dictionary);
11517 if (prefs->enable_default_dictionary
11518 || prefs->enable_default_alt_dictionary)
11519 compose_spell_menu_changed(compose);