2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 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"
87 #include "alertpanel.h"
88 #include "manage_window.h"
89 #include "gtkshruler.h"
91 #include "addr_compl.h"
92 #include "quote_fmt.h"
94 #include "foldersel.h"
97 #include "message_search.h"
102 #include "autofaces.h"
114 #define N_ATTACH_COLS (N_COL_COLUMNS)
118 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
119 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
127 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
132 } ComposeCallAdvancedAction;
136 PRIORITY_HIGHEST = 1,
145 COMPOSE_INSERT_SUCCESS,
146 COMPOSE_INSERT_READ_ERROR,
147 COMPOSE_INSERT_INVALID_CHARACTER,
148 COMPOSE_INSERT_NO_FILE
149 } ComposeInsertResult;
153 COMPOSE_WRITE_FOR_SEND,
154 COMPOSE_WRITE_FOR_STORE
159 COMPOSE_QUOTE_FORCED,
164 #define B64_LINE_SIZE 57
165 #define B64_BUFFSIZE 77
167 #define MAX_REFERENCES_LEN 999
169 static GList *compose_list = NULL;
171 static Compose *compose_generic_new (PrefsAccount *account,
174 GPtrArray *attach_files,
175 GList *listAddress );
177 static Compose *compose_create (PrefsAccount *account,
182 static void compose_entry_mark_default_to (Compose *compose,
183 const gchar *address);
184 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
185 ComposeQuoteMode quote_mode,
189 static Compose *compose_forward_multiple (PrefsAccount *account,
190 GSList *msginfo_list);
191 static Compose *compose_reply (MsgInfo *msginfo,
192 ComposeQuoteMode quote_mode,
197 static Compose *compose_reply_mode (ComposeMode mode,
198 GSList *msginfo_list,
200 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
201 static void compose_update_privacy_systems_menu(Compose *compose);
203 static GtkWidget *compose_account_option_menu_create
205 static void compose_set_out_encoding (Compose *compose);
206 static void compose_set_template_menu (Compose *compose);
207 static void compose_template_apply (Compose *compose,
210 static void compose_destroy (Compose *compose);
212 static void compose_entries_set (Compose *compose,
214 ComposeEntryType to_type);
215 static gint compose_parse_header (Compose *compose,
217 static gchar *compose_parse_references (const gchar *ref,
220 static gchar *compose_quote_fmt (Compose *compose,
226 gboolean need_unescape,
227 const gchar *err_msg);
229 static void compose_reply_set_entry (Compose *compose,
235 followup_and_reply_to);
236 static void compose_reedit_set_entry (Compose *compose,
239 static void compose_insert_sig (Compose *compose,
241 static gchar *compose_get_signature_str (Compose *compose);
242 static ComposeInsertResult compose_insert_file (Compose *compose,
245 static gboolean compose_attach_append (Compose *compose,
248 const gchar *content_type);
249 static void compose_attach_parts (Compose *compose,
252 static gboolean compose_beautify_paragraph (Compose *compose,
253 GtkTextIter *par_iter,
255 static void compose_wrap_all (Compose *compose);
256 static void compose_wrap_all_full (Compose *compose,
259 static void compose_set_title (Compose *compose);
260 static void compose_select_account (Compose *compose,
261 PrefsAccount *account,
264 static PrefsAccount *compose_current_mail_account(void);
265 /* static gint compose_send (Compose *compose); */
266 static gboolean compose_check_for_valid_recipient
268 static gboolean compose_check_entries (Compose *compose,
269 gboolean check_everything);
270 static gint compose_write_to_file (Compose *compose,
273 gboolean attach_parts);
274 static gint compose_write_body_to_file (Compose *compose,
276 static gint compose_remove_reedit_target (Compose *compose,
278 static void compose_remove_draft (Compose *compose);
279 static gint compose_queue_sub (Compose *compose,
283 gboolean check_subject,
284 gboolean remove_reedit_target);
285 static void compose_add_attachments (Compose *compose,
287 static gchar *compose_get_header (Compose *compose);
289 static void compose_convert_header (Compose *compose,
294 gboolean addr_field);
296 static void compose_attach_info_free (AttachInfo *ainfo);
297 static void compose_attach_remove_selected (GtkAction *action,
300 static void compose_attach_property (GtkAction *action,
302 static void compose_attach_property_create (gboolean *cancelled);
303 static void attach_property_ok (GtkWidget *widget,
304 gboolean *cancelled);
305 static void attach_property_cancel (GtkWidget *widget,
306 gboolean *cancelled);
307 static gint attach_property_delete_event (GtkWidget *widget,
309 gboolean *cancelled);
310 static gboolean attach_property_key_pressed (GtkWidget *widget,
312 gboolean *cancelled);
314 static void compose_exec_ext_editor (Compose *compose);
316 static gint compose_exec_ext_editor_real (const gchar *file);
317 static gboolean compose_ext_editor_kill (Compose *compose);
318 static gboolean compose_input_cb (GIOChannel *source,
319 GIOCondition condition,
321 static void compose_set_ext_editor_sensitive (Compose *compose,
323 #endif /* G_OS_UNIX */
325 static void compose_undo_state_changed (UndoMain *undostruct,
330 static void compose_create_header_entry (Compose *compose);
331 static void compose_add_header_entry (Compose *compose, const gchar *header, gchar *text);
332 static void compose_remove_header_entries(Compose *compose);
334 static void compose_update_priority_menu_item(Compose * compose);
336 static void compose_spell_menu_changed (void *data);
338 static void compose_add_field_list ( Compose *compose,
339 GList *listAddress );
341 /* callback functions */
343 static gboolean compose_edit_size_alloc (GtkEditable *widget,
344 GtkAllocation *allocation,
345 GtkSHRuler *shruler);
346 static void account_activated (GtkComboBox *optmenu,
348 static void attach_selected (GtkTreeView *tree_view,
349 GtkTreePath *tree_path,
350 GtkTreeViewColumn *column,
352 static gboolean attach_button_pressed (GtkWidget *widget,
353 GdkEventButton *event,
355 static gboolean attach_key_pressed (GtkWidget *widget,
358 static void compose_send_cb (GtkAction *action, gpointer data);
359 static void compose_send_later_cb (GtkAction *action, gpointer data);
361 static void compose_save_cb (GtkAction *action,
364 static void compose_attach_cb (GtkAction *action,
366 static void compose_insert_file_cb (GtkAction *action,
368 static void compose_insert_sig_cb (GtkAction *action,
371 static void compose_close_cb (GtkAction *action,
374 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
376 static void compose_address_cb (GtkAction *action,
378 static void about_show_cb (GtkAction *action,
380 static void compose_template_activate_cb(GtkWidget *widget,
383 static void compose_ext_editor_cb (GtkAction *action,
386 static gint compose_delete_cb (GtkWidget *widget,
390 static void compose_undo_cb (GtkAction *action,
392 static void compose_redo_cb (GtkAction *action,
394 static void compose_cut_cb (GtkAction *action,
396 static void compose_copy_cb (GtkAction *action,
398 static void compose_paste_cb (GtkAction *action,
400 static void compose_paste_as_quote_cb (GtkAction *action,
402 static void compose_paste_no_wrap_cb (GtkAction *action,
404 static void compose_paste_wrap_cb (GtkAction *action,
406 static void compose_allsel_cb (GtkAction *action,
409 static void compose_advanced_action_cb (GtkAction *action,
412 static void compose_grab_focus_cb (GtkWidget *widget,
415 static void compose_changed_cb (GtkTextBuffer *textbuf,
418 static void compose_wrap_cb (GtkAction *action,
420 static void compose_wrap_all_cb (GtkAction *action,
422 static void compose_find_cb (GtkAction *action,
424 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
426 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
429 static void compose_toggle_ruler_cb (GtkToggleAction *action,
431 static void compose_toggle_sign_cb (GtkToggleAction *action,
433 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
435 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
436 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
437 static void activate_privacy_system (Compose *compose,
438 PrefsAccount *account,
440 static void compose_use_signing(Compose *compose, gboolean use_signing);
441 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
442 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
444 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
446 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
447 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
448 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
450 static void compose_attach_drag_received_cb (GtkWidget *widget,
451 GdkDragContext *drag_context,
454 GtkSelectionData *data,
458 static void compose_insert_drag_received_cb (GtkWidget *widget,
459 GdkDragContext *drag_context,
462 GtkSelectionData *data,
466 static void compose_header_drag_received_cb (GtkWidget *widget,
467 GdkDragContext *drag_context,
470 GtkSelectionData *data,
475 static gboolean compose_drag_drop (GtkWidget *widget,
476 GdkDragContext *drag_context,
478 guint time, gpointer user_data);
480 static void text_inserted (GtkTextBuffer *buffer,
485 static Compose *compose_generic_reply(MsgInfo *msginfo,
486 ComposeQuoteMode quote_mode,
490 gboolean followup_and_reply_to,
493 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
494 ComposeHeaderEntry *headerentry);
495 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
497 ComposeHeaderEntry *headerentry);
499 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
501 static void compose_allow_user_actions (Compose *compose, gboolean allow);
503 static void compose_nothing_cb (GtkAction *action, gpointer data)
509 static void compose_check_all (GtkAction *action, gpointer data);
510 static void compose_highlight_all (GtkAction *action, gpointer data);
511 static void compose_check_backwards (GtkAction *action, gpointer data);
512 static void compose_check_forwards_go (GtkAction *action, gpointer data);
515 static gint compose_defer_auto_save_draft (Compose *compose);
516 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
518 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
521 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
522 FolderItem *folder_item);
524 static void compose_attach_update_label(Compose *compose);
526 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data);
528 static GtkActionEntry compose_popup_entries[] =
530 {"Compose", NULL, "Compose" },
531 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
532 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
533 {"Compose/---", NULL, "---", NULL, NULL, NULL },
534 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
537 static GtkActionEntry compose_entries[] =
539 {"Menu", NULL, "Menu" },
541 {"Message", NULL, N_("_Message") },
542 {"Edit", NULL, N_("_Edit") },
544 {"Spelling", NULL, N_("_Spelling") },
546 {"Options", NULL, N_("_Options") },
547 {"Tools", NULL, N_("_Tools") },
548 {"Help", NULL, N_("_Help") },
550 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
551 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
552 {"Message/---", NULL, "---" },
554 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
555 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
556 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
557 /* {"Message/---", NULL, "---" }, */
558 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
559 /* {"Message/---", NULL, "---" }, */
560 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
563 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
564 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
565 {"Edit/---", NULL, "---" },
567 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
568 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
569 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
571 {"Edit/SpecialPaste", NULL, N_("Special paste") },
572 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
573 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
574 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
576 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
578 {"Edit/Advanced", NULL, N_("A_dvanced") },
579 {"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*/
580 {"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*/
581 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
582 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
583 {"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*/
584 {"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*/
585 {"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*/
586 {"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*/
587 {"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*/
588 {"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*/
589 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
590 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
591 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
592 {"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*/
594 /* {"Edit/---", NULL, "---" }, */
595 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
597 /* {"Edit/---", NULL, "---" }, */
598 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
599 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
600 /* {"Edit/---", NULL, "---" }, */
601 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
604 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
605 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
606 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
607 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
609 {"Spelling/---", NULL, "---" },
610 {"Spelling/Options", NULL, N_("_Options") },
615 {"Options/ReplyMode", NULL, N_("Reply _mode") },
616 {"Options/---", NULL, "---" },
617 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
618 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
620 /* {"Options/---", NULL, "---" }, */
622 {"Options/Priority", NULL, N_("_Priority") },
624 {"Options/Encoding", NULL, N_("Character _encoding") },
625 {"Options/Encoding/---", NULL, "---" },
626 #define ENC_ACTION(cs_char,c_char,string) \
627 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
629 {"Options/Encoding/Western", NULL, N_("Western European") },
630 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
631 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
632 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
633 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
634 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
635 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
636 {"Options/Encoding/Korean", NULL, N_("Korean") },
637 {"Options/Encoding/Thai", NULL, N_("Thai") },
640 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
642 {"Tools/Template", NULL, N_("_Template") },
643 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
644 {"Tools/Actions", NULL, N_("Actio_ns") },
645 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
648 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
651 static GtkToggleActionEntry compose_toggle_entries[] =
653 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
654 {"Edit/AutoIndent", NULL, N_("Follow _indentation"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
655 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
656 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
657 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
658 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
659 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
662 static GtkRadioActionEntry compose_radio_rm_entries[] =
664 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
665 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
666 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
667 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
670 static GtkRadioActionEntry compose_radio_prio_entries[] =
672 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
673 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
674 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
675 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
676 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
679 static GtkRadioActionEntry compose_radio_enc_entries[] =
681 ENC_ACTION(CS_AUTO, C_AUTO, "_Automatic"), /* RADIO compose_set_encoding_cb */
682 ENC_ACTION(CS_US_ASCII, C_US_ASCII, "7bit ASCII (US-ASC_II)"), /* RADIO compose_set_encoding_cb */
683 ENC_ACTION(CS_UTF_8, C_UTF_8, "Unicode (_UTF-8)"), /* RADIO compose_set_encoding_cb */
684 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
685 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
686 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
687 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, "Central European (ISO-8859-_2)"), /* RADIO compose_set_encoding_cb */
688 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
689 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
690 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, "Greek (ISO-8859-_7)"), /* RADIO compose_set_encoding_cb */
691 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
692 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
693 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, "Turkish (ISO-8859-_9)"), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
714 static GtkTargetEntry compose_mime_types[] =
716 {"text/uri-list", 0, 0},
717 {"UTF8_STRING", 0, 0},
721 static gboolean compose_put_existing_to_front(MsgInfo *info)
723 GList *compose_list = compose_get_compose_list();
727 for (elem = compose_list; elem != NULL && elem->data != NULL;
729 Compose *c = (Compose*)elem->data;
731 if (!c->targetinfo || !c->targetinfo->msgid ||
735 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
736 gtkut_window_popup(c->window);
744 static GdkColor quote_color1 =
745 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
746 static GdkColor quote_color2 =
747 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
748 static GdkColor quote_color3 =
749 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
751 static GdkColor quote_bgcolor1 =
752 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
753 static GdkColor quote_bgcolor2 =
754 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
755 static GdkColor quote_bgcolor3 =
756 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
758 static GdkColor signature_color = {
765 static GdkColor uri_color = {
772 static void compose_create_tags(GtkTextView *text, Compose *compose)
774 GtkTextBuffer *buffer;
775 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
781 buffer = gtk_text_view_get_buffer(text);
783 if (prefs_common.enable_color) {
784 /* grab the quote colors, converting from an int to a GdkColor */
785 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
787 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
789 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
791 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
793 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
795 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
797 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
799 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
802 signature_color = quote_color1 = quote_color2 = quote_color3 =
803 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
806 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
807 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
808 "foreground-gdk", "e_color1,
809 "paragraph-background-gdk", "e_bgcolor1,
811 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
812 "foreground-gdk", "e_color2,
813 "paragraph-background-gdk", "e_bgcolor2,
815 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
816 "foreground-gdk", "e_color3,
817 "paragraph-background-gdk", "e_bgcolor3,
820 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
821 "foreground-gdk", "e_color1,
823 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
824 "foreground-gdk", "e_color2,
826 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
827 "foreground-gdk", "e_color3,
831 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
832 "foreground-gdk", &signature_color,
835 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
836 "foreground-gdk", &uri_color,
838 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
839 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
841 color[0] = quote_color1;
842 color[1] = quote_color2;
843 color[2] = quote_color3;
844 color[3] = quote_bgcolor1;
845 color[4] = quote_bgcolor2;
846 color[5] = quote_bgcolor3;
847 color[6] = signature_color;
848 color[7] = uri_color;
849 cmap = gdk_drawable_get_colormap(compose->window->window);
850 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
852 for (i = 0; i < 8; i++) {
853 if (success[i] == FALSE) {
856 g_warning("Compose: color allocation failed.\n");
857 style = gtk_widget_get_style(GTK_WIDGET(text));
858 quote_color1 = quote_color2 = quote_color3 =
859 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
860 signature_color = uri_color = black;
865 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
866 GPtrArray *attach_files)
868 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
871 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
873 return compose_generic_new(account, mailto, item, NULL, NULL);
876 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
878 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
881 #define SCROLL_TO_CURSOR(compose) { \
882 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
883 gtk_text_view_get_buffer( \
884 GTK_TEXT_VIEW(compose->text))); \
885 gtk_text_view_scroll_mark_onscreen( \
886 GTK_TEXT_VIEW(compose->text), \
890 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
891 GPtrArray *attach_files, GList *listAddress )
894 GtkTextView *textview;
895 GtkTextBuffer *textbuf;
897 const gchar *subject_format = NULL;
898 const gchar *body_format = NULL;
899 gchar *mailto_from = NULL;
900 PrefsAccount *mailto_account = NULL;
901 MsgInfo* dummyinfo = NULL;
903 /* check if mailto defines a from */
904 if (mailto && *mailto != '\0') {
905 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL);
906 /* mailto defines a from, check if we can get account prefs from it,
907 if not, the account prefs will be guessed using other ways, but we'll keep
910 mailto_account = account_find_from_address(mailto_from, TRUE);
912 account = mailto_account;
915 /* if no account prefs set from mailto, set if from folder prefs (if any) */
916 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
917 account = account_find_from_id(item->prefs->default_account);
919 /* if no account prefs set, fallback to the current one */
920 if (!account) account = cur_account;
921 g_return_val_if_fail(account != NULL, NULL);
923 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
925 /* override from name if mailto asked for it */
927 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
930 /* override from name according to folder properties */
931 if (item && item->prefs &&
932 item->prefs->compose_with_format &&
933 item->prefs->compose_override_from_format &&
934 *item->prefs->compose_override_from_format != '\0') {
939 dummyinfo = compose_msginfo_new_from_compose(compose);
941 /* decode \-escape sequences in the internal representation of the quote format */
942 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
943 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
946 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
949 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
951 quote_fmt_scan_string(tmp);
954 buf = quote_fmt_get_buffer();
956 alertpanel_error(_("New message From format error."));
958 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
959 quote_fmt_reset_vartable();
964 compose->replyinfo = NULL;
965 compose->fwdinfo = NULL;
967 textview = GTK_TEXT_VIEW(compose->text);
968 textbuf = gtk_text_view_get_buffer(textview);
969 compose_create_tags(textview, compose);
971 undo_block(compose->undostruct);
973 compose_set_dictionaries_from_folder_prefs(compose, item);
976 if (account->auto_sig)
977 compose_insert_sig(compose, FALSE);
978 gtk_text_buffer_get_start_iter(textbuf, &iter);
979 gtk_text_buffer_place_cursor(textbuf, &iter);
981 if (account->protocol != A_NNTP) {
982 if (mailto && *mailto != '\0') {
983 compose_entries_set(compose, mailto, COMPOSE_TO);
985 } else if (item && item->prefs->enable_default_to) {
986 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
987 compose_entry_mark_default_to(compose, item->prefs->default_to);
989 if (item && item->ret_rcpt) {
990 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
993 if (mailto && *mailto != '\0') {
994 if (!strchr(mailto, '@'))
995 compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
997 compose_entries_set(compose, mailto, COMPOSE_TO);
998 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
999 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1002 * CLAWS: just don't allow return receipt request, even if the user
1003 * may want to send an email. simple but foolproof.
1005 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1007 compose_add_field_list( compose, listAddress );
1009 if (item && item->prefs && item->prefs->compose_with_format) {
1010 subject_format = item->prefs->compose_subject_format;
1011 body_format = item->prefs->compose_body_format;
1012 } else if (account->compose_with_format) {
1013 subject_format = account->compose_subject_format;
1014 body_format = account->compose_body_format;
1015 } else if (prefs_common.compose_with_format) {
1016 subject_format = prefs_common.compose_subject_format;
1017 body_format = prefs_common.compose_body_format;
1020 if (subject_format || body_format) {
1023 && *subject_format != '\0' )
1025 gchar *subject = NULL;
1030 dummyinfo = compose_msginfo_new_from_compose(compose);
1032 /* decode \-escape sequences in the internal representation of the quote format */
1033 tmp = malloc(strlen(subject_format)+1);
1034 pref_get_unescaped_pref(tmp, subject_format);
1036 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1038 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1039 compose->gtkaspell);
1041 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1043 quote_fmt_scan_string(tmp);
1046 buf = quote_fmt_get_buffer();
1048 alertpanel_error(_("New message subject format error."));
1050 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1051 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1052 quote_fmt_reset_vartable();
1059 && *body_format != '\0' )
1062 GtkTextBuffer *buffer;
1063 GtkTextIter start, end;
1067 dummyinfo = compose_msginfo_new_from_compose(compose);
1069 text = GTK_TEXT_VIEW(compose->text);
1070 buffer = gtk_text_view_get_buffer(text);
1071 gtk_text_buffer_get_start_iter(buffer, &start);
1072 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1073 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1075 compose_quote_fmt(compose, dummyinfo,
1077 NULL, tmp, FALSE, TRUE,
1078 _("New message body format error at line %d."));
1079 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1080 quote_fmt_reset_vartable();
1086 procmsg_msginfo_free( dummyinfo );
1092 for (i = 0; i < attach_files->len; i++) {
1093 file = g_ptr_array_index(attach_files, i);
1094 compose_attach_append(compose, file, file, NULL);
1098 compose_show_first_last_header(compose, TRUE);
1100 /* Set save folder */
1101 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1102 gchar *folderidentifier;
1104 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1105 folderidentifier = folder_item_get_identifier(item);
1106 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1107 g_free(folderidentifier);
1110 gtk_widget_grab_focus(compose->header_last->entry);
1112 undo_unblock(compose->undostruct);
1114 if (prefs_common.auto_exteditor)
1115 compose_exec_ext_editor(compose);
1117 compose->draft_timeout_tag = -1;
1118 SCROLL_TO_CURSOR(compose);
1120 compose->modified = FALSE;
1121 compose_set_title(compose);
1125 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1126 gboolean override_pref)
1128 gchar *privacy = NULL;
1130 g_return_if_fail(compose != NULL);
1131 g_return_if_fail(account != NULL);
1133 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1136 if (account->default_privacy_system
1137 && strlen(account->default_privacy_system)) {
1138 privacy = account->default_privacy_system;
1140 GSList *privacy_avail = privacy_get_system_ids();
1141 if (privacy_avail && g_slist_length(privacy_avail)) {
1142 privacy = (gchar *)(privacy_avail->data);
1145 if (privacy != NULL) {
1146 if (compose->privacy_system == NULL)
1147 compose->privacy_system = g_strdup(privacy);
1148 else if (*(compose->privacy_system) == '\0') {
1149 g_free(compose->privacy_system);
1150 compose->privacy_system = g_strdup(privacy);
1152 compose_update_privacy_system_menu_item(compose, FALSE);
1153 compose_use_encryption(compose, TRUE);
1157 static void compose_force_signing(Compose *compose, PrefsAccount *account)
1159 gchar *privacy = NULL;
1161 if (account->default_privacy_system
1162 && strlen(account->default_privacy_system)) {
1163 privacy = account->default_privacy_system;
1165 GSList *privacy_avail = privacy_get_system_ids();
1166 if (privacy_avail && g_slist_length(privacy_avail)) {
1167 privacy = (gchar *)(privacy_avail->data);
1170 if (privacy != NULL) {
1171 if (compose->privacy_system == NULL)
1172 compose->privacy_system = g_strdup(privacy);
1173 compose_update_privacy_system_menu_item(compose, FALSE);
1174 compose_use_signing(compose, TRUE);
1178 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1182 Compose *compose = NULL;
1184 g_return_val_if_fail(msginfo_list != NULL, NULL);
1186 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1187 g_return_val_if_fail(msginfo != NULL, NULL);
1189 list_len = g_slist_length(msginfo_list);
1193 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1194 FALSE, prefs_common.default_reply_list, FALSE, body);
1196 case COMPOSE_REPLY_WITH_QUOTE:
1197 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1198 FALSE, prefs_common.default_reply_list, FALSE, body);
1200 case COMPOSE_REPLY_WITHOUT_QUOTE:
1201 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1202 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1204 case COMPOSE_REPLY_TO_SENDER:
1205 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1206 FALSE, FALSE, TRUE, body);
1208 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1209 compose = compose_followup_and_reply_to(msginfo,
1210 COMPOSE_QUOTE_CHECK,
1211 FALSE, FALSE, body);
1213 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1214 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1215 FALSE, FALSE, TRUE, body);
1217 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1218 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1219 FALSE, FALSE, TRUE, NULL);
1221 case COMPOSE_REPLY_TO_ALL:
1222 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1223 TRUE, FALSE, FALSE, body);
1225 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1226 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1227 TRUE, FALSE, FALSE, body);
1229 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1230 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1231 TRUE, FALSE, FALSE, NULL);
1233 case COMPOSE_REPLY_TO_LIST:
1234 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1235 FALSE, TRUE, FALSE, body);
1237 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1238 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1239 FALSE, TRUE, FALSE, body);
1241 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1242 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1243 FALSE, TRUE, FALSE, NULL);
1245 case COMPOSE_FORWARD:
1246 if (prefs_common.forward_as_attachment) {
1247 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1250 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1254 case COMPOSE_FORWARD_INLINE:
1255 /* check if we reply to more than one Message */
1256 if (list_len == 1) {
1257 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1260 /* more messages FALL THROUGH */
1261 case COMPOSE_FORWARD_AS_ATTACH:
1262 compose = compose_forward_multiple(NULL, msginfo_list);
1264 case COMPOSE_REDIRECT:
1265 compose = compose_redirect(NULL, msginfo, FALSE);
1268 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1271 if (compose == NULL) {
1272 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1276 compose->rmode = mode;
1277 switch (compose->rmode) {
1279 case COMPOSE_REPLY_WITH_QUOTE:
1280 case COMPOSE_REPLY_WITHOUT_QUOTE:
1281 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1282 debug_print("reply mode Normal\n");
1283 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1284 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1286 case COMPOSE_REPLY_TO_SENDER:
1287 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1288 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1289 debug_print("reply mode Sender\n");
1290 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1292 case COMPOSE_REPLY_TO_ALL:
1293 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1294 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1295 debug_print("reply mode All\n");
1296 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1298 case COMPOSE_REPLY_TO_LIST:
1299 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1300 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1301 debug_print("reply mode List\n");
1302 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1310 static Compose *compose_reply(MsgInfo *msginfo,
1311 ComposeQuoteMode quote_mode,
1317 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1318 to_sender, FALSE, body);
1321 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1322 ComposeQuoteMode quote_mode,
1327 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1328 to_sender, TRUE, body);
1331 static void compose_extract_original_charset(Compose *compose)
1333 MsgInfo *info = NULL;
1334 if (compose->replyinfo) {
1335 info = compose->replyinfo;
1336 } else if (compose->fwdinfo) {
1337 info = compose->fwdinfo;
1338 } else if (compose->targetinfo) {
1339 info = compose->targetinfo;
1342 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1343 MimeInfo *partinfo = mimeinfo;
1344 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1345 partinfo = procmime_mimeinfo_next(partinfo);
1347 compose->orig_charset =
1348 g_strdup(procmime_mimeinfo_get_parameter(
1349 partinfo, "charset"));
1351 procmime_mimeinfo_free_all(mimeinfo);
1355 #define SIGNAL_BLOCK(buffer) { \
1356 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1357 G_CALLBACK(compose_changed_cb), \
1359 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1360 G_CALLBACK(text_inserted), \
1364 #define SIGNAL_UNBLOCK(buffer) { \
1365 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1366 G_CALLBACK(compose_changed_cb), \
1368 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1369 G_CALLBACK(text_inserted), \
1373 static Compose *compose_generic_reply(MsgInfo *msginfo,
1374 ComposeQuoteMode quote_mode,
1375 gboolean to_all, gboolean to_ml,
1377 gboolean followup_and_reply_to,
1381 PrefsAccount *account = NULL;
1382 GtkTextView *textview;
1383 GtkTextBuffer *textbuf;
1384 gboolean quote = FALSE;
1385 const gchar *qmark = NULL;
1386 const gchar *body_fmt = NULL;
1388 g_return_val_if_fail(msginfo != NULL, NULL);
1389 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1391 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1393 g_return_val_if_fail(account != NULL, NULL);
1395 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1397 compose->updating = TRUE;
1399 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1400 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1402 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1403 if (!compose->replyinfo)
1404 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1406 compose_extract_original_charset(compose);
1408 if (msginfo->folder && msginfo->folder->ret_rcpt)
1409 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1411 /* Set save folder */
1412 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1413 gchar *folderidentifier;
1415 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1416 folderidentifier = folder_item_get_identifier(msginfo->folder);
1417 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1418 g_free(folderidentifier);
1421 if (compose_parse_header(compose, msginfo) < 0) {
1422 compose->updating = FALSE;
1423 compose_destroy(compose);
1427 /* override from name according to folder properties */
1428 if (msginfo->folder && msginfo->folder->prefs &&
1429 msginfo->folder->prefs->reply_with_format &&
1430 msginfo->folder->prefs->reply_override_from_format &&
1431 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1436 /* decode \-escape sequences in the internal representation of the quote format */
1437 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1438 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1441 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1442 compose->gtkaspell);
1444 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1446 quote_fmt_scan_string(tmp);
1449 buf = quote_fmt_get_buffer();
1451 alertpanel_error(_("Message reply From format error."));
1453 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1454 quote_fmt_reset_vartable();
1459 textview = (GTK_TEXT_VIEW(compose->text));
1460 textbuf = gtk_text_view_get_buffer(textview);
1461 compose_create_tags(textview, compose);
1463 undo_block(compose->undostruct);
1465 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1468 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1469 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1470 /* use the reply format of folder (if enabled), or the account's one
1471 (if enabled) or fallback to the global reply format, which is always
1472 enabled (even if empty), and use the relevant quotemark */
1474 if (msginfo->folder && msginfo->folder->prefs &&
1475 msginfo->folder->prefs->reply_with_format) {
1476 qmark = msginfo->folder->prefs->reply_quotemark;
1477 body_fmt = msginfo->folder->prefs->reply_body_format;
1479 } else if (account->reply_with_format) {
1480 qmark = account->reply_quotemark;
1481 body_fmt = account->reply_body_format;
1484 qmark = prefs_common.quotemark;
1485 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1486 body_fmt = gettext(prefs_common.quotefmt);
1493 /* empty quotemark is not allowed */
1494 if (qmark == NULL || *qmark == '\0')
1496 compose_quote_fmt(compose, compose->replyinfo,
1497 body_fmt, qmark, body, FALSE, TRUE,
1498 _("Message reply format error at line %d."));
1499 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1500 quote_fmt_reset_vartable();
1503 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1504 compose_force_encryption(compose, account, FALSE);
1507 privacy_msginfo_get_signed_state(compose->replyinfo);
1508 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1509 compose_force_signing(compose, account);
1512 SIGNAL_BLOCK(textbuf);
1514 if (account->auto_sig)
1515 compose_insert_sig(compose, FALSE);
1517 compose_wrap_all(compose);
1519 SIGNAL_UNBLOCK(textbuf);
1521 gtk_widget_grab_focus(compose->text);
1523 undo_unblock(compose->undostruct);
1525 if (prefs_common.auto_exteditor)
1526 compose_exec_ext_editor(compose);
1528 compose->modified = FALSE;
1529 compose_set_title(compose);
1531 compose->updating = FALSE;
1532 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1533 SCROLL_TO_CURSOR(compose);
1535 if (compose->deferred_destroy) {
1536 compose_destroy(compose);
1543 #define INSERT_FW_HEADER(var, hdr) \
1544 if (msginfo->var && *msginfo->var) { \
1545 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1546 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1547 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1550 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1551 gboolean as_attach, const gchar *body,
1552 gboolean no_extedit,
1556 GtkTextView *textview;
1557 GtkTextBuffer *textbuf;
1560 g_return_val_if_fail(msginfo != NULL, NULL);
1561 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1564 !(account = compose_guess_forward_account_from_msginfo
1566 account = cur_account;
1568 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1570 compose->updating = TRUE;
1571 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1572 if (!compose->fwdinfo)
1573 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1575 compose_extract_original_charset(compose);
1577 if (msginfo->subject && *msginfo->subject) {
1578 gchar *buf, *buf2, *p;
1580 buf = p = g_strdup(msginfo->subject);
1581 p += subject_get_prefix_length(p);
1582 memmove(buf, p, strlen(p) + 1);
1584 buf2 = g_strdup_printf("Fw: %s", buf);
1585 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1591 /* override from name according to folder properties */
1592 if (msginfo->folder && msginfo->folder->prefs &&
1593 msginfo->folder->prefs->forward_with_format &&
1594 msginfo->folder->prefs->forward_override_from_format &&
1595 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1599 MsgInfo *full_msginfo = NULL;
1602 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1604 full_msginfo = procmsg_msginfo_copy(msginfo);
1606 /* decode \-escape sequences in the internal representation of the quote format */
1607 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1608 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1611 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1612 compose->gtkaspell);
1614 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1616 quote_fmt_scan_string(tmp);
1619 buf = quote_fmt_get_buffer();
1621 alertpanel_error(_("Message forward From format error."));
1623 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1624 quote_fmt_reset_vartable();
1627 procmsg_msginfo_free(full_msginfo);
1630 textview = GTK_TEXT_VIEW(compose->text);
1631 textbuf = gtk_text_view_get_buffer(textview);
1632 compose_create_tags(textview, compose);
1634 undo_block(compose->undostruct);
1638 msgfile = procmsg_get_message_file(msginfo);
1639 if (!is_file_exist(msgfile))
1640 g_warning("%s: file not exist\n", msgfile);
1642 compose_attach_append(compose, msgfile, msgfile,
1647 const gchar *qmark = NULL;
1648 const gchar *body_fmt = NULL;
1649 MsgInfo *full_msginfo;
1651 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1652 body_fmt = gettext(prefs_common.fw_quotefmt);
1656 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1658 full_msginfo = procmsg_msginfo_copy(msginfo);
1660 /* use the forward format of folder (if enabled), or the account's one
1661 (if enabled) or fallback to the global forward format, which is always
1662 enabled (even if empty), and use the relevant quotemark */
1663 if (msginfo->folder && msginfo->folder->prefs &&
1664 msginfo->folder->prefs->forward_with_format) {
1665 qmark = msginfo->folder->prefs->forward_quotemark;
1666 body_fmt = msginfo->folder->prefs->forward_body_format;
1668 } else if (account->forward_with_format) {
1669 qmark = account->forward_quotemark;
1670 body_fmt = account->forward_body_format;
1673 qmark = prefs_common.fw_quotemark;
1674 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1675 body_fmt = gettext(prefs_common.fw_quotefmt);
1680 /* empty quotemark is not allowed */
1681 if (qmark == NULL || *qmark == '\0')
1684 compose_quote_fmt(compose, full_msginfo,
1685 body_fmt, qmark, body, FALSE, TRUE,
1686 _("Message forward format error at line %d."));
1687 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1688 quote_fmt_reset_vartable();
1689 compose_attach_parts(compose, msginfo);
1691 procmsg_msginfo_free(full_msginfo);
1694 SIGNAL_BLOCK(textbuf);
1696 if (account->auto_sig)
1697 compose_insert_sig(compose, FALSE);
1699 compose_wrap_all(compose);
1701 SIGNAL_UNBLOCK(textbuf);
1703 gtk_text_buffer_get_start_iter(textbuf, &iter);
1704 gtk_text_buffer_place_cursor(textbuf, &iter);
1706 gtk_widget_grab_focus(compose->header_last->entry);
1708 if (!no_extedit && prefs_common.auto_exteditor)
1709 compose_exec_ext_editor(compose);
1712 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1713 gchar *folderidentifier;
1715 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1716 folderidentifier = folder_item_get_identifier(msginfo->folder);
1717 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1718 g_free(folderidentifier);
1721 undo_unblock(compose->undostruct);
1723 compose->modified = FALSE;
1724 compose_set_title(compose);
1726 compose->updating = FALSE;
1727 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1728 SCROLL_TO_CURSOR(compose);
1730 if (compose->deferred_destroy) {
1731 compose_destroy(compose);
1738 #undef INSERT_FW_HEADER
1740 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1743 GtkTextView *textview;
1744 GtkTextBuffer *textbuf;
1748 gboolean single_mail = TRUE;
1750 g_return_val_if_fail(msginfo_list != NULL, NULL);
1752 if (g_slist_length(msginfo_list) > 1)
1753 single_mail = FALSE;
1755 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1756 if (((MsgInfo *)msginfo->data)->folder == NULL)
1759 /* guess account from first selected message */
1761 !(account = compose_guess_forward_account_from_msginfo
1762 (msginfo_list->data)))
1763 account = cur_account;
1765 g_return_val_if_fail(account != NULL, NULL);
1767 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1768 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1769 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1772 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1774 compose->updating = TRUE;
1776 /* override from name according to folder properties */
1777 if (msginfo_list->data) {
1778 MsgInfo *msginfo = msginfo_list->data;
1780 if (msginfo->folder && msginfo->folder->prefs &&
1781 msginfo->folder->prefs->forward_with_format &&
1782 msginfo->folder->prefs->forward_override_from_format &&
1783 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1788 /* decode \-escape sequences in the internal representation of the quote format */
1789 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1790 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1793 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1794 compose->gtkaspell);
1796 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1798 quote_fmt_scan_string(tmp);
1801 buf = quote_fmt_get_buffer();
1803 alertpanel_error(_("Message forward From format error."));
1805 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1806 quote_fmt_reset_vartable();
1812 textview = GTK_TEXT_VIEW(compose->text);
1813 textbuf = gtk_text_view_get_buffer(textview);
1814 compose_create_tags(textview, compose);
1816 undo_block(compose->undostruct);
1817 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1818 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1820 if (!is_file_exist(msgfile))
1821 g_warning("%s: file not exist\n", msgfile);
1823 compose_attach_append(compose, msgfile, msgfile,
1829 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1830 if (info->subject && *info->subject) {
1831 gchar *buf, *buf2, *p;
1833 buf = p = g_strdup(info->subject);
1834 p += subject_get_prefix_length(p);
1835 memmove(buf, p, strlen(p) + 1);
1837 buf2 = g_strdup_printf("Fw: %s", buf);
1838 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1844 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1845 _("Fw: multiple emails"));
1848 SIGNAL_BLOCK(textbuf);
1850 if (account->auto_sig)
1851 compose_insert_sig(compose, FALSE);
1853 compose_wrap_all(compose);
1855 SIGNAL_UNBLOCK(textbuf);
1857 gtk_text_buffer_get_start_iter(textbuf, &iter);
1858 gtk_text_buffer_place_cursor(textbuf, &iter);
1860 gtk_widget_grab_focus(compose->header_last->entry);
1861 undo_unblock(compose->undostruct);
1862 compose->modified = FALSE;
1863 compose_set_title(compose);
1865 compose->updating = FALSE;
1866 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1867 SCROLL_TO_CURSOR(compose);
1869 if (compose->deferred_destroy) {
1870 compose_destroy(compose);
1877 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1879 GtkTextIter start = *iter;
1880 GtkTextIter end_iter;
1881 int start_pos = gtk_text_iter_get_offset(&start);
1883 if (!compose->account->sig_sep)
1886 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1887 start_pos+strlen(compose->account->sig_sep));
1889 /* check sig separator */
1890 str = gtk_text_iter_get_text(&start, &end_iter);
1891 if (!strcmp(str, compose->account->sig_sep)) {
1893 /* check end of line (\n) */
1894 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1895 start_pos+strlen(compose->account->sig_sep));
1896 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1897 start_pos+strlen(compose->account->sig_sep)+1);
1898 tmp = gtk_text_iter_get_text(&start, &end_iter);
1899 if (!strcmp(tmp,"\n")) {
1911 static void compose_colorize_signature(Compose *compose)
1913 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1915 GtkTextIter end_iter;
1916 gtk_text_buffer_get_start_iter(buffer, &iter);
1917 while (gtk_text_iter_forward_line(&iter))
1918 if (compose_is_sig_separator(compose, buffer, &iter)) {
1919 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1920 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1924 #define BLOCK_WRAP() { \
1925 prev_autowrap = compose->autowrap; \
1926 buffer = gtk_text_view_get_buffer( \
1927 GTK_TEXT_VIEW(compose->text)); \
1928 compose->autowrap = FALSE; \
1930 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1931 G_CALLBACK(compose_changed_cb), \
1933 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1934 G_CALLBACK(text_inserted), \
1937 #define UNBLOCK_WRAP() { \
1938 compose->autowrap = prev_autowrap; \
1939 if (compose->autowrap) { \
1940 gint old = compose->draft_timeout_tag; \
1941 compose->draft_timeout_tag = -2; \
1942 compose_wrap_all(compose); \
1943 compose->draft_timeout_tag = old; \
1946 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1947 G_CALLBACK(compose_changed_cb), \
1949 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1950 G_CALLBACK(text_inserted), \
1954 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
1956 Compose *compose = NULL;
1957 PrefsAccount *account = NULL;
1958 GtkTextView *textview;
1959 GtkTextBuffer *textbuf;
1963 gchar buf[BUFFSIZE];
1964 gboolean use_signing = FALSE;
1965 gboolean use_encryption = FALSE;
1966 gchar *privacy_system = NULL;
1967 int priority = PRIORITY_NORMAL;
1968 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
1969 gboolean autowrap = prefs_common.autowrap;
1970 gboolean autoindent = prefs_common.auto_indent;
1972 g_return_val_if_fail(msginfo != NULL, NULL);
1973 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1975 if (compose_put_existing_to_front(msginfo)) {
1979 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
1980 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
1981 gchar queueheader_buf[BUFFSIZE];
1984 /* Select Account from queue headers */
1985 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1986 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
1987 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
1988 account = account_find_from_id(id);
1990 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1991 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
1992 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
1993 account = account_find_from_id(id);
1995 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1996 sizeof(queueheader_buf), "NAID:")) {
1997 id = atoi(&queueheader_buf[strlen("NAID:")]);
1998 account = account_find_from_id(id);
2000 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2001 sizeof(queueheader_buf), "MAID:")) {
2002 id = atoi(&queueheader_buf[strlen("MAID:")]);
2003 account = account_find_from_id(id);
2005 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2006 sizeof(queueheader_buf), "S:")) {
2007 account = account_find_from_address(queueheader_buf, FALSE);
2009 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2010 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2011 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2012 use_signing = param;
2015 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2016 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2017 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2018 use_signing = param;
2021 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2022 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2023 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2024 use_encryption = param;
2026 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2027 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2028 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2029 use_encryption = param;
2031 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2032 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2033 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2036 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2037 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2038 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2041 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2042 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2043 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2045 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2046 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2047 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2049 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2050 sizeof(queueheader_buf), "X-Priority: ")) {
2051 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2054 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2055 sizeof(queueheader_buf), "RMID:")) {
2056 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2057 if (tokens[0] && tokens[1] && tokens[2]) {
2058 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2059 if (orig_item != NULL) {
2060 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2065 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2066 sizeof(queueheader_buf), "FMID:")) {
2067 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2068 if (tokens[0] && tokens[1] && tokens[2]) {
2069 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2070 if (orig_item != NULL) {
2071 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2077 account = msginfo->folder->folder->account;
2080 if (!account && prefs_common.reedit_account_autosel) {
2081 gchar from[BUFFSIZE];
2082 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2083 extract_address(from);
2084 account = account_find_from_address(from, FALSE);
2088 account = cur_account;
2090 g_return_val_if_fail(account != NULL, NULL);
2092 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2094 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2095 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2096 compose->autowrap = autowrap;
2097 compose->replyinfo = replyinfo;
2098 compose->fwdinfo = fwdinfo;
2100 compose->updating = TRUE;
2101 compose->priority = priority;
2103 if (privacy_system != NULL) {
2104 compose->privacy_system = privacy_system;
2105 compose_use_signing(compose, use_signing);
2106 compose_use_encryption(compose, use_encryption);
2107 compose_update_privacy_system_menu_item(compose, FALSE);
2109 activate_privacy_system(compose, account, FALSE);
2112 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2114 compose_extract_original_charset(compose);
2116 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2117 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2118 gchar queueheader_buf[BUFFSIZE];
2120 /* Set message save folder */
2121 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2124 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2125 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
2126 gtk_editable_insert_text(GTK_EDITABLE(compose->savemsg_entry), &queueheader_buf[4], strlen(&queueheader_buf[4]), &startpos);
2128 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2129 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2131 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2136 if (compose_parse_header(compose, msginfo) < 0) {
2137 compose->updating = FALSE;
2138 compose_destroy(compose);
2141 compose_reedit_set_entry(compose, msginfo);
2143 textview = GTK_TEXT_VIEW(compose->text);
2144 textbuf = gtk_text_view_get_buffer(textview);
2145 compose_create_tags(textview, compose);
2147 mark = gtk_text_buffer_get_insert(textbuf);
2148 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2150 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2151 G_CALLBACK(compose_changed_cb),
2154 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2155 fp = procmime_get_first_encrypted_text_content(msginfo);
2157 compose_force_encryption(compose, account, TRUE);
2160 fp = procmime_get_first_text_content(msginfo);
2163 g_warning("Can't get text part\n");
2167 gboolean prev_autowrap = compose->autowrap;
2168 GtkTextBuffer *buffer = textbuf;
2170 while (fgets(buf, sizeof(buf), fp) != NULL) {
2172 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2178 compose_attach_parts(compose, msginfo);
2180 compose_colorize_signature(compose);
2182 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2183 G_CALLBACK(compose_changed_cb),
2186 gtk_widget_grab_focus(compose->text);
2188 if (prefs_common.auto_exteditor) {
2189 compose_exec_ext_editor(compose);
2191 compose->modified = FALSE;
2192 compose_set_title(compose);
2194 compose->updating = FALSE;
2195 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2196 SCROLL_TO_CURSOR(compose);
2198 if (compose->deferred_destroy) {
2199 compose_destroy(compose);
2203 compose->sig_str = compose_get_signature_str(compose);
2208 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2215 g_return_val_if_fail(msginfo != NULL, NULL);
2218 account = account_get_reply_account(msginfo,
2219 prefs_common.reply_account_autosel);
2220 g_return_val_if_fail(account != NULL, NULL);
2222 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2224 compose->updating = TRUE;
2226 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2227 compose->replyinfo = NULL;
2228 compose->fwdinfo = NULL;
2230 compose_show_first_last_header(compose, TRUE);
2232 gtk_widget_grab_focus(compose->header_last->entry);
2234 filename = procmsg_get_message_file(msginfo);
2236 if (filename == NULL) {
2237 compose->updating = FALSE;
2238 compose_destroy(compose);
2243 compose->redirect_filename = filename;
2245 /* Set save folder */
2246 item = msginfo->folder;
2247 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2248 gchar *folderidentifier;
2250 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2251 folderidentifier = folder_item_get_identifier(item);
2252 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
2253 g_free(folderidentifier);
2256 compose_attach_parts(compose, msginfo);
2258 if (msginfo->subject)
2259 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2261 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2263 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2264 _("Message redirect format error at line %d."));
2265 quote_fmt_reset_vartable();
2266 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2268 compose_colorize_signature(compose);
2271 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2272 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2273 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2275 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2276 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2277 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2278 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2279 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2280 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2281 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2282 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2284 if (compose->toolbar->draft_btn)
2285 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2286 if (compose->toolbar->insert_btn)
2287 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2288 if (compose->toolbar->attach_btn)
2289 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2290 if (compose->toolbar->sig_btn)
2291 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2292 if (compose->toolbar->exteditor_btn)
2293 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2294 if (compose->toolbar->linewrap_current_btn)
2295 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2296 if (compose->toolbar->linewrap_all_btn)
2297 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2299 compose->modified = FALSE;
2300 compose_set_title(compose);
2301 compose->updating = FALSE;
2302 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2303 SCROLL_TO_CURSOR(compose);
2305 if (compose->deferred_destroy) {
2306 compose_destroy(compose);
2313 GList *compose_get_compose_list(void)
2315 return compose_list;
2318 void compose_entry_append(Compose *compose, const gchar *address,
2319 ComposeEntryType type)
2321 const gchar *header;
2323 gboolean in_quote = FALSE;
2324 if (!address || *address == '\0') return;
2331 header = N_("Bcc:");
2333 case COMPOSE_REPLYTO:
2334 header = N_("Reply-To:");
2336 case COMPOSE_NEWSGROUPS:
2337 header = N_("Newsgroups:");
2339 case COMPOSE_FOLLOWUPTO:
2340 header = N_( "Followup-To:");
2347 header = prefs_common_translated_header_name(header);
2349 cur = begin = (gchar *)address;
2351 /* we separate the line by commas, but not if we're inside a quoted
2353 while (*cur != '\0') {
2355 in_quote = !in_quote;
2356 if (*cur == ',' && !in_quote) {
2357 gchar *tmp = g_strdup(begin);
2359 tmp[cur-begin]='\0';
2362 while (*tmp == ' ' || *tmp == '\t')
2364 compose_add_header_entry(compose, header, tmp);
2371 gchar *tmp = g_strdup(begin);
2373 tmp[cur-begin]='\0';
2376 while (*tmp == ' ' || *tmp == '\t')
2378 compose_add_header_entry(compose, header, tmp);
2383 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2385 static GdkColor yellow;
2386 static GdkColor black;
2387 static gboolean yellow_initialised = FALSE;
2391 if (!yellow_initialised) {
2392 gdk_color_parse("#f5f6be", &yellow);
2393 gdk_color_parse("#000000", &black);
2394 yellow_initialised = gdk_colormap_alloc_color(
2395 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2396 yellow_initialised &= gdk_colormap_alloc_color(
2397 gdk_colormap_get_system(), &black, FALSE, TRUE);
2400 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2401 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2402 if (gtk_entry_get_text(entry) &&
2403 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2404 if (yellow_initialised) {
2405 gtk_widget_modify_base(
2406 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2407 GTK_STATE_NORMAL, &yellow);
2408 gtk_widget_modify_text(
2409 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2410 GTK_STATE_NORMAL, &black);
2416 void compose_toolbar_cb(gint action, gpointer data)
2418 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2419 Compose *compose = (Compose*)toolbar_item->parent;
2421 g_return_if_fail(compose != NULL);
2425 compose_send_cb(NULL, compose);
2428 compose_send_later_cb(NULL, compose);
2431 compose_draft(compose, COMPOSE_QUIT_EDITING);
2434 compose_insert_file_cb(NULL, compose);
2437 compose_attach_cb(NULL, compose);
2440 compose_insert_sig(compose, FALSE);
2443 compose_ext_editor_cb(NULL, compose);
2445 case A_LINEWRAP_CURRENT:
2446 compose_beautify_paragraph(compose, NULL, TRUE);
2448 case A_LINEWRAP_ALL:
2449 compose_wrap_all_full(compose, TRUE);
2452 compose_address_cb(NULL, compose);
2455 case A_CHECK_SPELLING:
2456 compose_check_all(NULL, compose);
2464 static void compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2469 gchar *subject = NULL;
2473 gchar **attach = NULL;
2475 /* get mailto parts but skip from */
2476 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2479 compose_entry_append(compose, to, to_type);
2481 compose_entry_append(compose, cc, COMPOSE_CC);
2483 compose_entry_append(compose, bcc, COMPOSE_BCC);
2485 if (!g_utf8_validate (subject, -1, NULL)) {
2486 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2487 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2490 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2494 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2495 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2498 gboolean prev_autowrap = compose->autowrap;
2500 compose->autowrap = FALSE;
2502 mark = gtk_text_buffer_get_insert(buffer);
2503 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2505 if (!g_utf8_validate (body, -1, NULL)) {
2506 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2507 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2510 gtk_text_buffer_insert(buffer, &iter, body, -1);
2512 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2514 compose->autowrap = prev_autowrap;
2515 if (compose->autowrap)
2516 compose_wrap_all(compose);
2520 gint i = 0, att = 0;
2521 gchar *warn_files = NULL;
2522 while (attach[i] != NULL) {
2523 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2524 if (utf8_filename) {
2525 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2526 gchar *tmp = g_strdup_printf("%s%s\n",
2527 warn_files?warn_files:"",
2533 g_free(utf8_filename);
2535 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2540 alertpanel_notice(ngettext(
2541 "The following file has been attached: \n%s",
2542 "The following files have been attached: \n%s", att), warn_files);
2554 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2556 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2557 {"Cc:", NULL, TRUE},
2558 {"References:", NULL, FALSE},
2559 {"Bcc:", NULL, TRUE},
2560 {"Newsgroups:", NULL, TRUE},
2561 {"Followup-To:", NULL, TRUE},
2562 {"List-Post:", NULL, FALSE},
2563 {"X-Priority:", NULL, FALSE},
2564 {NULL, NULL, FALSE}};
2580 g_return_val_if_fail(msginfo != NULL, -1);
2582 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2583 procheader_get_header_fields(fp, hentry);
2586 if (hentry[H_REPLY_TO].body != NULL) {
2587 if (hentry[H_REPLY_TO].body[0] != '\0') {
2589 conv_unmime_header(hentry[H_REPLY_TO].body,
2592 g_free(hentry[H_REPLY_TO].body);
2593 hentry[H_REPLY_TO].body = NULL;
2595 if (hentry[H_CC].body != NULL) {
2596 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2597 g_free(hentry[H_CC].body);
2598 hentry[H_CC].body = NULL;
2600 if (hentry[H_REFERENCES].body != NULL) {
2601 if (compose->mode == COMPOSE_REEDIT)
2602 compose->references = hentry[H_REFERENCES].body;
2604 compose->references = compose_parse_references
2605 (hentry[H_REFERENCES].body, msginfo->msgid);
2606 g_free(hentry[H_REFERENCES].body);
2608 hentry[H_REFERENCES].body = NULL;
2610 if (hentry[H_BCC].body != NULL) {
2611 if (compose->mode == COMPOSE_REEDIT)
2613 conv_unmime_header(hentry[H_BCC].body, NULL);
2614 g_free(hentry[H_BCC].body);
2615 hentry[H_BCC].body = NULL;
2617 if (hentry[H_NEWSGROUPS].body != NULL) {
2618 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2619 hentry[H_NEWSGROUPS].body = NULL;
2621 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2622 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2623 compose->followup_to =
2624 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2627 g_free(hentry[H_FOLLOWUP_TO].body);
2628 hentry[H_FOLLOWUP_TO].body = NULL;
2630 if (hentry[H_LIST_POST].body != NULL) {
2633 extract_address(hentry[H_LIST_POST].body);
2634 if (hentry[H_LIST_POST].body[0] != '\0') {
2635 scan_mailto_url(hentry[H_LIST_POST].body,
2636 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2638 g_free(compose->ml_post);
2639 compose->ml_post = to;
2642 g_free(hentry[H_LIST_POST].body);
2643 hentry[H_LIST_POST].body = NULL;
2646 /* CLAWS - X-Priority */
2647 if (compose->mode == COMPOSE_REEDIT)
2648 if (hentry[H_X_PRIORITY].body != NULL) {
2651 priority = atoi(hentry[H_X_PRIORITY].body);
2652 g_free(hentry[H_X_PRIORITY].body);
2654 hentry[H_X_PRIORITY].body = NULL;
2656 if (priority < PRIORITY_HIGHEST ||
2657 priority > PRIORITY_LOWEST)
2658 priority = PRIORITY_NORMAL;
2660 compose->priority = priority;
2663 if (compose->mode == COMPOSE_REEDIT) {
2664 if (msginfo->inreplyto && *msginfo->inreplyto)
2665 compose->inreplyto = g_strdup(msginfo->inreplyto);
2669 if (msginfo->msgid && *msginfo->msgid)
2670 compose->inreplyto = g_strdup(msginfo->msgid);
2672 if (!compose->references) {
2673 if (msginfo->msgid && *msginfo->msgid) {
2674 if (msginfo->inreplyto && *msginfo->inreplyto)
2675 compose->references =
2676 g_strdup_printf("<%s>\n\t<%s>",
2680 compose->references =
2681 g_strconcat("<", msginfo->msgid, ">",
2683 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2684 compose->references =
2685 g_strconcat("<", msginfo->inreplyto, ">",
2693 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2695 GSList *ref_id_list, *cur;
2699 ref_id_list = references_list_append(NULL, ref);
2700 if (!ref_id_list) return NULL;
2701 if (msgid && *msgid)
2702 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2707 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2708 /* "<" + Message-ID + ">" + CR+LF+TAB */
2709 len += strlen((gchar *)cur->data) + 5;
2711 if (len > MAX_REFERENCES_LEN) {
2712 /* remove second message-ID */
2713 if (ref_id_list && ref_id_list->next &&
2714 ref_id_list->next->next) {
2715 g_free(ref_id_list->next->data);
2716 ref_id_list = g_slist_remove
2717 (ref_id_list, ref_id_list->next->data);
2719 slist_free_strings(ref_id_list);
2720 g_slist_free(ref_id_list);
2727 new_ref = g_string_new("");
2728 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2729 if (new_ref->len > 0)
2730 g_string_append(new_ref, "\n\t");
2731 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2734 slist_free_strings(ref_id_list);
2735 g_slist_free(ref_id_list);
2737 new_ref_str = new_ref->str;
2738 g_string_free(new_ref, FALSE);
2743 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2744 const gchar *fmt, const gchar *qmark,
2745 const gchar *body, gboolean rewrap,
2746 gboolean need_unescape,
2747 const gchar *err_msg)
2749 MsgInfo* dummyinfo = NULL;
2750 gchar *quote_str = NULL;
2752 gboolean prev_autowrap;
2753 const gchar *trimmed_body = body;
2754 gint cursor_pos = -1;
2755 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2756 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2761 SIGNAL_BLOCK(buffer);
2764 dummyinfo = compose_msginfo_new_from_compose(compose);
2765 msginfo = dummyinfo;
2768 if (qmark != NULL) {
2770 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2771 compose->gtkaspell);
2773 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2775 quote_fmt_scan_string(qmark);
2778 buf = quote_fmt_get_buffer();
2780 alertpanel_error(_("Quote mark format error."));
2782 Xstrdup_a(quote_str, buf, goto error)
2785 if (fmt && *fmt != '\0') {
2788 while (*trimmed_body == '\n')
2792 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2793 compose->gtkaspell);
2795 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2797 if (need_unescape) {
2800 /* decode \-escape sequences in the internal representation of the quote format */
2801 tmp = malloc(strlen(fmt)+1);
2802 pref_get_unescaped_pref(tmp, fmt);
2803 quote_fmt_scan_string(tmp);
2807 quote_fmt_scan_string(fmt);
2811 buf = quote_fmt_get_buffer();
2813 gint line = quote_fmt_get_line();
2814 alertpanel_error(err_msg, line);
2820 prev_autowrap = compose->autowrap;
2821 compose->autowrap = FALSE;
2823 mark = gtk_text_buffer_get_insert(buffer);
2824 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2825 if (g_utf8_validate(buf, -1, NULL)) {
2826 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2828 gchar *tmpout = NULL;
2829 tmpout = conv_codeset_strdup
2830 (buf, conv_get_locale_charset_str_no_utf8(),
2832 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2834 tmpout = g_malloc(strlen(buf)*2+1);
2835 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2837 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2841 cursor_pos = quote_fmt_get_cursor_pos();
2842 if (cursor_pos == -1)
2843 cursor_pos = gtk_text_iter_get_offset(&iter);
2844 compose->set_cursor_pos = cursor_pos;
2846 gtk_text_buffer_get_start_iter(buffer, &iter);
2847 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2848 gtk_text_buffer_place_cursor(buffer, &iter);
2850 compose->autowrap = prev_autowrap;
2851 if (compose->autowrap && rewrap)
2852 compose_wrap_all(compose);
2859 SIGNAL_UNBLOCK(buffer);
2861 procmsg_msginfo_free( dummyinfo );
2866 /* if ml_post is of type addr@host and from is of type
2867 * addr-anything@host, return TRUE
2869 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2871 gchar *left_ml = NULL;
2872 gchar *right_ml = NULL;
2873 gchar *left_from = NULL;
2874 gchar *right_from = NULL;
2875 gboolean result = FALSE;
2877 if (!ml_post || !from)
2880 left_ml = g_strdup(ml_post);
2881 if (strstr(left_ml, "@")) {
2882 right_ml = strstr(left_ml, "@")+1;
2883 *(strstr(left_ml, "@")) = '\0';
2886 left_from = g_strdup(from);
2887 if (strstr(left_from, "@")) {
2888 right_from = strstr(left_from, "@")+1;
2889 *(strstr(left_from, "@")) = '\0';
2892 if (left_ml && left_from && right_ml && right_from
2893 && !strncmp(left_from, left_ml, strlen(left_ml))
2894 && !strcmp(right_from, right_ml)) {
2903 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2905 gchar *my_addr1, *my_addr2;
2907 if (!addr1 || !addr2)
2910 Xstrdup_a(my_addr1, addr1, return FALSE);
2911 Xstrdup_a(my_addr2, addr2, return FALSE);
2913 extract_address(my_addr1);
2914 extract_address(my_addr2);
2916 return !strcasecmp(my_addr1, my_addr2);
2919 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2920 gboolean to_all, gboolean to_ml,
2922 gboolean followup_and_reply_to)
2924 GSList *cc_list = NULL;
2927 gchar *replyto = NULL;
2928 GHashTable *to_table;
2930 gboolean reply_to_ml = FALSE;
2931 gboolean default_reply_to = FALSE;
2933 g_return_if_fail(compose->account != NULL);
2934 g_return_if_fail(msginfo != NULL);
2936 reply_to_ml = to_ml && compose->ml_post;
2938 default_reply_to = msginfo->folder &&
2939 msginfo->folder->prefs->enable_default_reply_to;
2941 if (compose->account->protocol != A_NNTP) {
2942 if (reply_to_ml && !default_reply_to) {
2944 gboolean is_subscr = is_subscription(compose->ml_post,
2947 /* normal answer to ml post with a reply-to */
2948 compose_entry_append(compose,
2951 if (compose->replyto
2952 && !same_address(compose->ml_post, compose->replyto))
2953 compose_entry_append(compose,
2957 /* answer to subscription confirmation */
2958 if (compose->replyto)
2959 compose_entry_append(compose,
2962 else if (msginfo->from)
2963 compose_entry_append(compose,
2968 else if (!(to_all || to_sender) && default_reply_to) {
2969 compose_entry_append(compose,
2970 msginfo->folder->prefs->default_reply_to,
2972 compose_entry_mark_default_to(compose,
2973 msginfo->folder->prefs->default_reply_to);
2978 Xstrdup_a(tmp1, msginfo->from, return);
2979 extract_address(tmp1);
2980 if (to_all || to_sender ||
2981 !account_find_from_address(tmp1, FALSE))
2982 compose_entry_append(compose,
2983 (compose->replyto && !to_sender)
2984 ? compose->replyto :
2985 msginfo->from ? msginfo->from : "",
2987 else if (!to_all && !to_sender) {
2988 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2989 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
2990 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2991 if (compose->replyto) {
2992 compose_entry_append(compose,
2996 compose_entry_append(compose,
2997 msginfo->from ? msginfo->from : "",
3001 /* replying to own mail, use original recp */
3002 compose_entry_append(compose,
3003 msginfo->to ? msginfo->to : "",
3005 compose_entry_append(compose,
3006 msginfo->cc ? msginfo->cc : "",
3012 if (to_sender || (compose->followup_to &&
3013 !strncmp(compose->followup_to, "poster", 6)))
3014 compose_entry_append
3016 (compose->replyto ? compose->replyto :
3017 msginfo->from ? msginfo->from : ""),
3020 else if (followup_and_reply_to || to_all) {
3021 compose_entry_append
3023 (compose->replyto ? compose->replyto :
3024 msginfo->from ? msginfo->from : ""),
3027 compose_entry_append
3029 compose->followup_to ? compose->followup_to :
3030 compose->newsgroups ? compose->newsgroups : "",
3031 COMPOSE_NEWSGROUPS);
3034 compose_entry_append
3036 compose->followup_to ? compose->followup_to :
3037 compose->newsgroups ? compose->newsgroups : "",
3038 COMPOSE_NEWSGROUPS);
3041 if (msginfo->subject && *msginfo->subject) {
3045 buf = p = g_strdup(msginfo->subject);
3046 p += subject_get_prefix_length(p);
3047 memmove(buf, p, strlen(p) + 1);
3049 buf2 = g_strdup_printf("Re: %s", buf);
3050 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3055 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3057 if (to_ml && compose->ml_post) return;
3058 if (!to_all || compose->account->protocol == A_NNTP) return;
3060 if (compose->replyto) {
3061 Xstrdup_a(replyto, compose->replyto, return);
3062 extract_address(replyto);
3064 if (msginfo->from) {
3065 Xstrdup_a(from, msginfo->from, return);
3066 extract_address(from);
3069 if (replyto && from)
3070 cc_list = address_list_append_with_comments(cc_list, from);
3071 if (to_all && msginfo->folder &&
3072 msginfo->folder->prefs->enable_default_reply_to)
3073 cc_list = address_list_append_with_comments(cc_list,
3074 msginfo->folder->prefs->default_reply_to);
3075 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3076 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3078 to_table = g_hash_table_new(g_str_hash, g_str_equal);
3080 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
3081 if (compose->account) {
3082 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
3083 GINT_TO_POINTER(1));
3085 /* remove address on To: and that of current account */
3086 for (cur = cc_list; cur != NULL; ) {
3087 GSList *next = cur->next;
3090 addr = g_utf8_strdown(cur->data, -1);
3091 extract_address(addr);
3093 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3094 cc_list = g_slist_remove(cc_list, cur->data);
3096 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3100 hash_free_strings(to_table);
3101 g_hash_table_destroy(to_table);
3104 for (cur = cc_list; cur != NULL; cur = cur->next)
3105 compose_entry_append(compose, (gchar *)cur->data,
3107 slist_free_strings(cc_list);
3108 g_slist_free(cc_list);
3113 #define SET_ENTRY(entry, str) \
3116 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3119 #define SET_ADDRESS(type, str) \
3122 compose_entry_append(compose, str, type); \
3125 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3127 g_return_if_fail(msginfo != NULL);
3129 SET_ENTRY(subject_entry, msginfo->subject);
3130 SET_ENTRY(from_name, msginfo->from);
3131 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3132 SET_ADDRESS(COMPOSE_CC, compose->cc);
3133 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3134 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3135 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3136 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3138 compose_update_priority_menu_item(compose);
3139 compose_update_privacy_system_menu_item(compose, FALSE);
3140 compose_show_first_last_header(compose, TRUE);
3146 static void compose_insert_sig(Compose *compose, gboolean replace)
3148 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3149 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3151 GtkTextIter iter, iter_end;
3152 gint cur_pos, ins_pos;
3153 gboolean prev_autowrap;
3154 gboolean found = FALSE;
3155 gboolean exists = FALSE;
3157 g_return_if_fail(compose->account != NULL);
3161 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3162 G_CALLBACK(compose_changed_cb),
3165 mark = gtk_text_buffer_get_insert(buffer);
3166 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3167 cur_pos = gtk_text_iter_get_offset (&iter);
3170 gtk_text_buffer_get_end_iter(buffer, &iter);
3172 exists = (compose->sig_str != NULL);
3175 GtkTextIter first_iter, start_iter, end_iter;
3177 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3179 if (!exists || compose->sig_str[0] == '\0')
3182 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3183 compose->signature_tag);
3186 /* include previous \n\n */
3187 gtk_text_iter_backward_chars(&first_iter, 1);
3188 start_iter = first_iter;
3189 end_iter = first_iter;
3191 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3192 compose->signature_tag);
3193 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3194 compose->signature_tag);
3196 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3202 g_free(compose->sig_str);
3203 compose->sig_str = compose_get_signature_str(compose);
3205 cur_pos = gtk_text_iter_get_offset(&iter);
3207 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3208 g_free(compose->sig_str);
3209 compose->sig_str = NULL;
3211 if (compose->sig_inserted == FALSE)
3212 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3213 compose->sig_inserted = TRUE;
3215 cur_pos = gtk_text_iter_get_offset(&iter);
3216 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3218 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3219 gtk_text_iter_forward_chars(&iter, 1);
3220 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3221 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3223 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3224 cur_pos = gtk_text_buffer_get_char_count (buffer);
3227 /* put the cursor where it should be
3228 * either where the quote_fmt says, either where it was */
3229 if (compose->set_cursor_pos < 0)
3230 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3232 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3233 compose->set_cursor_pos);
3235 gtk_text_buffer_place_cursor(buffer, &iter);
3236 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3237 G_CALLBACK(compose_changed_cb),
3243 static gchar *compose_get_signature_str(Compose *compose)
3245 gchar *sig_body = NULL;
3246 gchar *sig_str = NULL;
3247 gchar *utf8_sig_str = NULL;
3249 g_return_val_if_fail(compose->account != NULL, NULL);
3251 if (!compose->account->sig_path)
3254 if (compose->account->sig_type == SIG_FILE) {
3255 if (!is_file_or_fifo_exist(compose->account->sig_path)) {
3256 g_warning("can't open signature file: %s\n",
3257 compose->account->sig_path);
3262 if (compose->account->sig_type == SIG_COMMAND)
3263 sig_body = get_command_output(compose->account->sig_path);
3267 tmp = file_read_to_str(compose->account->sig_path);
3270 sig_body = normalize_newlines(tmp);
3274 if (compose->account->sig_sep) {
3275 sig_str = g_strconcat("\n", compose->account->sig_sep, "\n", sig_body,
3279 sig_str = g_strconcat("\n", sig_body, NULL);
3282 if (g_utf8_validate(sig_str, -1, NULL) == TRUE)
3283 utf8_sig_str = sig_str;
3285 utf8_sig_str = conv_codeset_strdup
3286 (sig_str, conv_get_locale_charset_str_no_utf8(),
3292 return utf8_sig_str;
3295 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3298 GtkTextBuffer *buffer;
3301 const gchar *cur_encoding;
3302 gchar buf[BUFFSIZE];
3305 gboolean prev_autowrap;
3306 gboolean badtxt = FALSE;
3308 g_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3310 if ((fp = g_fopen(file, "rb")) == NULL) {
3311 FILE_OP_ERROR(file, "fopen");
3312 return COMPOSE_INSERT_READ_ERROR;
3315 prev_autowrap = compose->autowrap;
3316 compose->autowrap = FALSE;
3318 text = GTK_TEXT_VIEW(compose->text);
3319 buffer = gtk_text_view_get_buffer(text);
3320 mark = gtk_text_buffer_get_insert(buffer);
3321 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3323 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3324 G_CALLBACK(text_inserted),
3327 cur_encoding = conv_get_locale_charset_str_no_utf8();
3329 while (fgets(buf, sizeof(buf), fp) != NULL) {
3332 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3333 str = g_strdup(buf);
3335 str = conv_codeset_strdup
3336 (buf, cur_encoding, CS_INTERNAL);
3339 /* strip <CR> if DOS/Windows file,
3340 replace <CR> with <LF> if Macintosh file. */
3343 if (len > 0 && str[len - 1] != '\n') {
3345 if (str[len] == '\r') str[len] = '\n';
3348 gtk_text_buffer_insert(buffer, &iter, str, -1);
3352 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3353 G_CALLBACK(text_inserted),
3355 compose->autowrap = prev_autowrap;
3356 if (compose->autowrap)
3357 compose_wrap_all(compose);
3362 return COMPOSE_INSERT_INVALID_CHARACTER;
3364 return COMPOSE_INSERT_SUCCESS;
3367 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3368 const gchar *filename,
3369 const gchar *content_type)
3377 GtkListStore *store;
3379 gboolean has_binary = FALSE;
3381 if (!is_file_exist(file)) {
3382 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3383 gboolean result = FALSE;
3384 if (file_from_uri && is_file_exist(file_from_uri)) {
3385 result = compose_attach_append(
3386 compose, file_from_uri,
3390 g_free(file_from_uri);
3393 alertpanel_error("File %s doesn't exist\n", filename);
3396 if ((size = get_file_size(file)) < 0) {
3397 alertpanel_error("Can't get file size of %s\n", filename);
3401 alertpanel_error(_("File %s is empty."), filename);
3404 if ((fp = g_fopen(file, "rb")) == NULL) {
3405 alertpanel_error(_("Can't read %s."), filename);
3410 ainfo = g_new0(AttachInfo, 1);
3411 auto_ainfo = g_auto_pointer_new_with_free
3412 (ainfo, (GFreeFunc) compose_attach_info_free);
3413 ainfo->file = g_strdup(file);
3416 ainfo->content_type = g_strdup(content_type);
3417 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3419 MsgFlags flags = {0, 0};
3421 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3422 ainfo->encoding = ENC_7BIT;
3424 ainfo->encoding = ENC_8BIT;
3426 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3427 if (msginfo && msginfo->subject)
3428 name = g_strdup(msginfo->subject);
3430 name = g_path_get_basename(filename ? filename : file);
3432 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3434 procmsg_msginfo_free(msginfo);
3436 if (!g_ascii_strncasecmp(content_type, "text", 4))
3437 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3439 ainfo->encoding = ENC_BASE64;
3440 name = g_path_get_basename(filename ? filename : file);
3441 ainfo->name = g_strdup(name);
3445 ainfo->content_type = procmime_get_mime_type(file);
3446 if (!ainfo->content_type) {
3447 ainfo->content_type =
3448 g_strdup("application/octet-stream");
3449 ainfo->encoding = ENC_BASE64;
3450 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3452 procmime_get_encoding_for_text_file(file, &has_binary);
3454 ainfo->encoding = ENC_BASE64;
3455 name = g_path_get_basename(filename ? filename : file);
3456 ainfo->name = g_strdup(name);
3460 if (ainfo->name != NULL
3461 && !strcmp(ainfo->name, ".")) {
3462 g_free(ainfo->name);
3466 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3467 g_free(ainfo->content_type);
3468 ainfo->content_type = g_strdup("application/octet-stream");
3471 ainfo->size = (goffset)size;
3472 size_text = to_human_readable((goffset)size);
3474 store = GTK_LIST_STORE(gtk_tree_view_get_model
3475 (GTK_TREE_VIEW(compose->attach_clist)));
3477 gtk_list_store_append(store, &iter);
3478 gtk_list_store_set(store, &iter,
3479 COL_MIMETYPE, ainfo->content_type,
3480 COL_SIZE, size_text,
3481 COL_NAME, ainfo->name,
3483 COL_AUTODATA, auto_ainfo,
3486 g_auto_pointer_free(auto_ainfo);
3487 compose_attach_update_label(compose);
3491 static void compose_use_signing(Compose *compose, gboolean use_signing)
3493 compose->use_signing = use_signing;
3494 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3497 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3499 compose->use_encryption = use_encryption;
3500 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3503 #define NEXT_PART_NOT_CHILD(info) \
3505 node = info->node; \
3506 while (node->children) \
3507 node = g_node_last_child(node); \
3508 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3511 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3515 MimeInfo *firsttext = NULL;
3516 MimeInfo *encrypted = NULL;
3519 const gchar *partname = NULL;
3521 mimeinfo = procmime_scan_message(msginfo);
3522 if (!mimeinfo) return;
3524 if (mimeinfo->node->children == NULL) {
3525 procmime_mimeinfo_free_all(mimeinfo);
3529 /* find first content part */
3530 child = (MimeInfo *) mimeinfo->node->children->data;
3531 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3532 child = (MimeInfo *)child->node->children->data;
3534 if (child->type == MIMETYPE_TEXT) {
3536 debug_print("First text part found\n");
3537 } else if (compose->mode == COMPOSE_REEDIT &&
3538 child->type == MIMETYPE_APPLICATION &&
3539 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3540 encrypted = (MimeInfo *)child->node->parent->data;
3543 child = (MimeInfo *) mimeinfo->node->children->data;
3544 while (child != NULL) {
3547 if (child == encrypted) {
3548 /* skip this part of tree */
3549 NEXT_PART_NOT_CHILD(child);
3553 if (child->type == MIMETYPE_MULTIPART) {
3554 /* get the actual content */
3555 child = procmime_mimeinfo_next(child);
3559 if (child == firsttext) {
3560 child = procmime_mimeinfo_next(child);
3564 outfile = procmime_get_tmp_file_name(child);
3565 if ((err = procmime_get_part(outfile, child)) < 0)
3566 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3568 gchar *content_type;
3570 content_type = procmime_get_content_type_str(child->type, child->subtype);
3572 /* if we meet a pgp signature, we don't attach it, but
3573 * we force signing. */
3574 if ((strcmp(content_type, "application/pgp-signature") &&
3575 strcmp(content_type, "application/pkcs7-signature") &&
3576 strcmp(content_type, "application/x-pkcs7-signature"))
3577 || compose->mode == COMPOSE_REDIRECT) {
3578 partname = procmime_mimeinfo_get_parameter(child, "filename");
3579 if (partname == NULL)
3580 partname = procmime_mimeinfo_get_parameter(child, "name");
3581 if (partname == NULL)
3583 compose_attach_append(compose, outfile,
3584 partname, content_type);
3586 compose_force_signing(compose, compose->account);
3588 g_free(content_type);
3591 NEXT_PART_NOT_CHILD(child);
3593 procmime_mimeinfo_free_all(mimeinfo);
3596 #undef NEXT_PART_NOT_CHILD
3601 WAIT_FOR_INDENT_CHAR,
3602 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3605 /* return indent length, we allow:
3606 indent characters followed by indent characters or spaces/tabs,
3607 alphabets and numbers immediately followed by indent characters,
3608 and the repeating sequences of the above
3609 If quote ends with multiple spaces, only the first one is included. */
3610 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3611 const GtkTextIter *start, gint *len)
3613 GtkTextIter iter = *start;
3617 IndentState state = WAIT_FOR_INDENT_CHAR;
3620 gint alnum_count = 0;
3621 gint space_count = 0;
3624 if (prefs_common.quote_chars == NULL) {
3628 while (!gtk_text_iter_ends_line(&iter)) {
3629 wc = gtk_text_iter_get_char(&iter);
3630 if (g_unichar_iswide(wc))
3632 clen = g_unichar_to_utf8(wc, ch);
3636 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3637 is_space = g_unichar_isspace(wc);
3639 if (state == WAIT_FOR_INDENT_CHAR) {
3640 if (!is_indent && !g_unichar_isalnum(wc))
3643 quote_len += alnum_count + space_count + 1;
3644 alnum_count = space_count = 0;
3645 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3648 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3649 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3653 else if (is_indent) {
3654 quote_len += alnum_count + space_count + 1;
3655 alnum_count = space_count = 0;
3658 state = WAIT_FOR_INDENT_CHAR;
3662 gtk_text_iter_forward_char(&iter);
3665 if (quote_len > 0 && space_count > 0)
3671 if (quote_len > 0) {
3673 gtk_text_iter_forward_chars(&iter, quote_len);
3674 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3680 /* return >0 if the line is itemized */
3681 static int compose_itemized_length(GtkTextBuffer *buffer,
3682 const GtkTextIter *start)
3684 GtkTextIter iter = *start;
3689 if (gtk_text_iter_ends_line(&iter))
3694 wc = gtk_text_iter_get_char(&iter);
3695 if (!g_unichar_isspace(wc))
3697 gtk_text_iter_forward_char(&iter);
3698 if (gtk_text_iter_ends_line(&iter))
3702 clen = g_unichar_to_utf8(wc, ch);
3706 if (!strchr("*-+", ch[0]))
3709 gtk_text_iter_forward_char(&iter);
3710 if (gtk_text_iter_ends_line(&iter))
3712 wc = gtk_text_iter_get_char(&iter);
3713 if (g_unichar_isspace(wc)) {
3719 /* return the string at the start of the itemization */
3720 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3721 const GtkTextIter *start)
3723 GtkTextIter iter = *start;
3726 GString *item_chars = g_string_new("");
3729 if (gtk_text_iter_ends_line(&iter))
3734 wc = gtk_text_iter_get_char(&iter);
3735 if (!g_unichar_isspace(wc))
3737 gtk_text_iter_forward_char(&iter);
3738 if (gtk_text_iter_ends_line(&iter))
3740 g_string_append_unichar(item_chars, wc);
3743 str = item_chars->str;
3744 g_string_free(item_chars, FALSE);
3748 /* return the number of spaces at a line's start */
3749 static int compose_left_offset_length(GtkTextBuffer *buffer,
3750 const GtkTextIter *start)
3752 GtkTextIter iter = *start;
3755 if (gtk_text_iter_ends_line(&iter))
3759 wc = gtk_text_iter_get_char(&iter);
3760 if (!g_unichar_isspace(wc))
3763 gtk_text_iter_forward_char(&iter);
3764 if (gtk_text_iter_ends_line(&iter))
3768 gtk_text_iter_forward_char(&iter);
3769 if (gtk_text_iter_ends_line(&iter))
3774 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3775 const GtkTextIter *start,
3776 GtkTextIter *break_pos,
3780 GtkTextIter iter = *start, line_end = *start;
3781 PangoLogAttr *attrs;
3788 gboolean can_break = FALSE;
3789 gboolean do_break = FALSE;
3790 gboolean was_white = FALSE;
3791 gboolean prev_dont_break = FALSE;
3793 gtk_text_iter_forward_to_line_end(&line_end);
3794 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3795 len = g_utf8_strlen(str, -1);
3799 g_warning("compose_get_line_break_pos: len = 0!\n");
3803 /* g_print("breaking line: %d: %s (len = %d)\n",
3804 gtk_text_iter_get_line(&iter), str, len); */
3806 attrs = g_new(PangoLogAttr, len + 1);
3808 pango_default_break(str, -1, NULL, attrs, len + 1);
3812 /* skip quote and leading spaces */
3813 for (i = 0; *p != '\0' && i < len; i++) {
3816 wc = g_utf8_get_char(p);
3817 if (i >= quote_len && !g_unichar_isspace(wc))
3819 if (g_unichar_iswide(wc))
3821 else if (*p == '\t')
3825 p = g_utf8_next_char(p);
3828 for (; *p != '\0' && i < len; i++) {
3829 PangoLogAttr *attr = attrs + i;
3833 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3836 was_white = attr->is_white;
3838 /* don't wrap URI */
3839 if ((uri_len = get_uri_len(p)) > 0) {
3841 if (pos > 0 && col > max_col) {
3851 wc = g_utf8_get_char(p);
3852 if (g_unichar_iswide(wc)) {
3854 if (prev_dont_break && can_break && attr->is_line_break)
3856 } else if (*p == '\t')
3860 if (pos > 0 && col > max_col) {
3865 if (*p == '-' || *p == '/')
3866 prev_dont_break = TRUE;
3868 prev_dont_break = FALSE;
3870 p = g_utf8_next_char(p);
3874 debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3879 *break_pos = *start;
3880 gtk_text_iter_set_line_offset(break_pos, pos);
3885 static gboolean compose_join_next_line(Compose *compose,
3886 GtkTextBuffer *buffer,
3888 const gchar *quote_str)
3890 GtkTextIter iter_ = *iter, cur, prev, next, end;
3891 PangoLogAttr attrs[3];
3893 gchar *next_quote_str;
3896 gboolean keep_cursor = FALSE;
3898 if (!gtk_text_iter_forward_line(&iter_) ||
3899 gtk_text_iter_ends_line(&iter_)) {
3902 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
3904 if ((quote_str || next_quote_str) &&
3905 strcmp2(quote_str, next_quote_str) != 0) {
3906 g_free(next_quote_str);
3909 g_free(next_quote_str);
3912 if (quote_len > 0) {
3913 gtk_text_iter_forward_chars(&end, quote_len);
3914 if (gtk_text_iter_ends_line(&end)) {
3919 /* don't join itemized lines */
3920 if (compose_itemized_length(buffer, &end) > 0) {
3924 /* don't join signature separator */
3925 if (compose_is_sig_separator(compose, buffer, &iter_)) {
3928 /* delete quote str */
3930 gtk_text_buffer_delete(buffer, &iter_, &end);
3932 /* don't join line breaks put by the user */
3934 gtk_text_iter_backward_char(&cur);
3935 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
3936 gtk_text_iter_forward_char(&cur);
3940 gtk_text_iter_forward_char(&cur);
3941 /* delete linebreak and extra spaces */
3942 while (gtk_text_iter_backward_char(&cur)) {
3943 wc1 = gtk_text_iter_get_char(&cur);
3944 if (!g_unichar_isspace(wc1))
3949 while (!gtk_text_iter_ends_line(&cur)) {
3950 wc1 = gtk_text_iter_get_char(&cur);
3951 if (!g_unichar_isspace(wc1))
3953 gtk_text_iter_forward_char(&cur);
3956 if (!gtk_text_iter_equal(&prev, &next)) {
3959 mark = gtk_text_buffer_get_insert(buffer);
3960 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
3961 if (gtk_text_iter_equal(&prev, &cur))
3963 gtk_text_buffer_delete(buffer, &prev, &next);
3967 /* insert space if required */
3968 gtk_text_iter_backward_char(&prev);
3969 wc1 = gtk_text_iter_get_char(&prev);
3970 wc2 = gtk_text_iter_get_char(&next);
3971 gtk_text_iter_forward_char(&next);
3972 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
3973 pango_default_break(str, -1, NULL, attrs, 3);
3974 if (!attrs[1].is_line_break ||
3975 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
3976 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
3978 gtk_text_iter_backward_char(&iter_);
3979 gtk_text_buffer_place_cursor(buffer, &iter_);
3988 #define ADD_TXT_POS(bp_, ep_, pti_) \
3989 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
3990 last = last->next; \
3991 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
3992 last->next = NULL; \
3994 g_warning("alloc error scanning URIs\n"); \
3997 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
3999 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4000 GtkTextBuffer *buffer;
4001 GtkTextIter iter, break_pos, end_of_line;
4002 gchar *quote_str = NULL;
4004 gboolean wrap_quote = prefs_common.linewrap_quote;
4005 gboolean prev_autowrap = compose->autowrap;
4006 gint startq_offset = -1, noq_offset = -1;
4007 gint uri_start = -1, uri_stop = -1;
4008 gint nouri_start = -1, nouri_stop = -1;
4009 gint num_blocks = 0;
4010 gint quotelevel = -1;
4011 gboolean modified = force;
4012 gboolean removed = FALSE;
4013 gboolean modified_before_remove = FALSE;
4015 gboolean start = TRUE;
4016 gint itemized_len = 0, rem_item_len = 0;
4017 gchar *itemized_chars = NULL;
4018 gboolean item_continuation = FALSE;
4023 if (compose->draft_timeout_tag == -2) {
4027 compose->autowrap = FALSE;
4029 buffer = gtk_text_view_get_buffer(text);
4030 undo_wrapping(compose->undostruct, TRUE);
4035 mark = gtk_text_buffer_get_insert(buffer);
4036 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4040 if (compose->draft_timeout_tag == -2) {
4041 if (gtk_text_iter_ends_line(&iter)) {
4042 while (gtk_text_iter_ends_line(&iter) &&
4043 gtk_text_iter_forward_line(&iter))
4046 while (gtk_text_iter_backward_line(&iter)) {
4047 if (gtk_text_iter_ends_line(&iter)) {
4048 gtk_text_iter_forward_line(&iter);
4054 /* move to line start */
4055 gtk_text_iter_set_line_offset(&iter, 0);
4058 itemized_len = compose_itemized_length(buffer, &iter);
4060 if (!itemized_len) {
4061 itemized_len = compose_left_offset_length(buffer, &iter);
4062 item_continuation = TRUE;
4066 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4068 /* go until paragraph end (empty line) */
4069 while (start || !gtk_text_iter_ends_line(&iter)) {
4070 gchar *scanpos = NULL;
4071 /* parse table - in order of priority */
4073 const gchar *needle; /* token */
4075 /* token search function */
4076 gchar *(*search) (const gchar *haystack,
4077 const gchar *needle);
4078 /* part parsing function */
4079 gboolean (*parse) (const gchar *start,
4080 const gchar *scanpos,
4084 /* part to URI function */
4085 gchar *(*build_uri) (const gchar *bp,
4089 static struct table parser[] = {
4090 {"http://", strcasestr, get_uri_part, make_uri_string},
4091 {"https://", strcasestr, get_uri_part, make_uri_string},
4092 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4093 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4094 {"www.", strcasestr, get_uri_part, make_http_string},
4095 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4096 {"@", strcasestr, get_email_part, make_email_string}
4098 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4099 gint last_index = PARSE_ELEMS;
4101 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4105 if (!prev_autowrap && num_blocks == 0) {
4107 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4108 G_CALLBACK(text_inserted),
4111 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4114 uri_start = uri_stop = -1;
4116 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4119 debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4120 if (startq_offset == -1)
4121 startq_offset = gtk_text_iter_get_offset(&iter);
4122 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4123 if (quotelevel > 2) {
4124 /* recycle colors */
4125 if (prefs_common.recycle_quote_colors)
4134 if (startq_offset == -1)
4135 noq_offset = gtk_text_iter_get_offset(&iter);
4139 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4142 if (gtk_text_iter_ends_line(&iter)) {
4144 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4145 prefs_common.linewrap_len,
4147 GtkTextIter prev, next, cur;
4148 if (prev_autowrap != FALSE || force) {
4149 compose->automatic_break = TRUE;
4151 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4152 compose->automatic_break = FALSE;
4153 if (itemized_len && compose->autoindent) {
4154 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4155 if (!item_continuation)
4156 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4158 } else if (quote_str && wrap_quote) {
4159 compose->automatic_break = TRUE;
4161 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4162 compose->automatic_break = FALSE;
4163 if (itemized_len && compose->autoindent) {
4164 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4165 if (!item_continuation)
4166 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4170 /* remove trailing spaces */
4172 rem_item_len = itemized_len;
4173 while (compose->autoindent && rem_item_len-- > 0)
4174 gtk_text_iter_backward_char(&cur);
4175 gtk_text_iter_backward_char(&cur);
4178 while (!gtk_text_iter_starts_line(&cur)) {
4181 gtk_text_iter_backward_char(&cur);
4182 wc = gtk_text_iter_get_char(&cur);
4183 if (!g_unichar_isspace(wc))
4187 if (!gtk_text_iter_equal(&prev, &next)) {
4188 gtk_text_buffer_delete(buffer, &prev, &next);
4190 gtk_text_iter_forward_char(&break_pos);
4194 gtk_text_buffer_insert(buffer, &break_pos,
4198 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4200 /* move iter to current line start */
4201 gtk_text_iter_set_line_offset(&iter, 0);
4208 /* move iter to next line start */
4214 if (!prev_autowrap && num_blocks > 0) {
4216 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4217 G_CALLBACK(text_inserted),
4221 while (!gtk_text_iter_ends_line(&end_of_line)) {
4222 gtk_text_iter_forward_char(&end_of_line);
4224 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4226 nouri_start = gtk_text_iter_get_offset(&iter);
4227 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4229 walk_pos = gtk_text_iter_get_offset(&iter);
4230 /* FIXME: this looks phony. scanning for anything in the parse table */
4231 for (n = 0; n < PARSE_ELEMS; n++) {
4234 tmp = parser[n].search(walk, parser[n].needle);
4236 if (scanpos == NULL || tmp < scanpos) {
4245 /* check if URI can be parsed */
4246 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4247 (const gchar **)&ep, FALSE)
4248 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4252 strlen(parser[last_index].needle);
4255 uri_start = walk_pos + (bp - o_walk);
4256 uri_stop = walk_pos + (ep - o_walk);
4260 gtk_text_iter_forward_line(&iter);
4263 if (startq_offset != -1) {
4264 GtkTextIter startquote, endquote;
4265 gtk_text_buffer_get_iter_at_offset(
4266 buffer, &startquote, startq_offset);
4269 switch (quotelevel) {
4271 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4272 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4273 gtk_text_buffer_apply_tag_by_name(
4274 buffer, "quote0", &startquote, &endquote);
4275 gtk_text_buffer_remove_tag_by_name(
4276 buffer, "quote1", &startquote, &endquote);
4277 gtk_text_buffer_remove_tag_by_name(
4278 buffer, "quote2", &startquote, &endquote);
4283 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4284 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4285 gtk_text_buffer_apply_tag_by_name(
4286 buffer, "quote1", &startquote, &endquote);
4287 gtk_text_buffer_remove_tag_by_name(
4288 buffer, "quote0", &startquote, &endquote);
4289 gtk_text_buffer_remove_tag_by_name(
4290 buffer, "quote2", &startquote, &endquote);
4295 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4296 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4297 gtk_text_buffer_apply_tag_by_name(
4298 buffer, "quote2", &startquote, &endquote);
4299 gtk_text_buffer_remove_tag_by_name(
4300 buffer, "quote0", &startquote, &endquote);
4301 gtk_text_buffer_remove_tag_by_name(
4302 buffer, "quote1", &startquote, &endquote);
4308 } else if (noq_offset != -1) {
4309 GtkTextIter startnoquote, endnoquote;
4310 gtk_text_buffer_get_iter_at_offset(
4311 buffer, &startnoquote, noq_offset);
4314 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4315 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4316 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4317 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4318 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4319 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4320 gtk_text_buffer_remove_tag_by_name(
4321 buffer, "quote0", &startnoquote, &endnoquote);
4322 gtk_text_buffer_remove_tag_by_name(
4323 buffer, "quote1", &startnoquote, &endnoquote);
4324 gtk_text_buffer_remove_tag_by_name(
4325 buffer, "quote2", &startnoquote, &endnoquote);
4331 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4332 GtkTextIter nouri_start_iter, nouri_end_iter;
4333 gtk_text_buffer_get_iter_at_offset(
4334 buffer, &nouri_start_iter, nouri_start);
4335 gtk_text_buffer_get_iter_at_offset(
4336 buffer, &nouri_end_iter, nouri_stop);
4337 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4338 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4339 gtk_text_buffer_remove_tag_by_name(
4340 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4341 modified_before_remove = modified;
4346 if (uri_start >= 0 && uri_stop > 0) {
4347 GtkTextIter uri_start_iter, uri_end_iter, back;
4348 gtk_text_buffer_get_iter_at_offset(
4349 buffer, &uri_start_iter, uri_start);
4350 gtk_text_buffer_get_iter_at_offset(
4351 buffer, &uri_end_iter, uri_stop);
4352 back = uri_end_iter;
4353 gtk_text_iter_backward_char(&back);
4354 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4355 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4356 gtk_text_buffer_apply_tag_by_name(
4357 buffer, "link", &uri_start_iter, &uri_end_iter);
4359 if (removed && !modified_before_remove) {
4365 debug_print("not modified, out after %d lines\n", lines);
4369 debug_print("modified, out after %d lines\n", lines);
4371 g_free(itemized_chars);
4374 undo_wrapping(compose->undostruct, FALSE);
4375 compose->autowrap = prev_autowrap;
4380 void compose_action_cb(void *data)
4382 Compose *compose = (Compose *)data;
4383 compose_wrap_all(compose);
4386 static void compose_wrap_all(Compose *compose)
4388 compose_wrap_all_full(compose, FALSE);
4391 static void compose_wrap_all_full(Compose *compose, gboolean force)
4393 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4394 GtkTextBuffer *buffer;
4396 gboolean modified = TRUE;
4398 buffer = gtk_text_view_get_buffer(text);
4400 gtk_text_buffer_get_start_iter(buffer, &iter);
4401 while (!gtk_text_iter_is_end(&iter) && modified)
4402 modified = compose_beautify_paragraph(compose, &iter, force);
4406 static void compose_set_title(Compose *compose)
4412 edited = compose->modified ? _(" [Edited]") : "";
4414 subject = gtk_editable_get_chars(
4415 GTK_EDITABLE(compose->subject_entry), 0, -1);
4417 #ifndef GENERIC_UMPC
4418 if (subject && strlen(subject))
4419 str = g_strdup_printf(_("%s - Compose message%s"),
4422 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4424 str = g_strdup(_("Compose message"));
4427 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4433 * compose_current_mail_account:
4435 * Find a current mail account (the currently selected account, or the
4436 * default account, if a news account is currently selected). If a
4437 * mail account cannot be found, display an error message.
4439 * Return value: Mail account, or NULL if not found.
4441 static PrefsAccount *
4442 compose_current_mail_account(void)
4446 if (cur_account && cur_account->protocol != A_NNTP)
4449 ac = account_get_default();
4450 if (!ac || ac->protocol == A_NNTP) {
4451 alertpanel_error(_("Account for sending mail is not specified.\n"
4452 "Please select a mail account before sending."));
4459 #define QUOTE_IF_REQUIRED(out, str) \
4461 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4465 len = strlen(str) + 3; \
4466 if ((__tmp = alloca(len)) == NULL) { \
4467 g_warning("can't allocate memory\n"); \
4468 g_string_free(header, TRUE); \
4471 g_snprintf(__tmp, len, "\"%s\"", str); \
4476 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4477 g_warning("can't allocate memory\n"); \
4478 g_string_free(header, TRUE); \
4481 strcpy(__tmp, str); \
4487 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4489 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4493 len = strlen(str) + 3; \
4494 if ((__tmp = alloca(len)) == NULL) { \
4495 g_warning("can't allocate memory\n"); \
4498 g_snprintf(__tmp, len, "\"%s\"", str); \
4503 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4504 g_warning("can't allocate memory\n"); \
4507 strcpy(__tmp, str); \
4513 static void compose_select_account(Compose *compose, PrefsAccount *account,
4518 g_return_if_fail(account != NULL);
4520 compose->account = account;
4522 if (account->name && *account->name) {
4524 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4525 from = g_strdup_printf("%s <%s>",
4526 buf, account->address);
4527 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4529 from = g_strdup_printf("<%s>",
4531 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4536 compose_set_title(compose);
4538 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4539 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4541 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4542 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4543 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4545 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4547 activate_privacy_system(compose, account, FALSE);
4549 if (!init && compose->mode != COMPOSE_REDIRECT) {
4550 undo_block(compose->undostruct);
4551 compose_insert_sig(compose, TRUE);
4552 undo_unblock(compose->undostruct);
4556 /* use account's dict info if set */
4557 if (compose->gtkaspell) {
4558 if (account->enable_default_dictionary)
4559 gtkaspell_change_dict(compose->gtkaspell,
4560 account->default_dictionary, FALSE);
4561 if (account->enable_default_alt_dictionary)
4562 gtkaspell_change_alt_dict(compose->gtkaspell,
4563 account->default_alt_dictionary);
4564 if (account->enable_default_dictionary
4565 || account->enable_default_alt_dictionary)
4566 compose_spell_menu_changed(compose);
4571 gboolean compose_check_for_valid_recipient(Compose *compose) {
4572 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4573 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4574 gboolean recipient_found = FALSE;
4578 /* free to and newsgroup list */
4579 slist_free_strings(compose->to_list);
4580 g_slist_free(compose->to_list);
4581 compose->to_list = NULL;
4583 slist_free_strings(compose->newsgroup_list);
4584 g_slist_free(compose->newsgroup_list);
4585 compose->newsgroup_list = NULL;
4587 /* search header entries for to and newsgroup entries */
4588 for (list = compose->header_list; list; list = list->next) {
4591 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4592 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4595 if (entry[0] != '\0') {
4596 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4597 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4598 compose->to_list = address_list_append(compose->to_list, entry);
4599 recipient_found = TRUE;
4602 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4603 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4604 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4605 recipient_found = TRUE;
4612 return recipient_found;
4615 static gboolean compose_check_for_set_recipients(Compose *compose)
4617 if (compose->account->set_autocc && compose->account->auto_cc) {
4618 gboolean found_other = FALSE;
4620 /* search header entries for to and newsgroup entries */
4621 for (list = compose->header_list; list; list = list->next) {
4624 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4625 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4628 if (strcmp(entry, compose->account->auto_cc)
4629 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4639 if (compose->batch) {
4640 gtk_widget_show_all(compose->window);
4642 aval = alertpanel(_("Send"),
4643 _("The only recipient is the default CC address. Send anyway?"),
4644 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4645 if (aval != G_ALERTALTERNATE)
4649 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4650 gboolean found_other = FALSE;
4652 /* search header entries for to and newsgroup entries */
4653 for (list = compose->header_list; list; list = list->next) {
4656 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4657 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4660 if (strcmp(entry, compose->account->auto_bcc)
4661 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4671 if (compose->batch) {
4672 gtk_widget_show_all(compose->window);
4674 aval = alertpanel(_("Send"),
4675 _("The only recipient is the default BCC address. Send anyway?"),
4676 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4677 if (aval != G_ALERTALTERNATE)
4684 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4688 if (compose_check_for_valid_recipient(compose) == FALSE) {
4689 if (compose->batch) {
4690 gtk_widget_show_all(compose->window);
4692 alertpanel_error(_("Recipient is not specified."));
4696 if (compose_check_for_set_recipients(compose) == FALSE) {
4700 if (!compose->batch) {
4701 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4702 if (*str == '\0' && check_everything == TRUE &&
4703 compose->mode != COMPOSE_REDIRECT) {
4705 gchar *button_label;
4708 if (compose->sending)
4709 button_label = _("+_Send");
4711 button_label = _("+_Queue");
4712 message = g_strdup_printf(_("Subject is empty. %s"),
4713 compose->sending?_("Send it anyway?"):
4714 _("Queue it anyway?"));
4716 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4717 GTK_STOCK_CANCEL, button_label, NULL);
4719 if (aval != G_ALERTALTERNATE)
4724 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4730 gint compose_send(Compose *compose)
4733 FolderItem *folder = NULL;
4735 gchar *msgpath = NULL;
4736 gboolean discard_window = FALSE;
4737 gchar *errstr = NULL;
4738 gchar *tmsgid = NULL;
4739 MainWindow *mainwin = mainwindow_get_mainwindow();
4740 gboolean queued_removed = FALSE;
4742 if (prefs_common.send_dialog_invisible
4743 || compose->batch == TRUE)
4744 discard_window = TRUE;
4746 compose_allow_user_actions (compose, FALSE);
4747 compose->sending = TRUE;
4749 if (compose_check_entries(compose, TRUE) == FALSE) {
4750 if (compose->batch) {
4751 gtk_widget_show_all(compose->window);
4757 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4760 if (compose->batch) {
4761 gtk_widget_show_all(compose->window);
4764 alertpanel_error(_("Could not queue message for sending:\n\n"
4765 "Charset conversion failed."));
4766 } else if (val == -5) {
4767 alertpanel_error(_("Could not queue message for sending:\n\n"
4768 "Couldn't get recipient encryption key."));
4769 } else if (val == -6) {
4771 } else if (val == -3) {
4772 if (privacy_peek_error())
4773 alertpanel_error(_("Could not queue message for sending:\n\n"
4774 "Signature failed: %s"), privacy_get_error());
4775 } else if (val == -2 && errno != 0) {
4776 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4778 alertpanel_error(_("Could not queue message for sending."));
4783 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4784 if (discard_window) {
4785 compose->sending = FALSE;
4786 compose_close(compose);
4787 /* No more compose access in the normal codepath
4788 * after this point! */
4793 alertpanel_error(_("The message was queued but could not be "
4794 "sent.\nUse \"Send queued messages\" from "
4795 "the main window to retry."));
4796 if (!discard_window) {
4803 if (msgpath == NULL) {
4804 msgpath = folder_item_fetch_msg(folder, msgnum);
4805 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4808 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4809 claws_unlink(msgpath);
4812 if (!discard_window) {
4814 if (!queued_removed)
4815 folder_item_remove_msg(folder, msgnum);
4816 folder_item_scan(folder);
4818 /* make sure we delete that */
4819 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4821 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4822 folder_item_remove_msg(folder, tmp->msgnum);
4823 procmsg_msginfo_free(tmp);
4830 if (!queued_removed)
4831 folder_item_remove_msg(folder, msgnum);
4832 folder_item_scan(folder);
4834 /* make sure we delete that */
4835 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4837 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4838 folder_item_remove_msg(folder, tmp->msgnum);
4839 procmsg_msginfo_free(tmp);
4842 if (!discard_window) {
4843 compose->sending = FALSE;
4844 compose_allow_user_actions (compose, TRUE);
4845 compose_close(compose);
4849 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4850 "the main window to retry."), errstr);
4853 alertpanel_error_log(_("The message was queued but could not be "
4854 "sent.\nUse \"Send queued messages\" from "
4855 "the main window to retry."));
4857 if (!discard_window) {
4866 toolbar_main_set_sensitive(mainwin);
4867 main_window_set_menu_sensitive(mainwin);
4873 compose_allow_user_actions (compose, TRUE);
4874 compose->sending = FALSE;
4875 compose->modified = TRUE;
4876 toolbar_main_set_sensitive(mainwin);
4877 main_window_set_menu_sensitive(mainwin);
4882 static gboolean compose_use_attach(Compose *compose)
4884 GtkTreeModel *model = gtk_tree_view_get_model
4885 (GTK_TREE_VIEW(compose->attach_clist));
4886 return gtk_tree_model_iter_n_children(model, NULL) > 0;
4889 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
4892 gchar buf[BUFFSIZE];
4894 gboolean first_to_address;
4895 gboolean first_cc_address;
4897 ComposeHeaderEntry *headerentry;
4898 const gchar *headerentryname;
4899 const gchar *cc_hdr;
4900 const gchar *to_hdr;
4901 gboolean err = FALSE;
4903 debug_print("Writing redirect header\n");
4905 cc_hdr = prefs_common_translated_header_name("Cc:");
4906 to_hdr = prefs_common_translated_header_name("To:");
4908 first_to_address = TRUE;
4909 for (list = compose->header_list; list; list = list->next) {
4910 headerentry = ((ComposeHeaderEntry *)list->data);
4911 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4913 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
4914 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4915 Xstrdup_a(str, entstr, return -1);
4917 if (str[0] != '\0') {
4918 compose_convert_header
4919 (compose, buf, sizeof(buf), str,
4920 strlen("Resent-To") + 2, TRUE);
4922 if (first_to_address) {
4923 err |= (fprintf(fp, "Resent-To: ") < 0);
4924 first_to_address = FALSE;
4926 err |= (fprintf(fp, ",") < 0);
4928 err |= (fprintf(fp, "%s", buf) < 0);
4932 if (!first_to_address) {
4933 err |= (fprintf(fp, "\n") < 0);
4936 first_cc_address = TRUE;
4937 for (list = compose->header_list; list; list = list->next) {
4938 headerentry = ((ComposeHeaderEntry *)list->data);
4939 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4941 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
4942 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4943 Xstrdup_a(str, strg, return -1);
4945 if (str[0] != '\0') {
4946 compose_convert_header
4947 (compose, buf, sizeof(buf), str,
4948 strlen("Resent-Cc") + 2, TRUE);
4950 if (first_cc_address) {
4951 err |= (fprintf(fp, "Resent-Cc: ") < 0);
4952 first_cc_address = FALSE;
4954 err |= (fprintf(fp, ",") < 0);
4956 err |= (fprintf(fp, "%s", buf) < 0);
4960 if (!first_cc_address) {
4961 err |= (fprintf(fp, "\n") < 0);
4964 return (err ? -1:0);
4967 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
4969 gchar buf[BUFFSIZE];
4971 const gchar *entstr;
4972 /* struct utsname utsbuf; */
4973 gboolean err = FALSE;
4975 g_return_val_if_fail(fp != NULL, -1);
4976 g_return_val_if_fail(compose->account != NULL, -1);
4977 g_return_val_if_fail(compose->account->address != NULL, -1);
4980 get_rfc822_date(buf, sizeof(buf));
4981 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
4984 if (compose->account->name && *compose->account->name) {
4985 compose_convert_header
4986 (compose, buf, sizeof(buf), compose->account->name,
4987 strlen("From: "), TRUE);
4988 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
4989 buf, compose->account->address) < 0);
4991 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
4994 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4995 if (*entstr != '\0') {
4996 Xstrdup_a(str, entstr, return -1);
4999 compose_convert_header(compose, buf, sizeof(buf), str,
5000 strlen("Subject: "), FALSE);
5001 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5005 /* Resent-Message-ID */
5006 if (compose->account->set_domain && compose->account->domain) {
5007 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5008 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5009 g_snprintf(buf, sizeof(buf), "%s",
5010 strchr(compose->account->address, '@') ?
5011 strchr(compose->account->address, '@')+1 :
5012 compose->account->address);
5014 g_snprintf(buf, sizeof(buf), "%s", "");
5017 if (compose->account->gen_msgid) {
5018 generate_msgid(buf, sizeof(buf));
5019 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5020 compose->msgid = g_strdup(buf);
5022 compose->msgid = NULL;
5025 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5028 /* separator between header and body */
5029 err |= (fputs("\n", fp) == EOF);
5031 return (err ? -1:0);
5034 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5038 gchar buf[BUFFSIZE];
5040 gboolean skip = FALSE;
5041 gboolean err = FALSE;
5042 gchar *not_included[]={
5043 "Return-Path:", "Delivered-To:", "Received:",
5044 "Subject:", "X-UIDL:", "AF:",
5045 "NF:", "PS:", "SRH:",
5046 "SFN:", "DSR:", "MID:",
5047 "CFG:", "PT:", "S:",
5048 "RQ:", "SSV:", "NSV:",
5049 "SSH:", "R:", "MAID:",
5050 "NAID:", "RMID:", "FMID:",
5051 "SCF:", "RRCPT:", "NG:",
5052 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5053 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5054 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5055 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5056 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5059 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5060 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5064 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5066 for (i = 0; not_included[i] != NULL; i++) {
5067 if (g_ascii_strncasecmp(buf, not_included[i],
5068 strlen(not_included[i])) == 0) {
5075 if (fputs(buf, fdest) == -1)
5078 if (!prefs_common.redirect_keep_from) {
5079 if (g_ascii_strncasecmp(buf, "From:",
5080 strlen("From:")) == 0) {
5081 err |= (fputs(" (by way of ", fdest) == EOF);
5082 if (compose->account->name
5083 && *compose->account->name) {
5084 compose_convert_header
5085 (compose, buf, sizeof(buf),
5086 compose->account->name,
5089 err |= (fprintf(fdest, "%s <%s>",
5091 compose->account->address) < 0);
5093 err |= (fprintf(fdest, "%s",
5094 compose->account->address) < 0);
5095 err |= (fputs(")", fdest) == EOF);
5099 if (fputs("\n", fdest) == -1)
5106 if (compose_redirect_write_headers(compose, fdest))
5109 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5110 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5123 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5125 GtkTextBuffer *buffer;
5126 GtkTextIter start, end;
5129 const gchar *out_codeset;
5130 EncodingType encoding = ENC_UNKNOWN;
5131 MimeInfo *mimemsg, *mimetext;
5133 const gchar *src_codeset = CS_INTERNAL;
5135 if (action == COMPOSE_WRITE_FOR_SEND)
5136 attach_parts = TRUE;
5138 /* create message MimeInfo */
5139 mimemsg = procmime_mimeinfo_new();
5140 mimemsg->type = MIMETYPE_MESSAGE;
5141 mimemsg->subtype = g_strdup("rfc822");
5142 mimemsg->content = MIMECONTENT_MEM;
5143 mimemsg->tmp = TRUE; /* must free content later */
5144 mimemsg->data.mem = compose_get_header(compose);
5146 /* Create text part MimeInfo */
5147 /* get all composed text */
5148 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5149 gtk_text_buffer_get_start_iter(buffer, &start);
5150 gtk_text_buffer_get_end_iter(buffer, &end);
5151 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5153 out_codeset = conv_get_charset_str(compose->out_encoding);
5155 if (!out_codeset && is_ascii_str(chars)) {
5156 out_codeset = CS_US_ASCII;
5157 } else if (prefs_common.outgoing_fallback_to_ascii &&
5158 is_ascii_str(chars)) {
5159 out_codeset = CS_US_ASCII;
5160 encoding = ENC_7BIT;
5164 gchar *test_conv_global_out = NULL;
5165 gchar *test_conv_reply = NULL;
5167 /* automatic mode. be automatic. */
5168 codeconv_set_strict(TRUE);
5170 out_codeset = conv_get_outgoing_charset_str();
5172 debug_print("trying to convert to %s\n", out_codeset);
5173 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5176 if (!test_conv_global_out && compose->orig_charset
5177 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5178 out_codeset = compose->orig_charset;
5179 debug_print("failure; trying to convert to %s\n", out_codeset);
5180 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5183 if (!test_conv_global_out && !test_conv_reply) {
5185 out_codeset = CS_INTERNAL;
5186 debug_print("failure; finally using %s\n", out_codeset);
5188 g_free(test_conv_global_out);
5189 g_free(test_conv_reply);
5190 codeconv_set_strict(FALSE);
5193 if (encoding == ENC_UNKNOWN) {
5194 if (prefs_common.encoding_method == CTE_BASE64)
5195 encoding = ENC_BASE64;
5196 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5197 encoding = ENC_QUOTED_PRINTABLE;
5198 else if (prefs_common.encoding_method == CTE_8BIT)
5199 encoding = ENC_8BIT;
5201 encoding = procmime_get_encoding_for_charset(out_codeset);
5204 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5205 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5207 if (action == COMPOSE_WRITE_FOR_SEND) {
5208 codeconv_set_strict(TRUE);
5209 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5210 codeconv_set_strict(FALSE);
5216 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5217 "to the specified %s charset.\n"
5218 "Send it as %s?"), out_codeset, src_codeset);
5219 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5220 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5223 if (aval != G_ALERTALTERNATE) {
5228 out_codeset = src_codeset;
5234 out_codeset = src_codeset;
5239 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5240 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5241 strstr(buf, "\nFrom ") != NULL) {
5242 encoding = ENC_QUOTED_PRINTABLE;
5246 mimetext = procmime_mimeinfo_new();
5247 mimetext->content = MIMECONTENT_MEM;
5248 mimetext->tmp = TRUE; /* must free content later */
5249 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5250 * and free the data, which we need later. */
5251 mimetext->data.mem = g_strdup(buf);
5252 mimetext->type = MIMETYPE_TEXT;
5253 mimetext->subtype = g_strdup("plain");
5254 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5255 g_strdup(out_codeset));
5257 /* protect trailing spaces when signing message */
5258 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5259 privacy_system_can_sign(compose->privacy_system)) {
5260 encoding = ENC_QUOTED_PRINTABLE;
5263 debug_print("main text: %zd bytes encoded as %s in %d\n",
5264 strlen(buf), out_codeset, encoding);
5266 /* check for line length limit */
5267 if (action == COMPOSE_WRITE_FOR_SEND &&
5268 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5269 check_line_length(buf, 1000, &line) < 0) {
5273 msg = g_strdup_printf
5274 (_("Line %d exceeds the line length limit (998 bytes).\n"
5275 "The contents of the message might be broken on the way to the delivery.\n"
5277 "Send it anyway?"), line + 1);
5278 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5280 if (aval != G_ALERTALTERNATE) {
5286 if (encoding != ENC_UNKNOWN)
5287 procmime_encode_content(mimetext, encoding);
5289 /* append attachment parts */
5290 if (compose_use_attach(compose) && attach_parts) {
5291 MimeInfo *mimempart;
5292 gchar *boundary = NULL;
5293 mimempart = procmime_mimeinfo_new();
5294 mimempart->content = MIMECONTENT_EMPTY;
5295 mimempart->type = MIMETYPE_MULTIPART;
5296 mimempart->subtype = g_strdup("mixed");
5300 boundary = generate_mime_boundary(NULL);
5301 } while (strstr(buf, boundary) != NULL);
5303 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5306 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5308 g_node_append(mimempart->node, mimetext->node);
5309 g_node_append(mimemsg->node, mimempart->node);
5311 compose_add_attachments(compose, mimempart);
5313 g_node_append(mimemsg->node, mimetext->node);
5317 /* sign message if sending */
5318 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5319 privacy_system_can_sign(compose->privacy_system))
5320 if (!privacy_sign(compose->privacy_system, mimemsg, compose->account))
5323 procmime_write_mimeinfo(mimemsg, fp);
5325 procmime_mimeinfo_free_all(mimemsg);
5330 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5332 GtkTextBuffer *buffer;
5333 GtkTextIter start, end;
5338 if ((fp = g_fopen(file, "wb")) == NULL) {
5339 FILE_OP_ERROR(file, "fopen");
5343 /* chmod for security */
5344 if (change_file_mode_rw(fp, file) < 0) {
5345 FILE_OP_ERROR(file, "chmod");
5346 g_warning("can't change file mode\n");
5349 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5350 gtk_text_buffer_get_start_iter(buffer, &start);
5351 gtk_text_buffer_get_end_iter(buffer, &end);
5352 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5354 chars = conv_codeset_strdup
5355 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5358 if (!chars) return -1;
5361 len = strlen(chars);
5362 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5363 FILE_OP_ERROR(file, "fwrite");
5372 if (fclose(fp) == EOF) {
5373 FILE_OP_ERROR(file, "fclose");
5380 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5383 MsgInfo *msginfo = compose->targetinfo;
5385 g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5386 if (!msginfo) return -1;
5388 if (!force && MSG_IS_LOCKED(msginfo->flags))
5391 item = msginfo->folder;
5392 g_return_val_if_fail(item != NULL, -1);
5394 if (procmsg_msg_exist(msginfo) &&
5395 (folder_has_parent_of_type(item, F_QUEUE) ||
5396 folder_has_parent_of_type(item, F_DRAFT)
5397 || msginfo == compose->autosaved_draft)) {
5398 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5399 g_warning("can't remove the old message\n");
5402 debug_print("removed reedit target %d\n", msginfo->msgnum);
5409 static void compose_remove_draft(Compose *compose)
5412 MsgInfo *msginfo = compose->targetinfo;
5413 drafts = account_get_special_folder(compose->account, F_DRAFT);
5415 if (procmsg_msg_exist(msginfo)) {
5416 folder_item_remove_msg(drafts, msginfo->msgnum);
5421 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5422 gboolean remove_reedit_target)
5424 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5427 static gboolean compose_warn_encryption(Compose *compose)
5429 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5430 AlertValue val = G_ALERTALTERNATE;
5432 if (warning == NULL)
5435 val = alertpanel_full(_("Encryption warning"), warning,
5436 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5437 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5438 if (val & G_ALERTDISABLE) {
5439 val &= ~G_ALERTDISABLE;
5440 if (val == G_ALERTALTERNATE)
5441 privacy_inhibit_encrypt_warning(compose->privacy_system,
5445 if (val == G_ALERTALTERNATE) {
5452 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5453 gchar **msgpath, gboolean check_subject,
5454 gboolean remove_reedit_target)
5461 static gboolean lock = FALSE;
5462 PrefsAccount *mailac = NULL, *newsac = NULL;
5463 gboolean err = FALSE;
5465 debug_print("queueing message...\n");
5466 g_return_val_if_fail(compose->account != NULL, -1);
5470 if (compose_check_entries(compose, check_subject) == FALSE) {
5472 if (compose->batch) {
5473 gtk_widget_show_all(compose->window);
5478 if (!compose->to_list && !compose->newsgroup_list) {
5479 g_warning("can't get recipient list.");
5484 if (compose->to_list) {
5485 if (compose->account->protocol != A_NNTP)
5486 mailac = compose->account;
5487 else if (cur_account && cur_account->protocol != A_NNTP)
5488 mailac = cur_account;
5489 else if (!(mailac = compose_current_mail_account())) {
5491 alertpanel_error(_("No account for sending mails available!"));
5496 if (compose->newsgroup_list) {
5497 if (compose->account->protocol == A_NNTP)
5498 newsac = compose->account;
5499 else if (!newsac->protocol != A_NNTP) {
5501 alertpanel_error(_("No account for posting news available!"));
5506 /* write queue header */
5507 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5508 G_DIR_SEPARATOR, compose, (guint) rand());
5509 debug_print("queuing to %s\n", tmp);
5510 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5511 FILE_OP_ERROR(tmp, "fopen");
5517 if (change_file_mode_rw(fp, tmp) < 0) {
5518 FILE_OP_ERROR(tmp, "chmod");
5519 g_warning("can't change file mode\n");
5522 /* queueing variables */
5523 err |= (fprintf(fp, "AF:\n") < 0);
5524 err |= (fprintf(fp, "NF:0\n") < 0);
5525 err |= (fprintf(fp, "PS:10\n") < 0);
5526 err |= (fprintf(fp, "SRH:1\n") < 0);
5527 err |= (fprintf(fp, "SFN:\n") < 0);
5528 err |= (fprintf(fp, "DSR:\n") < 0);
5530 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5532 err |= (fprintf(fp, "MID:\n") < 0);
5533 err |= (fprintf(fp, "CFG:\n") < 0);
5534 err |= (fprintf(fp, "PT:0\n") < 0);
5535 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5536 err |= (fprintf(fp, "RQ:\n") < 0);
5538 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5540 err |= (fprintf(fp, "SSV:\n") < 0);
5542 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5544 err |= (fprintf(fp, "NSV:\n") < 0);
5545 err |= (fprintf(fp, "SSH:\n") < 0);
5546 /* write recepient list */
5547 if (compose->to_list) {
5548 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5549 for (cur = compose->to_list->next; cur != NULL;
5551 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5552 err |= (fprintf(fp, "\n") < 0);
5554 /* write newsgroup list */
5555 if (compose->newsgroup_list) {
5556 err |= (fprintf(fp, "NG:") < 0);
5557 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5558 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5559 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5560 err |= (fprintf(fp, "\n") < 0);
5562 /* Sylpheed account IDs */
5564 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5566 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5569 if (compose->privacy_system != NULL) {
5570 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5571 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5572 if (compose->use_encryption) {
5574 if (!compose_warn_encryption(compose)) {
5581 if (mailac && mailac->encrypt_to_self) {
5582 GSList *tmp_list = g_slist_copy(compose->to_list);
5583 tmp_list = g_slist_append(tmp_list, compose->account->address);
5584 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5585 g_slist_free(tmp_list);
5587 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5589 if (encdata != NULL) {
5590 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5591 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5592 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5594 } /* else we finally dont want to encrypt */
5596 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5597 /* and if encdata was null, it means there's been a problem in
5609 /* Save copy folder */
5610 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5611 gchar *savefolderid;
5613 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
5614 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5615 g_free(savefolderid);
5617 /* Save copy folder */
5618 if (compose->return_receipt) {
5619 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5621 /* Message-ID of message replying to */
5622 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5625 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5626 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5629 /* Message-ID of message forwarding to */
5630 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5633 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5634 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5638 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5639 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5641 /* end of headers */
5642 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5644 if (compose->redirect_filename != NULL) {
5645 if (compose_redirect_write_to_file(compose, fp) < 0) {
5654 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5659 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5663 g_warning("failed to write queue message\n");
5670 if (fclose(fp) == EOF) {
5671 FILE_OP_ERROR(tmp, "fclose");
5678 if (item && *item) {
5681 queue = account_get_special_folder(compose->account, F_QUEUE);
5684 g_warning("can't find queue folder\n");
5690 folder_item_scan(queue);
5691 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5692 g_warning("can't queue the message\n");
5699 if (msgpath == NULL) {
5705 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5706 compose_remove_reedit_target(compose, FALSE);
5709 if ((msgnum != NULL) && (item != NULL)) {
5717 static void compose_add_attachments(Compose *compose, MimeInfo *parent)
5720 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5722 struct stat statbuf;
5723 gchar *type, *subtype;
5724 GtkTreeModel *model;
5727 model = gtk_tree_view_get_model(tree_view);
5729 if (!gtk_tree_model_get_iter_first(model, &iter))
5732 gtk_tree_model_get(model, &iter,
5736 mimepart = procmime_mimeinfo_new();
5737 mimepart->content = MIMECONTENT_FILE;
5738 mimepart->data.filename = g_strdup(ainfo->file);
5739 mimepart->tmp = FALSE; /* or we destroy our attachment */
5740 mimepart->offset = 0;
5742 stat(ainfo->file, &statbuf);
5743 mimepart->length = statbuf.st_size;
5745 type = g_strdup(ainfo->content_type);
5747 if (!strchr(type, '/')) {
5749 type = g_strdup("application/octet-stream");
5752 subtype = strchr(type, '/') + 1;
5753 *(subtype - 1) = '\0';
5754 mimepart->type = procmime_get_media_type(type);
5755 mimepart->subtype = g_strdup(subtype);
5758 if (mimepart->type == MIMETYPE_MESSAGE &&
5759 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5760 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5763 g_hash_table_insert(mimepart->typeparameters,
5764 g_strdup("name"), g_strdup(ainfo->name));
5765 g_hash_table_insert(mimepart->dispositionparameters,
5766 g_strdup("filename"), g_strdup(ainfo->name));
5767 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5771 if (compose->use_signing) {
5772 if (ainfo->encoding == ENC_7BIT)
5773 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5774 else if (ainfo->encoding == ENC_8BIT)
5775 ainfo->encoding = ENC_BASE64;
5778 procmime_encode_content(mimepart, ainfo->encoding);
5780 g_node_append(parent->node, mimepart->node);
5781 } while (gtk_tree_model_iter_next(model, &iter));
5784 #define IS_IN_CUSTOM_HEADER(header) \
5785 (compose->account->add_customhdr && \
5786 custom_header_find(compose->account->customhdr_list, header) != NULL)
5788 static void compose_add_headerfield_from_headerlist(Compose *compose,
5790 const gchar *fieldname,
5791 const gchar *seperator)
5793 gchar *str, *fieldname_w_colon;
5794 gboolean add_field = FALSE;
5796 ComposeHeaderEntry *headerentry;
5797 const gchar *headerentryname;
5798 const gchar *trans_fieldname;
5801 if (IS_IN_CUSTOM_HEADER(fieldname))
5804 debug_print("Adding %s-fields\n", fieldname);
5806 fieldstr = g_string_sized_new(64);
5808 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5809 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5811 for (list = compose->header_list; list; list = list->next) {
5812 headerentry = ((ComposeHeaderEntry *)list->data);
5813 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5815 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5816 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5818 if (str[0] != '\0') {
5820 g_string_append(fieldstr, seperator);
5821 g_string_append(fieldstr, str);
5830 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5831 compose_convert_header
5832 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
5833 strlen(fieldname) + 2, TRUE);
5834 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5838 g_free(fieldname_w_colon);
5839 g_string_free(fieldstr, TRUE);
5844 static gchar *compose_get_header(Compose *compose)
5846 gchar buf[BUFFSIZE];
5847 const gchar *entry_str;
5851 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5853 gchar *from_name = NULL, *from_address = NULL;
5856 g_return_val_if_fail(compose->account != NULL, NULL);
5857 g_return_val_if_fail(compose->account->address != NULL, NULL);
5859 header = g_string_sized_new(64);
5862 get_rfc822_date(buf, sizeof(buf));
5863 g_string_append_printf(header, "Date: %s\n", buf);
5867 if (compose->account->name && *compose->account->name) {
5869 QUOTE_IF_REQUIRED(buf, compose->account->name);
5870 tmp = g_strdup_printf("%s <%s>",
5871 buf, compose->account->address);
5873 tmp = g_strdup_printf("%s",
5874 compose->account->address);
5876 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5877 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5879 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5880 from_address = g_strdup(compose->account->address);
5882 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5883 /* extract name and address */
5884 if (strstr(spec, " <") && strstr(spec, ">")) {
5885 from_address = g_strdup(strrchr(spec, '<')+1);
5886 *(strrchr(from_address, '>')) = '\0';
5887 from_name = g_strdup(spec);
5888 *(strrchr(from_name, '<')) = '\0';
5891 from_address = g_strdup(spec);
5898 if (from_name && *from_name) {
5899 compose_convert_header
5900 (compose, buf, sizeof(buf), from_name,
5901 strlen("From: "), TRUE);
5902 QUOTE_IF_REQUIRED(name, buf);
5904 g_string_append_printf(header, "From: %s <%s>\n",
5905 name, from_address);
5907 g_string_append_printf(header, "From: %s\n", from_address);
5910 g_free(from_address);
5913 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
5916 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
5919 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
5922 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
5925 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
5927 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
5930 compose_convert_header(compose, buf, sizeof(buf), str,
5931 strlen("Subject: "), FALSE);
5932 g_string_append_printf(header, "Subject: %s\n", buf);
5938 if (compose->account->set_domain && compose->account->domain) {
5939 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5940 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5941 g_snprintf(buf, sizeof(buf), "%s",
5942 strchr(compose->account->address, '@') ?
5943 strchr(compose->account->address, '@')+1 :
5944 compose->account->address);
5946 g_snprintf(buf, sizeof(buf), "%s", "");
5949 if (compose->account->gen_msgid) {
5950 generate_msgid(buf, sizeof(buf));
5951 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
5952 compose->msgid = g_strdup(buf);
5954 compose->msgid = NULL;
5957 if (compose->remove_references == FALSE) {
5959 if (compose->inreplyto && compose->to_list)
5960 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
5963 if (compose->references)
5964 g_string_append_printf(header, "References: %s\n", compose->references);
5968 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
5971 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
5974 if (compose->account->organization &&
5975 strlen(compose->account->organization) &&
5976 !IS_IN_CUSTOM_HEADER("Organization")) {
5977 compose_convert_header(compose, buf, sizeof(buf),
5978 compose->account->organization,
5979 strlen("Organization: "), FALSE);
5980 g_string_append_printf(header, "Organization: %s\n", buf);
5983 /* Program version and system info */
5984 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
5985 !compose->newsgroup_list) {
5986 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
5988 gtk_major_version, gtk_minor_version, gtk_micro_version,
5991 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
5992 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
5994 gtk_major_version, gtk_minor_version, gtk_micro_version,
5998 /* custom headers */
5999 if (compose->account->add_customhdr) {
6002 for (cur = compose->account->customhdr_list; cur != NULL;
6004 CustomHeader *chdr = (CustomHeader *)cur->data;
6006 if (custom_header_is_allowed(chdr->name)) {
6007 compose_convert_header
6008 (compose, buf, sizeof(buf),
6009 chdr->value ? chdr->value : "",
6010 strlen(chdr->name) + 2, FALSE);
6011 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6016 /* Automatic Faces and X-Faces */
6017 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6018 g_string_append_printf(header, "X-Face: %s\n", buf);
6020 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6021 g_string_append_printf(header, "X-Face: %s\n", buf);
6023 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6024 g_string_append_printf(header, "Face: %s\n", buf);
6026 else if (get_default_face (buf, sizeof(buf)) == 0) {
6027 g_string_append_printf(header, "Face: %s\n", buf);
6031 switch (compose->priority) {
6032 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6033 "X-Priority: 1 (Highest)\n");
6035 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6036 "X-Priority: 2 (High)\n");
6038 case PRIORITY_NORMAL: break;
6039 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6040 "X-Priority: 4 (Low)\n");
6042 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6043 "X-Priority: 5 (Lowest)\n");
6045 default: debug_print("compose: priority unknown : %d\n",
6049 /* Request Return Receipt */
6050 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6051 if (compose->return_receipt) {
6052 if (compose->account->name
6053 && *compose->account->name) {
6054 compose_convert_header(compose, buf, sizeof(buf),
6055 compose->account->name,
6056 strlen("Disposition-Notification-To: "),
6058 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6060 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6064 /* get special headers */
6065 for (list = compose->header_list; list; list = list->next) {
6066 ComposeHeaderEntry *headerentry;
6069 gchar *headername_wcolon;
6070 const gchar *headername_trans;
6073 gboolean standard_header = FALSE;
6075 headerentry = ((ComposeHeaderEntry *)list->data);
6077 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6079 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6084 if (!strstr(tmp, ":")) {
6085 headername_wcolon = g_strconcat(tmp, ":", NULL);
6086 headername = g_strdup(tmp);
6088 headername_wcolon = g_strdup(tmp);
6089 headername = g_strdup(strtok(tmp, ":"));
6093 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6094 Xstrdup_a(headervalue, entry_str, return NULL);
6095 subst_char(headervalue, '\r', ' ');
6096 subst_char(headervalue, '\n', ' ');
6097 string = std_headers;
6098 while (*string != NULL) {
6099 headername_trans = prefs_common_translated_header_name(*string);
6100 if (!strcmp(headername_trans, headername_wcolon))
6101 standard_header = TRUE;
6104 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6105 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6108 g_free(headername_wcolon);
6112 g_string_free(header, FALSE);
6117 #undef IS_IN_CUSTOM_HEADER
6119 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6120 gint header_len, gboolean addr_field)
6122 gchar *tmpstr = NULL;
6123 const gchar *out_codeset = NULL;
6125 g_return_if_fail(src != NULL);
6126 g_return_if_fail(dest != NULL);
6128 if (len < 1) return;
6130 tmpstr = g_strdup(src);
6132 subst_char(tmpstr, '\n', ' ');
6133 subst_char(tmpstr, '\r', ' ');
6136 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6137 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6138 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6143 codeconv_set_strict(TRUE);
6144 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6145 conv_get_charset_str(compose->out_encoding));
6146 codeconv_set_strict(FALSE);
6148 if (!dest || *dest == '\0') {
6149 gchar *test_conv_global_out = NULL;
6150 gchar *test_conv_reply = NULL;
6152 /* automatic mode. be automatic. */
6153 codeconv_set_strict(TRUE);
6155 out_codeset = conv_get_outgoing_charset_str();
6157 debug_print("trying to convert to %s\n", out_codeset);
6158 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6161 if (!test_conv_global_out && compose->orig_charset
6162 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6163 out_codeset = compose->orig_charset;
6164 debug_print("failure; trying to convert to %s\n", out_codeset);
6165 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6168 if (!test_conv_global_out && !test_conv_reply) {
6170 out_codeset = CS_INTERNAL;
6171 debug_print("finally using %s\n", out_codeset);
6173 g_free(test_conv_global_out);
6174 g_free(test_conv_reply);
6175 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6177 codeconv_set_strict(FALSE);
6182 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6186 g_return_if_fail(user_data != NULL);
6188 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6189 g_strstrip(address);
6190 if (*address != '\0') {
6191 gchar *name = procheader_get_fromname(address);
6192 extract_address(address);
6193 addressbook_add_contact(name, address, NULL, NULL);
6198 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6200 GtkWidget *menuitem;
6203 g_return_if_fail(menu != NULL);
6204 g_return_if_fail(GTK_IS_MENU_SHELL(menu));
6206 menuitem = gtk_separator_menu_item_new();
6207 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6208 gtk_widget_show(menuitem);
6210 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6211 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6213 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6214 g_strstrip(address);
6215 if (*address == '\0') {
6216 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6219 g_signal_connect(G_OBJECT(menuitem), "activate",
6220 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6221 gtk_widget_show(menuitem);
6224 static void compose_create_header_entry(Compose *compose)
6226 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6231 const gchar *header = NULL;
6232 ComposeHeaderEntry *headerentry;
6233 gboolean standard_header = FALSE;
6234 #if !(GTK_CHECK_VERSION(2,12,0))
6235 GtkTooltips *tips = compose->tooltips;
6238 headerentry = g_new0(ComposeHeaderEntry, 1);
6241 combo = gtk_combo_box_entry_new_text();
6243 while(*string != NULL) {
6244 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6245 (gchar*)prefs_common_translated_header_name(*string));
6248 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6249 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6250 G_CALLBACK(compose_grab_focus_cb), compose);
6251 gtk_widget_show(combo);
6252 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6253 compose->header_nextrow, compose->header_nextrow+1,
6254 GTK_SHRINK, GTK_FILL, 0, 0);
6255 if (compose->header_last) {
6256 const gchar *last_header_entry = gtk_entry_get_text(
6257 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6259 while (*string != NULL) {
6260 if (!strcmp(*string, last_header_entry))
6261 standard_header = TRUE;
6264 if (standard_header)
6265 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6267 if (!compose->header_last || !standard_header) {
6268 switch(compose->account->protocol) {
6270 header = prefs_common_translated_header_name("Newsgroups:");
6273 header = prefs_common_translated_header_name("To:");
6278 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6280 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6281 G_CALLBACK(compose_grab_focus_cb), compose);
6284 entry = gtk_entry_new();
6285 gtk_widget_show(entry);
6286 CLAWS_SET_TIP(entry,
6287 _("Use <tab> to autocomplete from addressbook"));
6288 gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2,
6289 compose->header_nextrow, compose->header_nextrow+1,
6290 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6292 g_signal_connect(G_OBJECT(entry), "key-press-event",
6293 G_CALLBACK(compose_headerentry_key_press_event_cb),
6295 g_signal_connect(G_OBJECT(entry), "changed",
6296 G_CALLBACK(compose_headerentry_changed_cb),
6298 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6299 G_CALLBACK(compose_grab_focus_cb), compose);
6302 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6303 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6304 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6305 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6306 G_CALLBACK(compose_header_drag_received_cb),
6308 g_signal_connect(G_OBJECT(entry), "drag-drop",
6309 G_CALLBACK(compose_drag_drop),
6311 g_signal_connect(G_OBJECT(entry), "populate-popup",
6312 G_CALLBACK(compose_entry_popup_extend),
6315 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6317 headerentry->compose = compose;
6318 headerentry->combo = combo;
6319 headerentry->entry = entry;
6320 headerentry->headernum = compose->header_nextrow;
6322 compose->header_nextrow++;
6323 compose->header_last = headerentry;
6324 compose->header_list =
6325 g_slist_append(compose->header_list,
6329 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text)
6331 ComposeHeaderEntry *last_header;
6333 last_header = compose->header_last;
6335 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((last_header->combo)))), header);
6336 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6339 static void compose_remove_header_entries(Compose *compose)
6342 for (list = compose->header_list; list; list = list->next) {
6343 ComposeHeaderEntry *headerentry =
6344 (ComposeHeaderEntry *)list->data;
6345 gtk_widget_destroy(headerentry->combo);
6346 gtk_widget_destroy(headerentry->entry);
6347 g_free(headerentry);
6349 compose->header_last = NULL;
6350 g_slist_free(compose->header_list);
6351 compose->header_list = NULL;
6352 compose->header_nextrow = 1;
6353 compose_create_header_entry(compose);
6356 static GtkWidget *compose_create_header(Compose *compose)
6358 GtkWidget *from_optmenu_hbox;
6359 GtkWidget *header_scrolledwin;
6360 GtkWidget *header_table;
6364 /* header labels and entries */
6365 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6366 gtk_widget_show(header_scrolledwin);
6367 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6369 header_table = gtk_table_new(2, 2, FALSE);
6370 gtk_widget_show(header_table);
6371 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6372 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6373 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6376 /* option menu for selecting accounts */
6377 from_optmenu_hbox = compose_account_option_menu_create(compose);
6378 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6379 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6382 compose->header_table = header_table;
6383 compose->header_list = NULL;
6384 compose->header_nextrow = count;
6386 compose_create_header_entry(compose);
6388 compose->table = NULL;
6390 return header_scrolledwin ;
6393 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6395 Compose *compose = (Compose *)data;
6396 GdkEventButton event;
6399 event.time = gtk_get_current_event_time();
6401 return attach_button_pressed(compose->attach_clist, &event, compose);
6404 static GtkWidget *compose_create_attach(Compose *compose)
6406 GtkWidget *attach_scrwin;
6407 GtkWidget *attach_clist;
6409 GtkListStore *store;
6410 GtkCellRenderer *renderer;
6411 GtkTreeViewColumn *column;
6412 GtkTreeSelection *selection;
6414 /* attachment list */
6415 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6416 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6417 GTK_POLICY_AUTOMATIC,
6418 GTK_POLICY_AUTOMATIC);
6419 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6421 store = gtk_list_store_new(N_ATTACH_COLS,
6426 G_TYPE_AUTO_POINTER,
6428 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6429 (GTK_TREE_MODEL(store)));
6430 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6431 g_object_unref(store);
6433 renderer = gtk_cell_renderer_text_new();
6434 column = gtk_tree_view_column_new_with_attributes
6435 (_("Mime type"), renderer, "text",
6436 COL_MIMETYPE, NULL);
6437 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6439 renderer = gtk_cell_renderer_text_new();
6440 column = gtk_tree_view_column_new_with_attributes
6441 (_("Size"), renderer, "text",
6443 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6445 renderer = gtk_cell_renderer_text_new();
6446 column = gtk_tree_view_column_new_with_attributes
6447 (_("Name"), renderer, "text",
6449 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6451 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6452 prefs_common.use_stripes_everywhere);
6453 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6454 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6456 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6457 G_CALLBACK(attach_selected), compose);
6458 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6459 G_CALLBACK(attach_button_pressed), compose);
6461 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6462 G_CALLBACK(popup_attach_button_pressed), compose);
6464 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6465 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6466 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6467 G_CALLBACK(popup_attach_button_pressed), compose);
6469 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6470 G_CALLBACK(attach_key_pressed), compose);
6473 gtk_drag_dest_set(attach_clist,
6474 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6475 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6476 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6477 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6478 G_CALLBACK(compose_attach_drag_received_cb),
6480 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6481 G_CALLBACK(compose_drag_drop),
6484 compose->attach_scrwin = attach_scrwin;
6485 compose->attach_clist = attach_clist;
6487 return attach_scrwin;
6490 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6491 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6493 static GtkWidget *compose_create_others(Compose *compose)
6496 GtkWidget *savemsg_checkbtn;
6497 GtkWidget *savemsg_entry;
6498 GtkWidget *savemsg_select;
6501 gchar *folderidentifier;
6503 /* Table for settings */
6504 table = gtk_table_new(3, 1, FALSE);
6505 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6506 gtk_widget_show(table);
6507 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6510 /* Save Message to folder */
6511 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6512 gtk_widget_show(savemsg_checkbtn);
6513 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6514 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6515 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6517 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6518 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6520 savemsg_entry = gtk_entry_new();
6521 gtk_widget_show(savemsg_entry);
6522 gtk_table_attach_defaults(GTK_TABLE(table), savemsg_entry, 1, 2, rowcount, rowcount + 1);
6523 gtk_editable_set_editable(GTK_EDITABLE(savemsg_entry), prefs_common.savemsg);
6524 g_signal_connect_after(G_OBJECT(savemsg_entry), "grab_focus",
6525 G_CALLBACK(compose_grab_focus_cb), compose);
6526 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6527 folderidentifier = folder_item_get_identifier(account_get_special_folder
6528 (compose->account, F_OUTBOX));
6529 gtk_entry_set_text(GTK_ENTRY(savemsg_entry), folderidentifier);
6530 g_free(folderidentifier);
6533 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6534 gtk_widget_show(savemsg_select);
6535 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6536 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6537 G_CALLBACK(compose_savemsg_select_cb),
6542 compose->savemsg_checkbtn = savemsg_checkbtn;
6543 compose->savemsg_entry = savemsg_entry;
6548 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6550 gtk_editable_set_editable(GTK_EDITABLE(compose->savemsg_entry),
6551 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6554 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6559 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6562 path = folder_item_get_identifier(dest);
6564 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), path);
6568 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6569 GdkAtom clip, GtkTextIter *insert_place);
6572 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6576 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6578 if (event->button == 3) {
6580 GtkTextIter sel_start, sel_end;
6581 gboolean stuff_selected;
6583 /* move the cursor to allow GtkAspell to check the word
6584 * under the mouse */
6585 if (event->x && event->y) {
6586 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6587 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6589 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6592 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6593 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6596 stuff_selected = gtk_text_buffer_get_selection_bounds(
6598 &sel_start, &sel_end);
6600 gtk_text_buffer_place_cursor (buffer, &iter);
6601 /* reselect stuff */
6603 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6604 gtk_text_buffer_select_range(buffer,
6605 &sel_start, &sel_end);
6607 return FALSE; /* pass the event so that the right-click goes through */
6610 if (event->button == 2) {
6615 /* get the middle-click position to paste at the correct place */
6616 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6617 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6619 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6622 entry_paste_clipboard(compose, text,
6623 prefs_common.linewrap_pastes,
6624 GDK_SELECTION_PRIMARY, &iter);
6632 static void compose_spell_menu_changed(void *data)
6634 Compose *compose = (Compose *)data;
6636 GtkWidget *menuitem;
6637 GtkWidget *parent_item;
6638 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6641 if (compose->gtkaspell == NULL)
6644 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6645 "/Menu/Spelling/Options");
6647 /* setting the submenu removes /Spelling/Options from the factory
6648 * so we need to save it */
6650 if (parent_item == NULL) {
6651 parent_item = compose->aspell_options_menu;
6652 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6654 compose->aspell_options_menu = parent_item;
6656 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6658 spell_menu = g_slist_reverse(spell_menu);
6659 for (items = spell_menu;
6660 items; items = items->next) {
6661 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6662 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6663 gtk_widget_show(GTK_WIDGET(menuitem));
6665 g_slist_free(spell_menu);
6667 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6672 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6674 Compose *compose = (Compose *)data;
6675 GdkEventButton event;
6678 event.time = gtk_get_current_event_time();
6682 return text_clicked(compose->text, &event, compose);
6685 static gboolean compose_force_window_origin = TRUE;
6686 static Compose *compose_create(PrefsAccount *account,
6695 GtkWidget *handlebox;
6697 GtkWidget *notebook;
6699 GtkWidget *attach_hbox;
6700 GtkWidget *attach_lab1;
6701 GtkWidget *attach_lab2;
6706 GtkWidget *subject_hbox;
6707 GtkWidget *subject_frame;
6708 GtkWidget *subject_entry;
6712 GtkWidget *edit_vbox;
6713 GtkWidget *ruler_hbox;
6715 GtkWidget *scrolledwin;
6717 GtkTextBuffer *buffer;
6718 GtkClipboard *clipboard;
6721 UndoMain *undostruct;
6723 gchar *titles[N_ATTACH_COLS];
6724 GtkWidget *popupmenu;
6725 GtkWidget *tmpl_menu;
6726 GtkActionGroup *action_group = NULL;
6729 GtkAspell * gtkaspell = NULL;
6732 static GdkGeometry geometry;
6734 g_return_val_if_fail(account != NULL, NULL);
6736 debug_print("Creating compose window...\n");
6737 compose = g_new0(Compose, 1);
6739 titles[COL_MIMETYPE] = _("MIME type");
6740 titles[COL_SIZE] = _("Size");
6741 titles[COL_NAME] = _("Name");
6743 compose->batch = batch;
6744 compose->account = account;
6745 compose->folder = folder;
6747 compose->mutex = g_mutex_new();
6748 compose->set_cursor_pos = -1;
6750 #if !(GTK_CHECK_VERSION(2,12,0))
6751 compose->tooltips = tips;
6754 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6756 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6757 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6759 if (!geometry.max_width) {
6760 geometry.max_width = gdk_screen_width();
6761 geometry.max_height = gdk_screen_height();
6764 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6765 &geometry, GDK_HINT_MAX_SIZE);
6766 if (!geometry.min_width) {
6767 geometry.min_width = 600;
6768 geometry.min_height = 480;
6770 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6771 &geometry, GDK_HINT_MIN_SIZE);
6773 #ifndef GENERIC_UMPC
6774 if (compose_force_window_origin)
6775 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
6776 prefs_common.compose_y);
6778 g_signal_connect(G_OBJECT(window), "delete_event",
6779 G_CALLBACK(compose_delete_cb), compose);
6780 MANAGE_WINDOW_SIGNALS_CONNECT(window);
6781 gtk_widget_realize(window);
6783 gtkut_widget_set_composer_icon(window);
6785 vbox = gtk_vbox_new(FALSE, 0);
6786 gtk_container_add(GTK_CONTAINER(window), vbox);
6788 compose->ui_manager = gtk_ui_manager_new();
6789 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
6790 G_N_ELEMENTS(compose_entries), (gpointer)compose);
6791 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
6792 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
6793 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
6794 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
6795 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
6796 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
6797 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
6798 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
6801 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
6803 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENU)
6806 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
6807 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
6809 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
6811 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
6812 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
6813 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
6816 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
6817 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
6818 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6819 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
6820 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
6821 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
6822 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6823 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
6824 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6825 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
6828 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
6829 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
6830 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6832 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
6833 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
6834 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
6836 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
6837 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
6838 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
6839 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
6841 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
6843 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
6844 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
6845 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
6846 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
6847 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
6848 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
6849 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
6850 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
6851 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
6852 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
6853 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
6854 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
6855 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
6856 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
6857 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
6859 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6861 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
6862 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
6863 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
6864 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
6865 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
6867 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6869 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
6873 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
6874 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
6875 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
6876 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
6877 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
6878 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
6882 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
6883 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
6884 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
6885 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
6886 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
6888 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6889 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
6890 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6891 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
6892 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
6895 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6896 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
6897 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
6898 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
6899 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
6900 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
6901 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
6903 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6904 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
6905 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6906 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
6907 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6909 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
6911 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
6912 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6913 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
6914 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
6915 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6917 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
6918 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)
6919 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)
6920 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
6922 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
6924 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
6925 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)
6926 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)
6928 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
6930 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
6931 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)
6932 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
6934 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
6935 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)
6936 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
6938 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
6940 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
6941 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)
6942 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
6943 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
6944 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
6946 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
6947 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)
6948 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)
6949 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
6950 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
6952 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
6953 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
6954 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
6955 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
6956 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
6958 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
6959 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
6960 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)
6962 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
6963 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
6964 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
6968 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
6969 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
6970 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
6971 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6972 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
6973 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6976 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
6978 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
6979 gtk_widget_show_all(menubar);
6981 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
6983 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
6985 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
6988 if (prefs_common.toolbar_detachable) {
6989 handlebox = gtk_handle_box_new();
6991 handlebox = gtk_hbox_new(FALSE, 0);
6993 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
6995 gtk_widget_realize(handlebox);
6997 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7000 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7004 vbox2 = gtk_vbox_new(FALSE, 2);
7005 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7006 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7009 notebook = gtk_notebook_new();
7010 gtk_widget_set_size_request(notebook, -1, 130);
7011 gtk_widget_show(notebook);
7013 /* header labels and entries */
7014 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7015 compose_create_header(compose),
7016 gtk_label_new_with_mnemonic(_("Hea_der")));
7017 /* attachment list */
7018 attach_hbox = gtk_hbox_new(FALSE, 0);
7019 gtk_widget_show(attach_hbox);
7021 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7022 gtk_widget_show(attach_lab1);
7023 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7025 attach_lab2 = gtk_label_new("");
7026 gtk_widget_show(attach_lab2);
7027 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7029 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7030 compose_create_attach(compose),
7033 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7034 compose_create_others(compose),
7035 gtk_label_new_with_mnemonic(_("Othe_rs")));
7038 subject_hbox = gtk_hbox_new(FALSE, 0);
7039 gtk_widget_show(subject_hbox);
7041 subject_frame = gtk_frame_new(NULL);
7042 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7043 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7044 gtk_widget_show(subject_frame);
7046 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7047 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7048 gtk_widget_show(subject);
7050 label = gtk_label_new(_("Subject:"));
7051 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7052 gtk_widget_show(label);
7054 subject_entry = gtk_entry_new();
7055 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7056 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7057 G_CALLBACK(compose_grab_focus_cb), compose);
7058 gtk_widget_show(subject_entry);
7059 compose->subject_entry = subject_entry;
7060 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7062 edit_vbox = gtk_vbox_new(FALSE, 0);
7064 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7067 ruler_hbox = gtk_hbox_new(FALSE, 0);
7068 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7070 ruler = gtk_shruler_new();
7071 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7072 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7076 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7077 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7078 GTK_POLICY_AUTOMATIC,
7079 GTK_POLICY_AUTOMATIC);
7080 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7082 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7083 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7085 text = gtk_text_view_new();
7086 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7087 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7088 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7089 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7090 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7092 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7094 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7095 G_CALLBACK(compose_edit_size_alloc),
7097 g_signal_connect(G_OBJECT(buffer), "changed",
7098 G_CALLBACK(compose_changed_cb), compose);
7099 g_signal_connect(G_OBJECT(text), "grab_focus",
7100 G_CALLBACK(compose_grab_focus_cb), compose);
7101 g_signal_connect(G_OBJECT(buffer), "insert_text",
7102 G_CALLBACK(text_inserted), compose);
7103 g_signal_connect(G_OBJECT(text), "button_press_event",
7104 G_CALLBACK(text_clicked), compose);
7106 g_signal_connect(G_OBJECT(text), "popup-menu",
7107 G_CALLBACK(compose_popup_menu), compose);
7109 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7110 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7111 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7112 G_CALLBACK(compose_popup_menu), compose);
7114 g_signal_connect(G_OBJECT(subject_entry), "changed",
7115 G_CALLBACK(compose_changed_cb), compose);
7118 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7119 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7120 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7121 g_signal_connect(G_OBJECT(text), "drag_data_received",
7122 G_CALLBACK(compose_insert_drag_received_cb),
7124 g_signal_connect(G_OBJECT(text), "drag-drop",
7125 G_CALLBACK(compose_drag_drop),
7127 gtk_widget_show_all(vbox);
7129 /* pane between attach clist and text */
7130 paned = gtk_vpaned_new();
7131 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7133 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7134 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7136 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7138 gtk_paned_add1(GTK_PANED(paned), notebook);
7139 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7140 gtk_widget_show_all(paned);
7143 if (prefs_common.textfont) {
7144 PangoFontDescription *font_desc;
7146 font_desc = pango_font_description_from_string
7147 (prefs_common.textfont);
7149 gtk_widget_modify_font(text, font_desc);
7150 pango_font_description_free(font_desc);
7154 gtk_action_group_add_actions(action_group, compose_popup_entries,
7155 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7156 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7157 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7158 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7159 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7160 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7161 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7163 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7165 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7166 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7167 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7169 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7171 undostruct = undo_init(text);
7172 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7175 address_completion_start(window);
7177 compose->window = window;
7178 compose->vbox = vbox;
7179 compose->menubar = menubar;
7180 compose->handlebox = handlebox;
7182 compose->vbox2 = vbox2;
7184 compose->paned = paned;
7186 compose->attach_label = attach_lab2;
7188 compose->notebook = notebook;
7189 compose->edit_vbox = edit_vbox;
7190 compose->ruler_hbox = ruler_hbox;
7191 compose->ruler = ruler;
7192 compose->scrolledwin = scrolledwin;
7193 compose->text = text;
7195 compose->focused_editable = NULL;
7197 compose->popupmenu = popupmenu;
7199 compose->tmpl_menu = tmpl_menu;
7201 compose->mode = mode;
7202 compose->rmode = mode;
7204 compose->targetinfo = NULL;
7205 compose->replyinfo = NULL;
7206 compose->fwdinfo = NULL;
7208 compose->replyto = NULL;
7210 compose->bcc = NULL;
7211 compose->followup_to = NULL;
7213 compose->ml_post = NULL;
7215 compose->inreplyto = NULL;
7216 compose->references = NULL;
7217 compose->msgid = NULL;
7218 compose->boundary = NULL;
7220 compose->autowrap = prefs_common.autowrap;
7221 compose->autoindent = prefs_common.auto_indent;
7222 compose->use_signing = FALSE;
7223 compose->use_encryption = FALSE;
7224 compose->privacy_system = NULL;
7226 compose->modified = FALSE;
7228 compose->return_receipt = FALSE;
7230 compose->to_list = NULL;
7231 compose->newsgroup_list = NULL;
7233 compose->undostruct = undostruct;
7235 compose->sig_str = NULL;
7237 compose->exteditor_file = NULL;
7238 compose->exteditor_pid = -1;
7239 compose->exteditor_tag = -1;
7240 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7243 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7244 if (mode != COMPOSE_REDIRECT) {
7245 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7246 strcmp(prefs_common.dictionary, "")) {
7247 gtkaspell = gtkaspell_new(prefs_common.aspell_path,
7248 prefs_common.dictionary,
7249 prefs_common.alt_dictionary,
7250 conv_get_locale_charset_str(),
7251 prefs_common.misspelled_col,
7252 prefs_common.check_while_typing,
7253 prefs_common.recheck_when_changing_dict,
7254 prefs_common.use_alternate,
7255 prefs_common.use_both_dicts,
7256 GTK_TEXT_VIEW(text),
7257 GTK_WINDOW(compose->window),
7258 compose_spell_menu_changed,
7261 alertpanel_error(_("Spell checker could not "
7263 gtkaspell_checkers_strerror());
7264 gtkaspell_checkers_reset_error();
7266 if (!gtkaspell_set_sug_mode(gtkaspell,
7267 prefs_common.aspell_sugmode)) {
7268 debug_print("Aspell: could not set "
7269 "suggestion mode %s\n",
7270 gtkaspell_checkers_strerror());
7271 gtkaspell_checkers_reset_error();
7274 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7278 compose->gtkaspell = gtkaspell;
7279 compose_spell_menu_changed(compose);
7282 compose_select_account(compose, account, TRUE);
7284 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7285 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7287 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7288 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7290 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7291 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7293 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7294 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7296 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7297 if (account->protocol != A_NNTP)
7298 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7299 prefs_common_translated_header_name("To:"));
7301 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7302 prefs_common_translated_header_name("Newsgroups:"));
7304 addressbook_set_target_compose(compose);
7306 if (mode != COMPOSE_REDIRECT)
7307 compose_set_template_menu(compose);
7309 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7312 compose_list = g_list_append(compose_list, compose);
7314 if (!prefs_common.show_ruler)
7315 gtk_widget_hide(ruler_hbox);
7317 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7320 compose->priority = PRIORITY_NORMAL;
7321 compose_update_priority_menu_item(compose);
7323 compose_set_out_encoding(compose);
7326 compose_update_actions_menu(compose);
7328 /* Privacy Systems menu */
7329 compose_update_privacy_systems_menu(compose);
7331 activate_privacy_system(compose, account, TRUE);
7332 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7334 gtk_widget_realize(window);
7336 gtk_widget_show(window);
7338 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7339 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7346 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7351 GtkWidget *optmenubox;
7354 GtkWidget *from_name = NULL;
7355 #if !(GTK_CHECK_VERSION(2,12,0))
7356 GtkTooltips *tips = compose->tooltips;
7359 gint num = 0, def_menu = 0;
7361 accounts = account_get_list();
7362 g_return_val_if_fail(accounts != NULL, NULL);
7364 optmenubox = gtk_event_box_new();
7365 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7366 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7368 hbox = gtk_hbox_new(FALSE, 6);
7369 from_name = gtk_entry_new();
7371 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7372 G_CALLBACK(compose_grab_focus_cb), compose);
7374 for (; accounts != NULL; accounts = accounts->next, num++) {
7375 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7376 gchar *name, *from = NULL;
7378 if (ac == compose->account) def_menu = num;
7380 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7383 if (ac == compose->account) {
7384 if (ac->name && *ac->name) {
7386 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7387 from = g_strdup_printf("%s <%s>",
7389 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7391 from = g_strdup_printf("%s",
7393 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7396 COMBOBOX_ADD(menu, name, ac->account_id);
7401 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7403 g_signal_connect(G_OBJECT(optmenu), "changed",
7404 G_CALLBACK(account_activated),
7406 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7407 G_CALLBACK(compose_entry_popup_extend),
7410 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7411 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7413 CLAWS_SET_TIP(optmenubox,
7414 _("Account to use for this email"));
7415 CLAWS_SET_TIP(from_name,
7416 _("Sender address to be used"));
7418 compose->from_name = from_name;
7423 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7425 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7426 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7427 Compose *compose = (Compose *) data;
7429 compose->priority = value;
7433 static void compose_reply_change_mode(Compose *compose,
7436 gboolean was_modified = compose->modified;
7438 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7440 g_return_if_fail(compose->replyinfo != NULL);
7442 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7444 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7446 if (action == COMPOSE_REPLY_TO_ALL)
7448 if (action == COMPOSE_REPLY_TO_SENDER)
7450 if (action == COMPOSE_REPLY_TO_LIST)
7453 compose_remove_header_entries(compose);
7454 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7455 if (compose->account->set_autocc && compose->account->auto_cc)
7456 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7458 if (compose->account->set_autobcc && compose->account->auto_bcc)
7459 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7461 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7462 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7463 compose_show_first_last_header(compose, TRUE);
7464 compose->modified = was_modified;
7465 compose_set_title(compose);
7468 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7470 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7471 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7472 Compose *compose = (Compose *) data;
7475 compose_reply_change_mode(compose, value);
7478 static void compose_update_priority_menu_item(Compose * compose)
7480 GtkWidget *menuitem = NULL;
7481 switch (compose->priority) {
7482 case PRIORITY_HIGHEST:
7483 menuitem = gtk_ui_manager_get_widget
7484 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7487 menuitem = gtk_ui_manager_get_widget
7488 (compose->ui_manager, "/Menu/Options/Priority/High");
7490 case PRIORITY_NORMAL:
7491 menuitem = gtk_ui_manager_get_widget
7492 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7495 menuitem = gtk_ui_manager_get_widget
7496 (compose->ui_manager, "/Menu/Options/Priority/Low");
7498 case PRIORITY_LOWEST:
7499 menuitem = gtk_ui_manager_get_widget
7500 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7503 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7506 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7508 Compose *compose = (Compose *) data;
7510 gboolean can_sign = FALSE, can_encrypt = FALSE;
7512 g_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7514 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7517 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7518 g_free(compose->privacy_system);
7519 compose->privacy_system = NULL;
7520 if (systemid != NULL) {
7521 compose->privacy_system = g_strdup(systemid);
7523 can_sign = privacy_system_can_sign(systemid);
7524 can_encrypt = privacy_system_can_encrypt(systemid);
7527 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7529 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7530 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7533 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7535 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7536 GtkWidget *menuitem = NULL;
7538 gboolean can_sign = FALSE, can_encrypt = FALSE;
7539 gboolean found = FALSE;
7541 if (compose->privacy_system != NULL) {
7543 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7544 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7545 g_return_if_fail(menuitem != NULL);
7547 amenu = GTK_MENU_SHELL(menuitem)->children;
7549 while (amenu != NULL) {
7550 GList *alist = amenu->next;
7552 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7553 if (systemid != NULL) {
7554 if (strcmp(systemid, compose->privacy_system) == 0 &&
7555 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7556 menuitem = GTK_WIDGET(amenu->data);
7558 can_sign = privacy_system_can_sign(systemid);
7559 can_encrypt = privacy_system_can_encrypt(systemid);
7563 } else if (strlen(compose->privacy_system) == 0 &&
7564 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7565 menuitem = GTK_WIDGET(amenu->data);
7568 can_encrypt = FALSE;
7575 if (menuitem != NULL)
7576 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7578 if (warn && !found && strlen(compose->privacy_system)) {
7579 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7580 "will not be able to sign or encrypt this message."),
7581 compose->privacy_system);
7585 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7586 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7589 static void compose_set_out_encoding(Compose *compose)
7591 CharSet out_encoding;
7592 const gchar *branch = NULL;
7593 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7595 switch(out_encoding) {
7596 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7597 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7598 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7599 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7600 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7601 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7602 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7603 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7604 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7605 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7606 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7607 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7608 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7609 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7610 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7611 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7612 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7613 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7614 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7615 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7616 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7617 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7618 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7619 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7620 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7621 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7622 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7623 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7624 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7625 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7626 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7627 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7629 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7632 static void compose_set_template_menu(Compose *compose)
7634 GSList *tmpl_list, *cur;
7638 tmpl_list = template_get_config();
7640 menu = gtk_menu_new();
7642 gtk_menu_set_accel_group (GTK_MENU (menu),
7643 gtk_ui_manager_get_accel_group(compose->ui_manager));
7644 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7645 Template *tmpl = (Template *)cur->data;
7646 gchar *accel_path = NULL;
7647 item = gtk_menu_item_new_with_label(tmpl->name);
7648 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7649 g_signal_connect(G_OBJECT(item), "activate",
7650 G_CALLBACK(compose_template_activate_cb),
7652 g_object_set_data(G_OBJECT(item), "template", tmpl);
7653 gtk_widget_show(item);
7654 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7655 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7659 gtk_widget_show(menu);
7660 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7663 void compose_update_actions_menu(Compose *compose)
7665 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7668 static void compose_update_privacy_systems_menu(Compose *compose)
7670 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7671 GSList *systems, *cur;
7673 GtkWidget *system_none;
7675 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7676 GtkWidget *privacy_menu = gtk_menu_new();
7678 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7679 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7681 g_signal_connect(G_OBJECT(system_none), "activate",
7682 G_CALLBACK(compose_set_privacy_system_cb), compose);
7684 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7685 gtk_widget_show(system_none);
7687 systems = privacy_get_system_ids();
7688 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7689 gchar *systemid = cur->data;
7691 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7692 widget = gtk_radio_menu_item_new_with_label(group,
7693 privacy_system_get_name(systemid));
7694 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7695 g_strdup(systemid), g_free);
7696 g_signal_connect(G_OBJECT(widget), "activate",
7697 G_CALLBACK(compose_set_privacy_system_cb), compose);
7699 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7700 gtk_widget_show(widget);
7703 g_slist_free(systems);
7704 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7705 gtk_widget_show_all(privacy_menu);
7706 gtk_widget_show_all(privacy_menuitem);
7709 void compose_reflect_prefs_all(void)
7714 for (cur = compose_list; cur != NULL; cur = cur->next) {
7715 compose = (Compose *)cur->data;
7716 compose_set_template_menu(compose);
7720 void compose_reflect_prefs_pixmap_theme(void)
7725 for (cur = compose_list; cur != NULL; cur = cur->next) {
7726 compose = (Compose *)cur->data;
7727 toolbar_update(TOOLBAR_COMPOSE, compose);
7731 static const gchar *compose_quote_char_from_context(Compose *compose)
7733 const gchar *qmark = NULL;
7735 g_return_val_if_fail(compose != NULL, NULL);
7737 switch (compose->mode) {
7738 /* use forward-specific quote char */
7739 case COMPOSE_FORWARD:
7740 case COMPOSE_FORWARD_AS_ATTACH:
7741 case COMPOSE_FORWARD_INLINE:
7742 if (compose->folder && compose->folder->prefs &&
7743 compose->folder->prefs->forward_with_format)
7744 qmark = compose->folder->prefs->forward_quotemark;
7745 else if (compose->account->forward_with_format)
7746 qmark = compose->account->forward_quotemark;
7748 qmark = prefs_common.fw_quotemark;
7751 /* use reply-specific quote char in all other modes */
7753 if (compose->folder && compose->folder->prefs &&
7754 compose->folder->prefs->reply_with_format)
7755 qmark = compose->folder->prefs->reply_quotemark;
7756 else if (compose->account->reply_with_format)
7757 qmark = compose->account->reply_quotemark;
7759 qmark = prefs_common.quotemark;
7763 if (qmark == NULL || *qmark == '\0')
7769 static void compose_template_apply(Compose *compose, Template *tmpl,
7773 GtkTextBuffer *buffer;
7777 gchar *parsed_str = NULL;
7778 gint cursor_pos = 0;
7779 const gchar *err_msg = _("Template body format error at line %d.");
7782 /* process the body */
7784 text = GTK_TEXT_VIEW(compose->text);
7785 buffer = gtk_text_view_get_buffer(text);
7788 qmark = compose_quote_char_from_context(compose);
7790 if (compose->replyinfo != NULL) {
7793 gtk_text_buffer_set_text(buffer, "", -1);
7794 mark = gtk_text_buffer_get_insert(buffer);
7795 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7797 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7798 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7800 } else if (compose->fwdinfo != NULL) {
7803 gtk_text_buffer_set_text(buffer, "", -1);
7804 mark = gtk_text_buffer_get_insert(buffer);
7805 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7807 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7808 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7811 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7813 GtkTextIter start, end;
7816 gtk_text_buffer_get_start_iter(buffer, &start);
7817 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7818 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7820 /* clear the buffer now */
7822 gtk_text_buffer_set_text(buffer, "", -1);
7824 parsed_str = compose_quote_fmt(compose, dummyinfo,
7825 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7826 procmsg_msginfo_free( dummyinfo );
7832 gtk_text_buffer_set_text(buffer, "", -1);
7833 mark = gtk_text_buffer_get_insert(buffer);
7834 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7837 if (replace && parsed_str && compose->account->auto_sig)
7838 compose_insert_sig(compose, FALSE);
7840 if (replace && parsed_str) {
7841 gtk_text_buffer_get_start_iter(buffer, &iter);
7842 gtk_text_buffer_place_cursor(buffer, &iter);
7846 cursor_pos = quote_fmt_get_cursor_pos();
7847 compose->set_cursor_pos = cursor_pos;
7848 if (cursor_pos == -1)
7850 gtk_text_buffer_get_start_iter(buffer, &iter);
7851 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7852 gtk_text_buffer_place_cursor(buffer, &iter);
7855 /* process the other fields */
7857 compose_template_apply_fields(compose, tmpl);
7858 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7859 quote_fmt_reset_vartable();
7860 compose_changed_cb(NULL, compose);
7863 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7865 MsgInfo* dummyinfo = NULL;
7866 MsgInfo *msginfo = NULL;
7869 if (compose->replyinfo != NULL)
7870 msginfo = compose->replyinfo;
7871 else if (compose->fwdinfo != NULL)
7872 msginfo = compose->fwdinfo;
7874 dummyinfo = compose_msginfo_new_from_compose(compose);
7875 msginfo = dummyinfo;
7878 if (tmpl->from && *tmpl->from != '\0') {
7880 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7881 compose->gtkaspell);
7883 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7885 quote_fmt_scan_string(tmpl->from);
7888 buf = quote_fmt_get_buffer();
7890 alertpanel_error(_("Template From format error."));
7892 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
7896 if (tmpl->to && *tmpl->to != '\0') {
7898 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7899 compose->gtkaspell);
7901 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7903 quote_fmt_scan_string(tmpl->to);
7906 buf = quote_fmt_get_buffer();
7908 alertpanel_error(_("Template To format error."));
7910 compose_entry_append(compose, buf, COMPOSE_TO);
7914 if (tmpl->cc && *tmpl->cc != '\0') {
7916 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7917 compose->gtkaspell);
7919 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7921 quote_fmt_scan_string(tmpl->cc);
7924 buf = quote_fmt_get_buffer();
7926 alertpanel_error(_("Template Cc format error."));
7928 compose_entry_append(compose, buf, COMPOSE_CC);
7932 if (tmpl->bcc && *tmpl->bcc != '\0') {
7934 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7935 compose->gtkaspell);
7937 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7939 quote_fmt_scan_string(tmpl->bcc);
7942 buf = quote_fmt_get_buffer();
7944 alertpanel_error(_("Template Bcc format error."));
7946 compose_entry_append(compose, buf, COMPOSE_BCC);
7950 /* process the subject */
7951 if (tmpl->subject && *tmpl->subject != '\0') {
7953 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7954 compose->gtkaspell);
7956 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7958 quote_fmt_scan_string(tmpl->subject);
7961 buf = quote_fmt_get_buffer();
7963 alertpanel_error(_("Template subject format error."));
7965 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
7969 procmsg_msginfo_free( dummyinfo );
7972 static void compose_destroy(Compose *compose)
7974 GtkTextBuffer *buffer;
7975 GtkClipboard *clipboard;
7977 compose_list = g_list_remove(compose_list, compose);
7979 if (compose->updating) {
7980 debug_print("danger, not destroying anything now\n");
7981 compose->deferred_destroy = TRUE;
7984 /* NOTE: address_completion_end() does nothing with the window
7985 * however this may change. */
7986 address_completion_end(compose->window);
7988 slist_free_strings(compose->to_list);
7989 g_slist_free(compose->to_list);
7990 slist_free_strings(compose->newsgroup_list);
7991 g_slist_free(compose->newsgroup_list);
7992 slist_free_strings(compose->header_list);
7993 g_slist_free(compose->header_list);
7995 procmsg_msginfo_free(compose->targetinfo);
7996 procmsg_msginfo_free(compose->replyinfo);
7997 procmsg_msginfo_free(compose->fwdinfo);
7999 g_free(compose->replyto);
8000 g_free(compose->cc);
8001 g_free(compose->bcc);
8002 g_free(compose->newsgroups);
8003 g_free(compose->followup_to);
8005 g_free(compose->ml_post);
8007 g_free(compose->inreplyto);
8008 g_free(compose->references);
8009 g_free(compose->msgid);
8010 g_free(compose->boundary);
8012 g_free(compose->redirect_filename);
8013 if (compose->undostruct)
8014 undo_destroy(compose->undostruct);
8016 g_free(compose->sig_str);
8018 g_free(compose->exteditor_file);
8020 g_free(compose->orig_charset);
8022 g_free(compose->privacy_system);
8024 if (addressbook_get_target_compose() == compose)
8025 addressbook_set_target_compose(NULL);
8028 if (compose->gtkaspell) {
8029 gtkaspell_delete(compose->gtkaspell);
8030 compose->gtkaspell = NULL;
8034 if (!compose->batch) {
8035 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8036 prefs_common.compose_height = compose->window->allocation.height;
8039 if (!gtk_widget_get_parent(compose->paned))
8040 gtk_widget_destroy(compose->paned);
8041 gtk_widget_destroy(compose->popupmenu);
8043 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8044 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8045 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8047 gtk_widget_destroy(compose->window);
8048 toolbar_destroy(compose->toolbar);
8049 g_free(compose->toolbar);
8050 g_mutex_free(compose->mutex);
8054 static void compose_attach_info_free(AttachInfo *ainfo)
8056 g_free(ainfo->file);
8057 g_free(ainfo->content_type);
8058 g_free(ainfo->name);
8062 static void compose_attach_update_label(Compose *compose)
8067 GtkTreeModel *model;
8072 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8073 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8074 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8078 while(gtk_tree_model_iter_next(model, &iter))
8081 text = g_strdup_printf("(%d)", i);
8082 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8086 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8088 Compose *compose = (Compose *)data;
8089 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8090 GtkTreeSelection *selection;
8092 GtkTreeModel *model;
8094 selection = gtk_tree_view_get_selection(tree_view);
8095 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8100 for (cur = sel; cur != NULL; cur = cur->next) {
8101 GtkTreePath *path = cur->data;
8102 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8105 gtk_tree_path_free(path);
8108 for (cur = sel; cur != NULL; cur = cur->next) {
8109 GtkTreeRowReference *ref = cur->data;
8110 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8113 if (gtk_tree_model_get_iter(model, &iter, path))
8114 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8116 gtk_tree_path_free(path);
8117 gtk_tree_row_reference_free(ref);
8121 compose_attach_update_label(compose);
8124 static struct _AttachProperty
8127 GtkWidget *mimetype_entry;
8128 GtkWidget *encoding_optmenu;
8129 GtkWidget *path_entry;
8130 GtkWidget *filename_entry;
8132 GtkWidget *cancel_btn;
8135 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8137 gtk_tree_path_free((GtkTreePath *)ptr);
8140 static void compose_attach_property(GtkAction *action, gpointer data)
8142 Compose *compose = (Compose *)data;
8143 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8145 GtkComboBox *optmenu;
8146 GtkTreeSelection *selection;
8148 GtkTreeModel *model;
8151 static gboolean cancelled;
8153 /* only if one selected */
8154 selection = gtk_tree_view_get_selection(tree_view);
8155 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8158 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8162 path = (GtkTreePath *) sel->data;
8163 gtk_tree_model_get_iter(model, &iter, path);
8164 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8167 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8173 if (!attach_prop.window)
8174 compose_attach_property_create(&cancelled);
8175 gtk_widget_grab_focus(attach_prop.ok_btn);
8176 gtk_widget_show(attach_prop.window);
8177 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8179 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8180 if (ainfo->encoding == ENC_UNKNOWN)
8181 combobox_select_by_data(optmenu, ENC_BASE64);
8183 combobox_select_by_data(optmenu, ainfo->encoding);
8185 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8186 ainfo->content_type ? ainfo->content_type : "");
8187 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8188 ainfo->file ? ainfo->file : "");
8189 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8190 ainfo->name ? ainfo->name : "");
8193 const gchar *entry_text;
8195 gchar *cnttype = NULL;
8202 gtk_widget_hide(attach_prop.window);
8207 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8208 if (*entry_text != '\0') {
8211 text = g_strstrip(g_strdup(entry_text));
8212 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8213 cnttype = g_strdup(text);
8216 alertpanel_error(_("Invalid MIME type."));
8222 ainfo->encoding = combobox_get_active_data(optmenu);
8224 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8225 if (*entry_text != '\0') {
8226 if (is_file_exist(entry_text) &&
8227 (size = get_file_size(entry_text)) > 0)
8228 file = g_strdup(entry_text);
8231 (_("File doesn't exist or is empty."));
8237 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8238 if (*entry_text != '\0') {
8239 g_free(ainfo->name);
8240 ainfo->name = g_strdup(entry_text);
8244 g_free(ainfo->content_type);
8245 ainfo->content_type = cnttype;
8248 g_free(ainfo->file);
8252 ainfo->size = (goffset)size;
8254 /* update tree store */
8255 text = to_human_readable(ainfo->size);
8256 gtk_tree_model_get_iter(model, &iter, path);
8257 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8258 COL_MIMETYPE, ainfo->content_type,
8260 COL_NAME, ainfo->name,
8266 gtk_tree_path_free(path);
8269 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8271 label = gtk_label_new(str); \
8272 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8273 GTK_FILL, 0, 0, 0); \
8274 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8276 entry = gtk_entry_new(); \
8277 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8278 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8281 static void compose_attach_property_create(gboolean *cancelled)
8287 GtkWidget *mimetype_entry;
8290 GtkListStore *optmenu_menu;
8291 GtkWidget *path_entry;
8292 GtkWidget *filename_entry;
8295 GtkWidget *cancel_btn;
8296 GList *mime_type_list, *strlist;
8299 debug_print("Creating attach_property window...\n");
8301 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8302 gtk_widget_set_size_request(window, 480, -1);
8303 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8304 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8305 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8306 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8307 g_signal_connect(G_OBJECT(window), "delete_event",
8308 G_CALLBACK(attach_property_delete_event),
8310 g_signal_connect(G_OBJECT(window), "key_press_event",
8311 G_CALLBACK(attach_property_key_pressed),
8314 vbox = gtk_vbox_new(FALSE, 8);
8315 gtk_container_add(GTK_CONTAINER(window), vbox);
8317 table = gtk_table_new(4, 2, FALSE);
8318 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8319 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8320 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8322 label = gtk_label_new(_("MIME type"));
8323 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8325 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8326 mimetype_entry = gtk_combo_box_entry_new_text();
8327 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8328 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8330 /* stuff with list */
8331 mime_type_list = procmime_get_mime_type_list();
8333 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8334 MimeType *type = (MimeType *) mime_type_list->data;
8337 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8339 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8342 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8343 (GCompareFunc)strcmp2);
8346 for (mime_type_list = strlist; mime_type_list != NULL;
8347 mime_type_list = mime_type_list->next) {
8348 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8349 g_free(mime_type_list->data);
8351 g_list_free(strlist);
8352 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8353 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8355 label = gtk_label_new(_("Encoding"));
8356 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8358 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8360 hbox = gtk_hbox_new(FALSE, 0);
8361 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8362 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8364 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8365 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8367 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8368 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8369 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8370 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8371 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8373 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8375 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8376 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8378 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8379 &ok_btn, GTK_STOCK_OK,
8381 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8382 gtk_widget_grab_default(ok_btn);
8384 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8385 G_CALLBACK(attach_property_ok),
8387 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8388 G_CALLBACK(attach_property_cancel),
8391 gtk_widget_show_all(vbox);
8393 attach_prop.window = window;
8394 attach_prop.mimetype_entry = mimetype_entry;
8395 attach_prop.encoding_optmenu = optmenu;
8396 attach_prop.path_entry = path_entry;
8397 attach_prop.filename_entry = filename_entry;
8398 attach_prop.ok_btn = ok_btn;
8399 attach_prop.cancel_btn = cancel_btn;
8402 #undef SET_LABEL_AND_ENTRY
8404 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8410 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8416 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8417 gboolean *cancelled)
8425 static gboolean attach_property_key_pressed(GtkWidget *widget,
8427 gboolean *cancelled)
8429 if (event && event->keyval == GDK_Escape) {
8436 static void compose_exec_ext_editor(Compose *compose)
8443 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8444 G_DIR_SEPARATOR, compose);
8446 if (pipe(pipe_fds) < 0) {
8452 if ((pid = fork()) < 0) {
8459 /* close the write side of the pipe */
8462 compose->exteditor_file = g_strdup(tmp);
8463 compose->exteditor_pid = pid;
8465 compose_set_ext_editor_sensitive(compose, FALSE);
8467 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8468 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8472 } else { /* process-monitoring process */
8478 /* close the read side of the pipe */
8481 if (compose_write_body_to_file(compose, tmp) < 0) {
8482 fd_write_all(pipe_fds[1], "2\n", 2);
8486 pid_ed = compose_exec_ext_editor_real(tmp);
8488 fd_write_all(pipe_fds[1], "1\n", 2);
8492 /* wait until editor is terminated */
8493 waitpid(pid_ed, NULL, 0);
8495 fd_write_all(pipe_fds[1], "0\n", 2);
8502 #endif /* G_OS_UNIX */
8506 static gint compose_exec_ext_editor_real(const gchar *file)
8513 g_return_val_if_fail(file != NULL, -1);
8515 if ((pid = fork()) < 0) {
8520 if (pid != 0) return pid;
8522 /* grandchild process */
8524 if (setpgid(0, getppid()))
8527 if (prefs_common_get_ext_editor_cmd() &&
8528 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8529 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8530 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8532 if (prefs_common_get_ext_editor_cmd())
8533 g_warning("External editor command line is invalid: '%s'\n",
8534 prefs_common_get_ext_editor_cmd());
8535 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8538 cmdline = strsplit_with_quote(buf, " ", 1024);
8539 execvp(cmdline[0], cmdline);
8542 g_strfreev(cmdline);
8547 static gboolean compose_ext_editor_kill(Compose *compose)
8549 pid_t pgid = compose->exteditor_pid * -1;
8552 ret = kill(pgid, 0);
8554 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8558 msg = g_strdup_printf
8559 (_("The external editor is still working.\n"
8560 "Force terminating the process?\n"
8561 "process group id: %d"), -pgid);
8562 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8563 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8567 if (val == G_ALERTALTERNATE) {
8568 g_source_remove(compose->exteditor_tag);
8569 g_io_channel_shutdown(compose->exteditor_ch,
8571 g_io_channel_unref(compose->exteditor_ch);
8573 if (kill(pgid, SIGTERM) < 0) perror("kill");
8574 waitpid(compose->exteditor_pid, NULL, 0);
8576 g_warning("Terminated process group id: %d", -pgid);
8577 g_warning("Temporary file: %s",
8578 compose->exteditor_file);
8580 compose_set_ext_editor_sensitive(compose, TRUE);
8582 g_free(compose->exteditor_file);
8583 compose->exteditor_file = NULL;
8584 compose->exteditor_pid = -1;
8585 compose->exteditor_ch = NULL;
8586 compose->exteditor_tag = -1;
8594 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8598 Compose *compose = (Compose *)data;
8601 debug_print(_("Compose: input from monitoring process\n"));
8603 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8605 g_io_channel_shutdown(source, FALSE, NULL);
8606 g_io_channel_unref(source);
8608 waitpid(compose->exteditor_pid, NULL, 0);
8610 if (buf[0] == '0') { /* success */
8611 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8612 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8614 gtk_text_buffer_set_text(buffer, "", -1);
8615 compose_insert_file(compose, compose->exteditor_file);
8616 compose_changed_cb(NULL, compose);
8618 if (claws_unlink(compose->exteditor_file) < 0)
8619 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8620 } else if (buf[0] == '1') { /* failed */
8621 g_warning("Couldn't exec external editor\n");
8622 if (claws_unlink(compose->exteditor_file) < 0)
8623 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8624 } else if (buf[0] == '2') {
8625 g_warning("Couldn't write to file\n");
8626 } else if (buf[0] == '3') {
8627 g_warning("Pipe read failed\n");
8630 compose_set_ext_editor_sensitive(compose, TRUE);
8632 g_free(compose->exteditor_file);
8633 compose->exteditor_file = NULL;
8634 compose->exteditor_pid = -1;
8635 compose->exteditor_ch = NULL;
8636 compose->exteditor_tag = -1;
8641 static void compose_set_ext_editor_sensitive(Compose *compose,
8644 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8645 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8646 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8647 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8648 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8649 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8650 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8652 gtk_widget_set_sensitive(compose->text, sensitive);
8653 if (compose->toolbar->send_btn)
8654 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8655 if (compose->toolbar->sendl_btn)
8656 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8657 if (compose->toolbar->draft_btn)
8658 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8659 if (compose->toolbar->insert_btn)
8660 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8661 if (compose->toolbar->sig_btn)
8662 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8663 if (compose->toolbar->exteditor_btn)
8664 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8665 if (compose->toolbar->linewrap_current_btn)
8666 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8667 if (compose->toolbar->linewrap_all_btn)
8668 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8670 #endif /* G_OS_UNIX */
8673 * compose_undo_state_changed:
8675 * Change the sensivity of the menuentries undo and redo
8677 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8678 gint redo_state, gpointer data)
8680 Compose *compose = (Compose *)data;
8682 switch (undo_state) {
8683 case UNDO_STATE_TRUE:
8684 if (!undostruct->undo_state) {
8685 undostruct->undo_state = TRUE;
8686 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8689 case UNDO_STATE_FALSE:
8690 if (undostruct->undo_state) {
8691 undostruct->undo_state = FALSE;
8692 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8695 case UNDO_STATE_UNCHANGED:
8697 case UNDO_STATE_REFRESH:
8698 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8701 g_warning("Undo state not recognized");
8705 switch (redo_state) {
8706 case UNDO_STATE_TRUE:
8707 if (!undostruct->redo_state) {
8708 undostruct->redo_state = TRUE;
8709 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8712 case UNDO_STATE_FALSE:
8713 if (undostruct->redo_state) {
8714 undostruct->redo_state = FALSE;
8715 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8718 case UNDO_STATE_UNCHANGED:
8720 case UNDO_STATE_REFRESH:
8721 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8724 g_warning("Redo state not recognized");
8729 /* callback functions */
8731 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8732 * includes "non-client" (windows-izm) in calculation, so this calculation
8733 * may not be accurate.
8735 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8736 GtkAllocation *allocation,
8737 GtkSHRuler *shruler)
8739 if (prefs_common.show_ruler) {
8740 gint char_width = 0, char_height = 0;
8741 gint line_width_in_chars;
8743 gtkut_get_font_size(GTK_WIDGET(widget),
8744 &char_width, &char_height);
8745 line_width_in_chars =
8746 (allocation->width - allocation->x) / char_width;
8748 /* got the maximum */
8749 gtk_ruler_set_range(GTK_RULER(shruler),
8750 0.0, line_width_in_chars, 0,
8751 /*line_width_in_chars*/ char_width);
8757 static void account_activated(GtkComboBox *optmenu, gpointer data)
8759 Compose *compose = (Compose *)data;
8762 gchar *folderidentifier;
8763 gint account_id = 0;
8767 /* Get ID of active account in the combo box */
8768 menu = gtk_combo_box_get_model(optmenu);
8769 gtk_combo_box_get_active_iter(optmenu, &iter);
8770 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8772 ac = account_find_from_id(account_id);
8773 g_return_if_fail(ac != NULL);
8775 if (ac != compose->account)
8776 compose_select_account(compose, ac, FALSE);
8778 /* Set message save folder */
8779 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8780 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8782 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8783 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8785 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8786 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8787 folderidentifier = folder_item_get_identifier(account_get_special_folder
8788 (compose->account, F_OUTBOX));
8789 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
8790 g_free(folderidentifier);
8794 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8795 GtkTreeViewColumn *column, Compose *compose)
8797 compose_attach_property(NULL, compose);
8800 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8803 Compose *compose = (Compose *)data;
8804 GtkTreeSelection *attach_selection;
8805 gint attach_nr_selected;
8807 if (!event) return FALSE;
8809 if (event->button == 3) {
8810 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8811 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8813 if (attach_nr_selected > 0)
8815 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8816 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8818 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8819 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8822 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8823 NULL, NULL, event->button, event->time);
8830 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8833 Compose *compose = (Compose *)data;
8835 if (!event) return FALSE;
8837 switch (event->keyval) {
8839 compose_attach_remove_selected(NULL, compose);
8845 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8847 toolbar_comp_set_sensitive(compose, allow);
8848 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8849 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8851 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8853 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8854 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8855 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8857 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8861 static void compose_send_cb(GtkAction *action, gpointer data)
8863 Compose *compose = (Compose *)data;
8865 if (prefs_common.work_offline &&
8866 !inc_offline_should_override(TRUE,
8867 _("Claws Mail needs network access in order "
8868 "to send this email.")))
8871 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8872 g_source_remove(compose->draft_timeout_tag);
8873 compose->draft_timeout_tag = -1;
8876 compose_send(compose);
8879 static void compose_send_later_cb(GtkAction *action, gpointer data)
8881 Compose *compose = (Compose *)data;
8885 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8889 compose_close(compose);
8890 } else if (val == -1) {
8891 alertpanel_error(_("Could not queue message."));
8892 } else if (val == -2) {
8893 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8894 } else if (val == -3) {
8895 if (privacy_peek_error())
8896 alertpanel_error(_("Could not queue message for sending:\n\n"
8897 "Signature failed: %s"), privacy_get_error());
8898 } else if (val == -4) {
8899 alertpanel_error(_("Could not queue message for sending:\n\n"
8900 "Charset conversion failed."));
8901 } else if (val == -5) {
8902 alertpanel_error(_("Could not queue message for sending:\n\n"
8903 "Couldn't get recipient encryption key."));
8904 } else if (val == -6) {
8907 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
8910 #define DRAFTED_AT_EXIT "drafted_at_exit"
8911 static void compose_register_draft(MsgInfo *info)
8913 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8914 DRAFTED_AT_EXIT, NULL);
8915 FILE *fp = fopen(filepath, "ab");
8918 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
8926 gboolean compose_draft (gpointer data, guint action)
8928 Compose *compose = (Compose *)data;
8932 MsgFlags flag = {0, 0};
8933 static gboolean lock = FALSE;
8934 MsgInfo *newmsginfo;
8936 gboolean target_locked = FALSE;
8937 gboolean err = FALSE;
8939 if (lock) return FALSE;
8941 if (compose->sending)
8944 draft = account_get_special_folder(compose->account, F_DRAFT);
8945 g_return_val_if_fail(draft != NULL, FALSE);
8947 if (!g_mutex_trylock(compose->mutex)) {
8948 /* we don't want to lock the mutex once it's available,
8949 * because as the only other part of compose.c locking
8950 * it is compose_close - which means once unlocked,
8951 * the compose struct will be freed */
8952 debug_print("couldn't lock mutex, probably sending\n");
8958 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
8959 G_DIR_SEPARATOR, compose);
8960 if ((fp = g_fopen(tmp, "wb")) == NULL) {
8961 FILE_OP_ERROR(tmp, "fopen");
8965 /* chmod for security */
8966 if (change_file_mode_rw(fp, tmp) < 0) {
8967 FILE_OP_ERROR(tmp, "chmod");
8968 g_warning("can't change file mode\n");
8971 /* Save draft infos */
8972 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
8973 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
8975 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
8976 gchar *savefolderid;
8978 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8979 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
8980 g_free(savefolderid);
8982 if (compose->return_receipt) {
8983 err |= (fprintf(fp, "RRCPT:1\n") < 0);
8985 if (compose->privacy_system) {
8986 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
8987 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
8988 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
8991 /* Message-ID of message replying to */
8992 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
8995 folderid = folder_item_get_identifier(compose->replyinfo->folder);
8996 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
8999 /* Message-ID of message forwarding to */
9000 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9003 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9004 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9008 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9009 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9011 /* end of headers */
9012 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9019 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9023 if (fclose(fp) == EOF) {
9027 if (compose->targetinfo) {
9028 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9029 flag.perm_flags = target_locked?MSG_LOCKED:0;
9031 flag.tmp_flags = MSG_DRAFT;
9033 folder_item_scan(draft);
9034 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9035 MsgInfo *tmpinfo = NULL;
9036 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9037 if (compose->msgid) {
9038 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9041 msgnum = tmpinfo->msgnum;
9042 procmsg_msginfo_free(tmpinfo);
9043 debug_print("got draft msgnum %d from scanning\n", msgnum);
9045 debug_print("didn't get draft msgnum after scanning\n");
9048 debug_print("got draft msgnum %d from adding\n", msgnum);
9054 if (action != COMPOSE_AUTO_SAVE) {
9055 if (action != COMPOSE_DRAFT_FOR_EXIT)
9056 alertpanel_error(_("Could not save draft."));
9059 gtkut_window_popup(compose->window);
9060 val = alertpanel_full(_("Could not save draft"),
9061 _("Could not save draft.\n"
9062 "Do you want to cancel exit or discard this email?"),
9063 _("_Cancel exit"), _("_Discard email"), NULL,
9064 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9065 if (val == G_ALERTALTERNATE) {
9067 g_mutex_unlock(compose->mutex); /* must be done before closing */
9068 compose_close(compose);
9072 g_mutex_unlock(compose->mutex); /* must be done before closing */
9081 if (compose->mode == COMPOSE_REEDIT) {
9082 compose_remove_reedit_target(compose, TRUE);
9085 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9088 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9090 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9092 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9093 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9094 procmsg_msginfo_set_flags(newmsginfo, 0,
9095 MSG_HAS_ATTACHMENT);
9097 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9098 compose_register_draft(newmsginfo);
9100 procmsg_msginfo_free(newmsginfo);
9103 folder_item_scan(draft);
9105 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9107 g_mutex_unlock(compose->mutex); /* must be done before closing */
9108 compose_close(compose);
9114 path = folder_item_fetch_msg(draft, msgnum);
9116 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9119 if (g_stat(path, &s) < 0) {
9120 FILE_OP_ERROR(path, "stat");
9126 procmsg_msginfo_free(compose->targetinfo);
9127 compose->targetinfo = procmsg_msginfo_new();
9128 compose->targetinfo->msgnum = msgnum;
9129 compose->targetinfo->size = (goffset)s.st_size;
9130 compose->targetinfo->mtime = s.st_mtime;
9131 compose->targetinfo->folder = draft;
9133 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9134 compose->mode = COMPOSE_REEDIT;
9136 if (action == COMPOSE_AUTO_SAVE) {
9137 compose->autosaved_draft = compose->targetinfo;
9139 compose->modified = FALSE;
9140 compose_set_title(compose);
9144 g_mutex_unlock(compose->mutex);
9148 void compose_clear_exit_drafts(void)
9150 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9151 DRAFTED_AT_EXIT, NULL);
9152 if (is_file_exist(filepath))
9153 claws_unlink(filepath);
9158 void compose_reopen_exit_drafts(void)
9160 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9161 DRAFTED_AT_EXIT, NULL);
9162 FILE *fp = fopen(filepath, "rb");
9166 while (fgets(buf, sizeof(buf), fp)) {
9167 gchar **parts = g_strsplit(buf, "\t", 2);
9168 const gchar *folder = parts[0];
9169 int msgnum = parts[1] ? atoi(parts[1]):-1;
9171 if (folder && *folder && msgnum > -1) {
9172 FolderItem *item = folder_find_item_from_identifier(folder);
9173 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9175 compose_reedit(info, FALSE);
9182 compose_clear_exit_drafts();
9185 static void compose_save_cb(GtkAction *action, gpointer data)
9187 compose_draft(data, COMPOSE_KEEP_EDITING);
9190 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9192 if (compose && file_list) {
9195 for ( tmp = file_list; tmp; tmp = tmp->next) {
9196 gchar *file = (gchar *) tmp->data;
9197 gchar *utf8_filename = conv_filename_to_utf8(file);
9198 compose_attach_append(compose, file, utf8_filename, NULL);
9199 compose_changed_cb(NULL, compose);
9204 g_free(utf8_filename);
9209 static void compose_attach_cb(GtkAction *action, gpointer data)
9211 Compose *compose = (Compose *)data;
9214 if (compose->redirect_filename != NULL)
9217 file_list = filesel_select_multiple_files_open(_("Select file"));
9220 compose_attach_from_list(compose, file_list, TRUE);
9221 g_list_free(file_list);
9225 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9227 Compose *compose = (Compose *)data;
9230 file_list = filesel_select_multiple_files_open(_("Select file"));
9235 for ( tmp = file_list; tmp; tmp = tmp->next) {
9236 gchar *file = (gchar *) tmp->data;
9237 gchar *filedup = g_strdup(file);
9238 gchar *shortfile = g_path_get_basename(filedup);
9239 ComposeInsertResult res;
9241 res = compose_insert_file(compose, file);
9242 if (res == COMPOSE_INSERT_READ_ERROR) {
9243 alertpanel_error(_("File '%s' could not be read."), shortfile);
9244 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9245 alertpanel_error(_("File '%s' contained invalid characters\n"
9246 "for the current encoding, insertion may be incorrect."), shortfile);
9252 g_list_free(file_list);
9256 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9258 Compose *compose = (Compose *)data;
9260 compose_insert_sig(compose, FALSE);
9263 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9267 Compose *compose = (Compose *)data;
9269 gtkut_widget_get_uposition(widget, &x, &y);
9270 if (!compose->batch) {
9271 prefs_common.compose_x = x;
9272 prefs_common.compose_y = y;
9274 if (compose->sending || compose->updating)
9276 compose_close_cb(NULL, compose);
9280 void compose_close_toolbar(Compose *compose)
9282 compose_close_cb(NULL, compose);
9285 static void compose_close_cb(GtkAction *action, gpointer data)
9287 Compose *compose = (Compose *)data;
9291 if (compose->exteditor_tag != -1) {
9292 if (!compose_ext_editor_kill(compose))
9297 if (compose->modified) {
9298 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9299 if (!g_mutex_trylock(compose->mutex)) {
9300 /* we don't want to lock the mutex once it's available,
9301 * because as the only other part of compose.c locking
9302 * it is compose_close - which means once unlocked,
9303 * the compose struct will be freed */
9304 debug_print("couldn't lock mutex, probably sending\n");
9308 val = alertpanel(_("Discard message"),
9309 _("This message has been modified. Discard it?"),
9310 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9312 val = alertpanel(_("Save changes"),
9313 _("This message has been modified. Save the latest changes?"),
9314 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9316 g_mutex_unlock(compose->mutex);
9318 case G_ALERTDEFAULT:
9319 if (prefs_common.autosave && !reedit)
9320 compose_remove_draft(compose);
9322 case G_ALERTALTERNATE:
9323 compose_draft(data, COMPOSE_QUIT_EDITING);
9330 compose_close(compose);
9333 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9335 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9336 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9337 Compose *compose = (Compose *) data;
9340 compose->out_encoding = (CharSet)value;
9343 static void compose_address_cb(GtkAction *action, gpointer data)
9345 Compose *compose = (Compose *)data;
9347 addressbook_open(compose);
9350 static void about_show_cb(GtkAction *action, gpointer data)
9355 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9357 Compose *compose = (Compose *)data;
9362 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9363 g_return_if_fail(tmpl != NULL);
9365 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9367 val = alertpanel(_("Apply template"), msg,
9368 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9371 if (val == G_ALERTDEFAULT)
9372 compose_template_apply(compose, tmpl, TRUE);
9373 else if (val == G_ALERTALTERNATE)
9374 compose_template_apply(compose, tmpl, FALSE);
9377 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9379 Compose *compose = (Compose *)data;
9381 compose_exec_ext_editor(compose);
9384 static void compose_undo_cb(GtkAction *action, gpointer data)
9386 Compose *compose = (Compose *)data;
9387 gboolean prev_autowrap = compose->autowrap;
9389 compose->autowrap = FALSE;
9390 undo_undo(compose->undostruct);
9391 compose->autowrap = prev_autowrap;
9394 static void compose_redo_cb(GtkAction *action, gpointer data)
9396 Compose *compose = (Compose *)data;
9397 gboolean prev_autowrap = compose->autowrap;
9399 compose->autowrap = FALSE;
9400 undo_redo(compose->undostruct);
9401 compose->autowrap = prev_autowrap;
9404 static void entry_cut_clipboard(GtkWidget *entry)
9406 if (GTK_IS_EDITABLE(entry))
9407 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9408 else if (GTK_IS_TEXT_VIEW(entry))
9409 gtk_text_buffer_cut_clipboard(
9410 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9411 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9415 static void entry_copy_clipboard(GtkWidget *entry)
9417 if (GTK_IS_EDITABLE(entry))
9418 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9419 else if (GTK_IS_TEXT_VIEW(entry))
9420 gtk_text_buffer_copy_clipboard(
9421 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9422 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9425 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9426 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9428 if (GTK_IS_TEXT_VIEW(entry)) {
9429 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9430 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9431 GtkTextIter start_iter, end_iter;
9433 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9435 if (contents == NULL)
9438 /* we shouldn't delete the selection when middle-click-pasting, or we
9439 * can't mid-click-paste our own selection */
9440 if (clip != GDK_SELECTION_PRIMARY) {
9441 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9442 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9445 if (insert_place == NULL) {
9446 /* if insert_place isn't specified, insert at the cursor.
9447 * used for Ctrl-V pasting */
9448 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9449 start = gtk_text_iter_get_offset(&start_iter);
9450 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9452 /* if insert_place is specified, paste here.
9453 * used for mid-click-pasting */
9454 start = gtk_text_iter_get_offset(insert_place);
9455 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9459 /* paste unwrapped: mark the paste so it's not wrapped later */
9460 end = start + strlen(contents);
9461 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9462 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9463 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9464 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9465 /* rewrap paragraph now (after a mid-click-paste) */
9466 mark_start = gtk_text_buffer_get_insert(buffer);
9467 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9468 gtk_text_iter_backward_char(&start_iter);
9469 compose_beautify_paragraph(compose, &start_iter, TRUE);
9471 } else if (GTK_IS_EDITABLE(entry))
9472 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9474 compose->modified = TRUE;
9477 static void entry_allsel(GtkWidget *entry)
9479 if (GTK_IS_EDITABLE(entry))
9480 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9481 else if (GTK_IS_TEXT_VIEW(entry)) {
9482 GtkTextIter startiter, enditer;
9483 GtkTextBuffer *textbuf;
9485 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9486 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9487 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9489 gtk_text_buffer_move_mark_by_name(textbuf,
9490 "selection_bound", &startiter);
9491 gtk_text_buffer_move_mark_by_name(textbuf,
9492 "insert", &enditer);
9496 static void compose_cut_cb(GtkAction *action, gpointer data)
9498 Compose *compose = (Compose *)data;
9499 if (compose->focused_editable
9500 #ifndef GENERIC_UMPC
9501 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9504 entry_cut_clipboard(compose->focused_editable);
9507 static void compose_copy_cb(GtkAction *action, gpointer data)
9509 Compose *compose = (Compose *)data;
9510 if (compose->focused_editable
9511 #ifndef GENERIC_UMPC
9512 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9515 entry_copy_clipboard(compose->focused_editable);
9518 static void compose_paste_cb(GtkAction *action, gpointer data)
9520 Compose *compose = (Compose *)data;
9522 GtkTextBuffer *buffer;
9524 if (compose->focused_editable &&
9525 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9526 entry_paste_clipboard(compose, compose->focused_editable,
9527 prefs_common.linewrap_pastes,
9528 GDK_SELECTION_CLIPBOARD, NULL);
9532 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9534 Compose *compose = (Compose *)data;
9535 gint wrap_quote = prefs_common.linewrap_quote;
9536 if (compose->focused_editable
9537 #ifndef GENERIC_UMPC
9538 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9541 /* let text_insert() (called directly or at a later time
9542 * after the gtk_editable_paste_clipboard) know that
9543 * text is to be inserted as a quotation. implemented
9544 * by using a simple refcount... */
9545 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9546 G_OBJECT(compose->focused_editable),
9547 "paste_as_quotation"));
9548 g_object_set_data(G_OBJECT(compose->focused_editable),
9549 "paste_as_quotation",
9550 GINT_TO_POINTER(paste_as_quotation + 1));
9551 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9552 entry_paste_clipboard(compose, compose->focused_editable,
9553 prefs_common.linewrap_pastes,
9554 GDK_SELECTION_CLIPBOARD, NULL);
9555 prefs_common.linewrap_quote = wrap_quote;
9559 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9561 Compose *compose = (Compose *)data;
9563 GtkTextBuffer *buffer;
9565 if (compose->focused_editable
9566 #ifndef GENERIC_UMPC
9567 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9570 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9571 GDK_SELECTION_CLIPBOARD, NULL);
9575 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9577 Compose *compose = (Compose *)data;
9579 GtkTextBuffer *buffer;
9581 if (compose->focused_editable
9582 #ifndef GENERIC_UMPC
9583 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9586 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9587 GDK_SELECTION_CLIPBOARD, NULL);
9591 static void compose_allsel_cb(GtkAction *action, gpointer data)
9593 Compose *compose = (Compose *)data;
9594 if (compose->focused_editable
9595 #ifndef GENERIC_UMPC
9596 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9599 entry_allsel(compose->focused_editable);
9602 static void textview_move_beginning_of_line (GtkTextView *text)
9604 GtkTextBuffer *buffer;
9608 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9610 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9611 mark = gtk_text_buffer_get_insert(buffer);
9612 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9613 gtk_text_iter_set_line_offset(&ins, 0);
9614 gtk_text_buffer_place_cursor(buffer, &ins);
9617 static void textview_move_forward_character (GtkTextView *text)
9619 GtkTextBuffer *buffer;
9623 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9625 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9626 mark = gtk_text_buffer_get_insert(buffer);
9627 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9628 if (gtk_text_iter_forward_cursor_position(&ins))
9629 gtk_text_buffer_place_cursor(buffer, &ins);
9632 static void textview_move_backward_character (GtkTextView *text)
9634 GtkTextBuffer *buffer;
9638 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9640 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9641 mark = gtk_text_buffer_get_insert(buffer);
9642 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9643 if (gtk_text_iter_backward_cursor_position(&ins))
9644 gtk_text_buffer_place_cursor(buffer, &ins);
9647 static void textview_move_forward_word (GtkTextView *text)
9649 GtkTextBuffer *buffer;
9654 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9656 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9657 mark = gtk_text_buffer_get_insert(buffer);
9658 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9659 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9660 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9661 gtk_text_iter_backward_word_start(&ins);
9662 gtk_text_buffer_place_cursor(buffer, &ins);
9666 static void textview_move_backward_word (GtkTextView *text)
9668 GtkTextBuffer *buffer;
9673 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9675 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9676 mark = gtk_text_buffer_get_insert(buffer);
9677 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9678 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9679 if (gtk_text_iter_backward_word_starts(&ins, 1))
9680 gtk_text_buffer_place_cursor(buffer, &ins);
9683 static void textview_move_end_of_line (GtkTextView *text)
9685 GtkTextBuffer *buffer;
9689 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9691 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9692 mark = gtk_text_buffer_get_insert(buffer);
9693 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9694 if (gtk_text_iter_forward_to_line_end(&ins))
9695 gtk_text_buffer_place_cursor(buffer, &ins);
9698 static void textview_move_next_line (GtkTextView *text)
9700 GtkTextBuffer *buffer;
9705 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9707 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9708 mark = gtk_text_buffer_get_insert(buffer);
9709 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9710 offset = gtk_text_iter_get_line_offset(&ins);
9711 if (gtk_text_iter_forward_line(&ins)) {
9712 gtk_text_iter_set_line_offset(&ins, offset);
9713 gtk_text_buffer_place_cursor(buffer, &ins);
9717 static void textview_move_previous_line (GtkTextView *text)
9719 GtkTextBuffer *buffer;
9724 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9726 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9727 mark = gtk_text_buffer_get_insert(buffer);
9728 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9729 offset = gtk_text_iter_get_line_offset(&ins);
9730 if (gtk_text_iter_backward_line(&ins)) {
9731 gtk_text_iter_set_line_offset(&ins, offset);
9732 gtk_text_buffer_place_cursor(buffer, &ins);
9736 static void textview_delete_forward_character (GtkTextView *text)
9738 GtkTextBuffer *buffer;
9740 GtkTextIter ins, end_iter;
9742 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9744 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9745 mark = gtk_text_buffer_get_insert(buffer);
9746 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9748 if (gtk_text_iter_forward_char(&end_iter)) {
9749 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9753 static void textview_delete_backward_character (GtkTextView *text)
9755 GtkTextBuffer *buffer;
9757 GtkTextIter ins, end_iter;
9759 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9761 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9762 mark = gtk_text_buffer_get_insert(buffer);
9763 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9765 if (gtk_text_iter_backward_char(&end_iter)) {
9766 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9770 static void textview_delete_forward_word (GtkTextView *text)
9772 GtkTextBuffer *buffer;
9774 GtkTextIter ins, end_iter;
9776 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9778 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9779 mark = gtk_text_buffer_get_insert(buffer);
9780 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9782 if (gtk_text_iter_forward_word_end(&end_iter)) {
9783 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9787 static void textview_delete_backward_word (GtkTextView *text)
9789 GtkTextBuffer *buffer;
9791 GtkTextIter ins, end_iter;
9793 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9795 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9796 mark = gtk_text_buffer_get_insert(buffer);
9797 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9799 if (gtk_text_iter_backward_word_start(&end_iter)) {
9800 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9804 static void textview_delete_line (GtkTextView *text)
9806 GtkTextBuffer *buffer;
9808 GtkTextIter ins, start_iter, end_iter;
9810 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9812 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9813 mark = gtk_text_buffer_get_insert(buffer);
9814 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9817 gtk_text_iter_set_line_offset(&start_iter, 0);
9820 if (gtk_text_iter_ends_line(&end_iter)){
9821 if (!gtk_text_iter_forward_char(&end_iter))
9822 gtk_text_iter_backward_char(&start_iter);
9825 gtk_text_iter_forward_to_line_end(&end_iter);
9826 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9829 static void textview_delete_to_line_end (GtkTextView *text)
9831 GtkTextBuffer *buffer;
9833 GtkTextIter ins, end_iter;
9835 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9837 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9838 mark = gtk_text_buffer_get_insert(buffer);
9839 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9841 if (gtk_text_iter_ends_line(&end_iter))
9842 gtk_text_iter_forward_char(&end_iter);
9844 gtk_text_iter_forward_to_line_end(&end_iter);
9845 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9848 #define DO_ACTION(name, act) { \
9849 if(!strcmp(name, a_name)) { \
9853 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
9855 const gchar *a_name = gtk_action_get_name(action);
9856 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
9857 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
9858 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
9859 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
9860 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
9861 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
9862 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
9863 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
9864 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
9865 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
9866 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
9867 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
9868 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
9869 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
9873 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
9875 Compose *compose = (Compose *)data;
9876 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9877 ComposeCallAdvancedAction action = -1;
9879 action = compose_call_advanced_action_from_path(gaction);
9882 void (*do_action) (GtkTextView *text);
9883 } action_table[] = {
9884 {textview_move_beginning_of_line},
9885 {textview_move_forward_character},
9886 {textview_move_backward_character},
9887 {textview_move_forward_word},
9888 {textview_move_backward_word},
9889 {textview_move_end_of_line},
9890 {textview_move_next_line},
9891 {textview_move_previous_line},
9892 {textview_delete_forward_character},
9893 {textview_delete_backward_character},
9894 {textview_delete_forward_word},
9895 {textview_delete_backward_word},
9896 {textview_delete_line},
9897 {textview_delete_to_line_end}
9900 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
9902 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
9903 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
9904 if (action_table[action].do_action)
9905 action_table[action].do_action(text);
9907 g_warning("Not implemented yet.");
9911 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
9915 if (GTK_IS_EDITABLE(widget)) {
9916 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
9917 gtk_editable_set_position(GTK_EDITABLE(widget),
9920 if (widget->parent && widget->parent->parent
9921 && widget->parent->parent->parent) {
9922 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
9923 gint y = widget->allocation.y;
9924 gint height = widget->allocation.height;
9925 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
9926 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
9928 if (y < (int)shown->value) {
9929 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
9931 if (y + height > (int)shown->value + (int)shown->page_size) {
9932 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
9933 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9934 y + height - (int)shown->page_size - 1);
9936 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9937 (int)shown->upper - (int)shown->page_size - 1);
9944 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
9945 compose->focused_editable = widget;
9948 if (GTK_IS_TEXT_VIEW(widget)
9949 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
9950 g_object_ref(compose->notebook);
9951 g_object_ref(compose->edit_vbox);
9952 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9953 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9954 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
9955 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
9956 g_object_unref(compose->notebook);
9957 g_object_unref(compose->edit_vbox);
9958 g_signal_handlers_block_by_func(G_OBJECT(widget),
9959 G_CALLBACK(compose_grab_focus_cb),
9961 gtk_widget_grab_focus(widget);
9962 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9963 G_CALLBACK(compose_grab_focus_cb),
9965 } else if (!GTK_IS_TEXT_VIEW(widget)
9966 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
9967 g_object_ref(compose->notebook);
9968 g_object_ref(compose->edit_vbox);
9969 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9970 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9971 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
9972 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
9973 g_object_unref(compose->notebook);
9974 g_object_unref(compose->edit_vbox);
9975 g_signal_handlers_block_by_func(G_OBJECT(widget),
9976 G_CALLBACK(compose_grab_focus_cb),
9978 gtk_widget_grab_focus(widget);
9979 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9980 G_CALLBACK(compose_grab_focus_cb),
9986 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
9988 compose->modified = TRUE;
9989 #ifndef GENERIC_UMPC
9990 compose_set_title(compose);
9994 static void compose_wrap_cb(GtkAction *action, gpointer data)
9996 Compose *compose = (Compose *)data;
9997 compose_beautify_paragraph(compose, NULL, TRUE);
10000 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10002 Compose *compose = (Compose *)data;
10003 compose_wrap_all_full(compose, TRUE);
10006 static void compose_find_cb(GtkAction *action, gpointer data)
10008 Compose *compose = (Compose *)data;
10010 message_search_compose(compose);
10013 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10016 Compose *compose = (Compose *)data;
10017 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10018 if (compose->autowrap)
10019 compose_wrap_all_full(compose, TRUE);
10020 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10023 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10026 Compose *compose = (Compose *)data;
10027 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10030 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10032 Compose *compose = (Compose *)data;
10034 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10037 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10039 Compose *compose = (Compose *)data;
10041 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10044 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10046 g_free(compose->privacy_system);
10048 compose->privacy_system = g_strdup(account->default_privacy_system);
10049 compose_update_privacy_system_menu_item(compose, warn);
10052 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10054 Compose *compose = (Compose *)data;
10056 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10057 gtk_widget_show(compose->ruler_hbox);
10058 prefs_common.show_ruler = TRUE;
10060 gtk_widget_hide(compose->ruler_hbox);
10061 gtk_widget_queue_resize(compose->edit_vbox);
10062 prefs_common.show_ruler = FALSE;
10066 static void compose_attach_drag_received_cb (GtkWidget *widget,
10067 GdkDragContext *context,
10070 GtkSelectionData *data,
10073 gpointer user_data)
10075 Compose *compose = (Compose *)user_data;
10078 if (gdk_atom_name(data->type) &&
10079 !strcmp(gdk_atom_name(data->type), "text/uri-list")
10080 && gtk_drag_get_source_widget(context) !=
10081 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10082 list = uri_list_extract_filenames((const gchar *)data->data);
10083 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10084 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10085 compose_attach_append
10086 (compose, (const gchar *)tmp->data,
10087 utf8_filename, NULL);
10088 g_free(utf8_filename);
10090 if (list) compose_changed_cb(NULL, compose);
10091 list_free_strings(list);
10093 } else if (gtk_drag_get_source_widget(context)
10094 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10095 /* comes from our summaryview */
10096 SummaryView * summaryview = NULL;
10097 GSList * list = NULL, *cur = NULL;
10099 if (mainwindow_get_mainwindow())
10100 summaryview = mainwindow_get_mainwindow()->summaryview;
10103 list = summary_get_selected_msg_list(summaryview);
10105 for (cur = list; cur; cur = cur->next) {
10106 MsgInfo *msginfo = (MsgInfo *)cur->data;
10107 gchar *file = NULL;
10109 file = procmsg_get_message_file_full(msginfo,
10112 compose_attach_append(compose, (const gchar *)file,
10113 (const gchar *)file, "message/rfc822");
10117 g_slist_free(list);
10121 static gboolean compose_drag_drop(GtkWidget *widget,
10122 GdkDragContext *drag_context,
10124 guint time, gpointer user_data)
10126 /* not handling this signal makes compose_insert_drag_received_cb
10131 static void compose_insert_drag_received_cb (GtkWidget *widget,
10132 GdkDragContext *drag_context,
10135 GtkSelectionData *data,
10138 gpointer user_data)
10140 Compose *compose = (Compose *)user_data;
10143 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10145 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10146 AlertValue val = G_ALERTDEFAULT;
10148 list = uri_list_extract_filenames((const gchar *)data->data);
10150 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10151 /* Assume a list of no files, and data has ://, is a remote link */
10152 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10153 gchar *tmpfile = get_tmp_file();
10154 str_write_to_file(tmpdata, tmpfile);
10156 compose_insert_file(compose, tmpfile);
10157 claws_unlink(tmpfile);
10159 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10160 compose_beautify_paragraph(compose, NULL, TRUE);
10163 switch (prefs_common.compose_dnd_mode) {
10164 case COMPOSE_DND_ASK:
10165 val = alertpanel_full(_("Insert or attach?"),
10166 _("Do you want to insert the contents of the file(s) "
10167 "into the message body, or attach it to the email?"),
10168 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10169 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10171 case COMPOSE_DND_INSERT:
10172 val = G_ALERTALTERNATE;
10174 case COMPOSE_DND_ATTACH:
10175 val = G_ALERTOTHER;
10178 /* unexpected case */
10179 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10182 if (val & G_ALERTDISABLE) {
10183 val &= ~G_ALERTDISABLE;
10184 /* remember what action to perform by default, only if we don't click Cancel */
10185 if (val == G_ALERTALTERNATE)
10186 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10187 else if (val == G_ALERTOTHER)
10188 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10191 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10192 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10193 list_free_strings(list);
10196 } else if (val == G_ALERTOTHER) {
10197 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10198 list_free_strings(list);
10203 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10204 compose_insert_file(compose, (const gchar *)tmp->data);
10206 list_free_strings(list);
10208 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10211 #if GTK_CHECK_VERSION(2, 8, 0)
10212 /* do nothing, handled by GTK */
10214 gchar *tmpfile = get_tmp_file();
10215 str_write_to_file((const gchar *)data->data, tmpfile);
10216 compose_insert_file(compose, tmpfile);
10217 claws_unlink(tmpfile);
10219 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10223 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10226 static void compose_header_drag_received_cb (GtkWidget *widget,
10227 GdkDragContext *drag_context,
10230 GtkSelectionData *data,
10233 gpointer user_data)
10235 GtkEditable *entry = (GtkEditable *)user_data;
10236 gchar *email = (gchar *)data->data;
10238 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10241 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10242 gchar *decoded=g_new(gchar, strlen(email));
10245 email += strlen("mailto:");
10246 decode_uri(decoded, email); /* will fit */
10247 gtk_editable_delete_text(entry, 0, -1);
10248 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10249 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10253 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10256 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10258 Compose *compose = (Compose *)data;
10260 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10261 compose->return_receipt = TRUE;
10263 compose->return_receipt = FALSE;
10266 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10268 Compose *compose = (Compose *)data;
10270 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10271 compose->remove_references = TRUE;
10273 compose->remove_references = FALSE;
10276 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10277 GdkEventKey *event,
10278 ComposeHeaderEntry *headerentry)
10280 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10281 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10282 !(event->state & GDK_MODIFIER_MASK) &&
10283 (event->keyval == GDK_BackSpace) &&
10284 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10285 gtk_container_remove
10286 (GTK_CONTAINER(headerentry->compose->header_table),
10287 headerentry->combo);
10288 gtk_container_remove
10289 (GTK_CONTAINER(headerentry->compose->header_table),
10290 headerentry->entry);
10291 headerentry->compose->header_list =
10292 g_slist_remove(headerentry->compose->header_list,
10294 g_free(headerentry);
10295 } else if (event->keyval == GDK_Tab) {
10296 if (headerentry->compose->header_last == headerentry) {
10297 /* Override default next focus, and give it to subject_entry
10298 * instead of notebook tabs
10300 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10301 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10308 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10309 ComposeHeaderEntry *headerentry)
10311 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10312 compose_create_header_entry(headerentry->compose);
10313 g_signal_handlers_disconnect_matched
10314 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10315 0, 0, NULL, NULL, headerentry);
10317 /* Automatically scroll down */
10318 compose_show_first_last_header(headerentry->compose, FALSE);
10324 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10326 GtkAdjustment *vadj;
10328 g_return_if_fail(compose);
10329 g_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10330 g_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10332 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10333 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10334 gtk_adjustment_changed(vadj);
10337 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10338 const gchar *text, gint len, Compose *compose)
10340 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10341 (G_OBJECT(compose->text), "paste_as_quotation"));
10344 g_return_if_fail(text != NULL);
10346 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10347 G_CALLBACK(text_inserted),
10349 if (paste_as_quotation) {
10351 const gchar *qmark;
10353 GtkTextIter start_iter;
10356 len = strlen(text);
10358 new_text = g_strndup(text, len);
10360 qmark = compose_quote_char_from_context(compose);
10362 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10363 gtk_text_buffer_place_cursor(buffer, iter);
10365 pos = gtk_text_iter_get_offset(iter);
10367 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10368 _("Quote format error at line %d."));
10369 quote_fmt_reset_vartable();
10371 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10372 GINT_TO_POINTER(paste_as_quotation - 1));
10374 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10375 gtk_text_buffer_place_cursor(buffer, iter);
10376 gtk_text_buffer_delete_mark(buffer, mark);
10378 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10379 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10380 compose_beautify_paragraph(compose, &start_iter, FALSE);
10381 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10382 gtk_text_buffer_delete_mark(buffer, mark);
10384 if (strcmp(text, "\n") || compose->automatic_break
10385 || gtk_text_iter_starts_line(iter)) {
10386 GtkTextIter before_ins;
10387 gtk_text_buffer_insert(buffer, iter, text, len);
10388 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10389 before_ins = *iter;
10390 gtk_text_iter_backward_chars(&before_ins, len);
10391 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10394 /* check if the preceding is just whitespace or quote */
10395 GtkTextIter start_line;
10396 gchar *tmp = NULL, *quote = NULL;
10397 gint quote_len = 0, is_normal = 0;
10398 start_line = *iter;
10399 gtk_text_iter_set_line_offset(&start_line, 0);
10400 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10403 if (*tmp == '\0') {
10406 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10414 gtk_text_buffer_insert(buffer, iter, text, len);
10416 gtk_text_buffer_insert_with_tags_by_name(buffer,
10417 iter, text, len, "no_join", NULL);
10422 if (!paste_as_quotation) {
10423 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10424 compose_beautify_paragraph(compose, iter, FALSE);
10425 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10426 gtk_text_buffer_delete_mark(buffer, mark);
10429 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10430 G_CALLBACK(text_inserted),
10432 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10434 if (prefs_common.autosave &&
10435 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10436 compose->draft_timeout_tag != -2 /* disabled while loading */)
10437 compose->draft_timeout_tag = g_timeout_add
10438 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10440 static gint compose_defer_auto_save_draft(Compose *compose)
10442 compose->draft_timeout_tag = -1;
10443 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10448 static void compose_check_all(GtkAction *action, gpointer data)
10450 Compose *compose = (Compose *)data;
10451 if (compose->gtkaspell)
10452 gtkaspell_check_all(compose->gtkaspell);
10455 static void compose_highlight_all(GtkAction *action, gpointer data)
10457 Compose *compose = (Compose *)data;
10458 if (compose->gtkaspell)
10459 gtkaspell_highlight_all(compose->gtkaspell);
10462 static void compose_check_backwards(GtkAction *action, gpointer data)
10464 Compose *compose = (Compose *)data;
10465 if (compose->gtkaspell)
10466 gtkaspell_check_backwards(compose->gtkaspell);
10468 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10472 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10474 Compose *compose = (Compose *)data;
10475 if (compose->gtkaspell)
10476 gtkaspell_check_forwards_go(compose->gtkaspell);
10478 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10484 *\brief Guess originating forward account from MsgInfo and several
10485 * "common preference" settings. Return NULL if no guess.
10487 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10489 PrefsAccount *account = NULL;
10491 g_return_val_if_fail(msginfo, NULL);
10492 g_return_val_if_fail(msginfo->folder, NULL);
10493 g_return_val_if_fail(msginfo->folder->prefs, NULL);
10495 if (msginfo->folder->prefs->enable_default_account)
10496 account = account_find_from_id(msginfo->folder->prefs->default_account);
10499 account = msginfo->folder->folder->account;
10501 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10503 Xstrdup_a(to, msginfo->to, return NULL);
10504 extract_address(to);
10505 account = account_find_from_address(to, FALSE);
10508 if (!account && prefs_common.forward_account_autosel) {
10509 gchar cc[BUFFSIZE];
10510 if (!procheader_get_header_from_msginfo
10511 (msginfo, cc,sizeof cc , "Cc:")) {
10512 gchar *buf = cc + strlen("Cc:");
10513 extract_address(buf);
10514 account = account_find_from_address(buf, FALSE);
10518 if (!account && prefs_common.forward_account_autosel) {
10519 gchar deliveredto[BUFFSIZE];
10520 if (!procheader_get_header_from_msginfo
10521 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10522 gchar *buf = deliveredto + strlen("Delivered-To:");
10523 extract_address(buf);
10524 account = account_find_from_address(buf, FALSE);
10531 gboolean compose_close(Compose *compose)
10535 if (!g_mutex_trylock(compose->mutex)) {
10536 /* we have to wait for the (possibly deferred by auto-save)
10537 * drafting to be done, before destroying the compose under
10539 debug_print("waiting for drafting to finish...\n");
10540 compose_allow_user_actions(compose, FALSE);
10541 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10544 g_return_val_if_fail(compose, FALSE);
10545 gtkut_widget_get_uposition(compose->window, &x, &y);
10546 if (!compose->batch) {
10547 prefs_common.compose_x = x;
10548 prefs_common.compose_y = y;
10550 g_mutex_unlock(compose->mutex);
10551 compose_destroy(compose);
10556 * Add entry field for each address in list.
10557 * \param compose E-Mail composition object.
10558 * \param listAddress List of (formatted) E-Mail addresses.
10560 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10563 node = listAddress;
10565 addr = ( gchar * ) node->data;
10566 compose_entry_append( compose, addr, COMPOSE_TO );
10567 node = g_list_next( node );
10571 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10572 guint action, gboolean opening_multiple)
10574 gchar *body = NULL;
10575 GSList *new_msglist = NULL;
10576 MsgInfo *tmp_msginfo = NULL;
10577 gboolean originally_enc = FALSE;
10578 gboolean originally_sig = FALSE;
10579 Compose *compose = NULL;
10581 g_return_if_fail(msgview != NULL);
10583 g_return_if_fail(msginfo_list != NULL);
10585 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10586 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10587 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10589 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10590 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10591 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10592 orig_msginfo, mimeinfo);
10593 if (tmp_msginfo != NULL) {
10594 new_msglist = g_slist_append(NULL, tmp_msginfo);
10596 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10597 privacy_msginfo_get_signed_state(orig_msginfo);
10598 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
10600 tmp_msginfo->folder = orig_msginfo->folder;
10601 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10602 if (orig_msginfo->tags)
10603 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10608 if (!opening_multiple)
10609 body = messageview_get_selection(msgview);
10612 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10613 procmsg_msginfo_free(tmp_msginfo);
10614 g_slist_free(new_msglist);
10616 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10618 if (compose && originally_enc) {
10619 compose_force_encryption(compose, compose->account, FALSE);
10622 if (compose && originally_sig && compose->account->default_sign_reply) {
10623 compose_force_signing(compose, compose->account);
10629 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10632 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
10633 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10634 GSList *cur = msginfo_list;
10635 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10636 "messages. Opening the windows "
10637 "could take some time. Do you "
10638 "want to continue?"),
10639 g_slist_length(msginfo_list));
10640 if (g_slist_length(msginfo_list) > 9
10641 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10642 != G_ALERTALTERNATE) {
10647 /* We'll open multiple compose windows */
10648 /* let the WM place the next windows */
10649 compose_force_window_origin = FALSE;
10650 for (; cur; cur = cur->next) {
10652 tmplist.data = cur->data;
10653 tmplist.next = NULL;
10654 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10656 compose_force_window_origin = TRUE;
10658 /* forwarding multiple mails as attachments is done via a
10659 * single compose window */
10660 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10664 void compose_set_position(Compose *compose, gint pos)
10666 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10668 gtkut_text_view_set_position(text, pos);
10671 gboolean compose_search_string(Compose *compose,
10672 const gchar *str, gboolean case_sens)
10674 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10676 return gtkut_text_view_search_string(text, str, case_sens);
10679 gboolean compose_search_string_backward(Compose *compose,
10680 const gchar *str, gboolean case_sens)
10682 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10684 return gtkut_text_view_search_string_backward(text, str, case_sens);
10687 /* allocate a msginfo structure and populate its data from a compose data structure */
10688 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10690 MsgInfo *newmsginfo;
10692 gchar buf[BUFFSIZE];
10694 g_return_val_if_fail( compose != NULL, NULL );
10696 newmsginfo = procmsg_msginfo_new();
10699 get_rfc822_date(buf, sizeof(buf));
10700 newmsginfo->date = g_strdup(buf);
10703 if (compose->from_name) {
10704 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10705 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10709 if (compose->subject_entry)
10710 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10712 /* to, cc, reply-to, newsgroups */
10713 for (list = compose->header_list; list; list = list->next) {
10714 gchar *header = gtk_editable_get_chars(
10716 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
10717 gchar *entry = gtk_editable_get_chars(
10718 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10720 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10721 if ( newmsginfo->to == NULL ) {
10722 newmsginfo->to = g_strdup(entry);
10723 } else if (entry && *entry) {
10724 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10725 g_free(newmsginfo->to);
10726 newmsginfo->to = tmp;
10729 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10730 if ( newmsginfo->cc == NULL ) {
10731 newmsginfo->cc = g_strdup(entry);
10732 } else if (entry && *entry) {
10733 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10734 g_free(newmsginfo->cc);
10735 newmsginfo->cc = tmp;
10738 if ( strcasecmp(header,
10739 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10740 if ( newmsginfo->newsgroups == NULL ) {
10741 newmsginfo->newsgroups = g_strdup(entry);
10742 } else if (entry && *entry) {
10743 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10744 g_free(newmsginfo->newsgroups);
10745 newmsginfo->newsgroups = tmp;
10753 /* other data is unset */
10759 /* update compose's dictionaries from folder dict settings */
10760 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10761 FolderItem *folder_item)
10763 g_return_if_fail(compose != NULL);
10765 if (compose->gtkaspell && folder_item && folder_item->prefs) {
10766 FolderItemPrefs *prefs = folder_item->prefs;
10768 if (prefs->enable_default_dictionary)
10769 gtkaspell_change_dict(compose->gtkaspell,
10770 prefs->default_dictionary, FALSE);
10771 if (folder_item->prefs->enable_default_alt_dictionary)
10772 gtkaspell_change_alt_dict(compose->gtkaspell,
10773 prefs->default_alt_dictionary);
10774 if (prefs->enable_default_dictionary
10775 || prefs->enable_default_alt_dictionary)
10776 compose_spell_menu_changed(compose);