2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2008 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_("Auto _indent"), 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.dictionary,
7248 prefs_common.alt_dictionary,
7249 conv_get_locale_charset_str(),
7250 prefs_common.misspelled_col,
7251 prefs_common.check_while_typing,
7252 prefs_common.recheck_when_changing_dict,
7253 prefs_common.use_alternate,
7254 prefs_common.use_both_dicts,
7255 GTK_TEXT_VIEW(text),
7256 GTK_WINDOW(compose->window),
7257 compose_spell_menu_changed,
7260 alertpanel_error(_("Spell checker could not "
7262 gtkaspell_checkers_strerror());
7263 gtkaspell_checkers_reset_error();
7265 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7269 compose->gtkaspell = gtkaspell;
7270 compose_spell_menu_changed(compose);
7273 compose_select_account(compose, account, TRUE);
7275 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7276 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7278 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7279 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7281 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7282 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7284 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7285 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7287 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7288 if (account->protocol != A_NNTP)
7289 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7290 prefs_common_translated_header_name("To:"));
7292 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7293 prefs_common_translated_header_name("Newsgroups:"));
7295 addressbook_set_target_compose(compose);
7297 if (mode != COMPOSE_REDIRECT)
7298 compose_set_template_menu(compose);
7300 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7303 compose_list = g_list_append(compose_list, compose);
7305 if (!prefs_common.show_ruler)
7306 gtk_widget_hide(ruler_hbox);
7308 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7311 compose->priority = PRIORITY_NORMAL;
7312 compose_update_priority_menu_item(compose);
7314 compose_set_out_encoding(compose);
7317 compose_update_actions_menu(compose);
7319 /* Privacy Systems menu */
7320 compose_update_privacy_systems_menu(compose);
7322 activate_privacy_system(compose, account, TRUE);
7323 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7325 gtk_widget_realize(window);
7327 gtk_widget_show(window);
7329 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7330 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7337 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7342 GtkWidget *optmenubox;
7345 GtkWidget *from_name = NULL;
7346 #if !(GTK_CHECK_VERSION(2,12,0))
7347 GtkTooltips *tips = compose->tooltips;
7350 gint num = 0, def_menu = 0;
7352 accounts = account_get_list();
7353 g_return_val_if_fail(accounts != NULL, NULL);
7355 optmenubox = gtk_event_box_new();
7356 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7357 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7359 hbox = gtk_hbox_new(FALSE, 6);
7360 from_name = gtk_entry_new();
7362 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7363 G_CALLBACK(compose_grab_focus_cb), compose);
7365 for (; accounts != NULL; accounts = accounts->next, num++) {
7366 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7367 gchar *name, *from = NULL;
7369 if (ac == compose->account) def_menu = num;
7371 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7374 if (ac == compose->account) {
7375 if (ac->name && *ac->name) {
7377 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7378 from = g_strdup_printf("%s <%s>",
7380 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7382 from = g_strdup_printf("%s",
7384 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7387 COMBOBOX_ADD(menu, name, ac->account_id);
7392 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7394 g_signal_connect(G_OBJECT(optmenu), "changed",
7395 G_CALLBACK(account_activated),
7397 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7398 G_CALLBACK(compose_entry_popup_extend),
7401 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7402 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7404 CLAWS_SET_TIP(optmenubox,
7405 _("Account to use for this email"));
7406 CLAWS_SET_TIP(from_name,
7407 _("Sender address to be used"));
7409 compose->from_name = from_name;
7414 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7416 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7417 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7418 Compose *compose = (Compose *) data;
7420 compose->priority = value;
7424 static void compose_reply_change_mode(Compose *compose,
7427 gboolean was_modified = compose->modified;
7429 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7431 g_return_if_fail(compose->replyinfo != NULL);
7433 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7435 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7437 if (action == COMPOSE_REPLY_TO_ALL)
7439 if (action == COMPOSE_REPLY_TO_SENDER)
7441 if (action == COMPOSE_REPLY_TO_LIST)
7444 compose_remove_header_entries(compose);
7445 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7446 if (compose->account->set_autocc && compose->account->auto_cc)
7447 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7449 if (compose->account->set_autobcc && compose->account->auto_bcc)
7450 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7452 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7453 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7454 compose_show_first_last_header(compose, TRUE);
7455 compose->modified = was_modified;
7456 compose_set_title(compose);
7459 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7461 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7462 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7463 Compose *compose = (Compose *) data;
7466 compose_reply_change_mode(compose, value);
7469 static void compose_update_priority_menu_item(Compose * compose)
7471 GtkWidget *menuitem = NULL;
7472 switch (compose->priority) {
7473 case PRIORITY_HIGHEST:
7474 menuitem = gtk_ui_manager_get_widget
7475 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7478 menuitem = gtk_ui_manager_get_widget
7479 (compose->ui_manager, "/Menu/Options/Priority/High");
7481 case PRIORITY_NORMAL:
7482 menuitem = gtk_ui_manager_get_widget
7483 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7486 menuitem = gtk_ui_manager_get_widget
7487 (compose->ui_manager, "/Menu/Options/Priority/Low");
7489 case PRIORITY_LOWEST:
7490 menuitem = gtk_ui_manager_get_widget
7491 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7494 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7497 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7499 Compose *compose = (Compose *) data;
7501 gboolean can_sign = FALSE, can_encrypt = FALSE;
7503 g_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7505 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7508 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7509 g_free(compose->privacy_system);
7510 compose->privacy_system = NULL;
7511 if (systemid != NULL) {
7512 compose->privacy_system = g_strdup(systemid);
7514 can_sign = privacy_system_can_sign(systemid);
7515 can_encrypt = privacy_system_can_encrypt(systemid);
7518 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7520 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7521 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7524 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7526 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7527 GtkWidget *menuitem = NULL;
7529 gboolean can_sign = FALSE, can_encrypt = FALSE;
7530 gboolean found = FALSE;
7532 if (compose->privacy_system != NULL) {
7534 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7535 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7536 g_return_if_fail(menuitem != NULL);
7538 amenu = GTK_MENU_SHELL(menuitem)->children;
7540 while (amenu != NULL) {
7541 GList *alist = amenu->next;
7543 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7544 if (systemid != NULL) {
7545 if (strcmp(systemid, compose->privacy_system) == 0 &&
7546 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7547 menuitem = GTK_WIDGET(amenu->data);
7549 can_sign = privacy_system_can_sign(systemid);
7550 can_encrypt = privacy_system_can_encrypt(systemid);
7554 } else if (strlen(compose->privacy_system) == 0 &&
7555 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7556 menuitem = GTK_WIDGET(amenu->data);
7559 can_encrypt = FALSE;
7566 if (menuitem != NULL)
7567 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7569 if (warn && !found && strlen(compose->privacy_system)) {
7570 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7571 "will not be able to sign or encrypt this message."),
7572 compose->privacy_system);
7576 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7577 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7580 static void compose_set_out_encoding(Compose *compose)
7582 CharSet out_encoding;
7583 const gchar *branch = NULL;
7584 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7586 switch(out_encoding) {
7587 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7588 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7589 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7590 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7591 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7592 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7593 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7594 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7595 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7596 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7597 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7598 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7599 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7600 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7601 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7602 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7603 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7604 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7605 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7606 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7607 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7608 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7609 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7610 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7611 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7612 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7613 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7614 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7615 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7616 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7617 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7618 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7620 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7623 static void compose_set_template_menu(Compose *compose)
7625 GSList *tmpl_list, *cur;
7629 tmpl_list = template_get_config();
7631 menu = gtk_menu_new();
7633 gtk_menu_set_accel_group (GTK_MENU (menu),
7634 gtk_ui_manager_get_accel_group(compose->ui_manager));
7635 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7636 Template *tmpl = (Template *)cur->data;
7637 gchar *accel_path = NULL;
7638 item = gtk_menu_item_new_with_label(tmpl->name);
7639 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7640 g_signal_connect(G_OBJECT(item), "activate",
7641 G_CALLBACK(compose_template_activate_cb),
7643 g_object_set_data(G_OBJECT(item), "template", tmpl);
7644 gtk_widget_show(item);
7645 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7646 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7650 gtk_widget_show(menu);
7651 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7654 void compose_update_actions_menu(Compose *compose)
7656 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7659 static void compose_update_privacy_systems_menu(Compose *compose)
7661 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7662 GSList *systems, *cur;
7664 GtkWidget *system_none;
7666 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7667 GtkWidget *privacy_menu = gtk_menu_new();
7669 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7670 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7672 g_signal_connect(G_OBJECT(system_none), "activate",
7673 G_CALLBACK(compose_set_privacy_system_cb), compose);
7675 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7676 gtk_widget_show(system_none);
7678 systems = privacy_get_system_ids();
7679 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7680 gchar *systemid = cur->data;
7682 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7683 widget = gtk_radio_menu_item_new_with_label(group,
7684 privacy_system_get_name(systemid));
7685 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7686 g_strdup(systemid), g_free);
7687 g_signal_connect(G_OBJECT(widget), "activate",
7688 G_CALLBACK(compose_set_privacy_system_cb), compose);
7690 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7691 gtk_widget_show(widget);
7694 g_slist_free(systems);
7695 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7696 gtk_widget_show_all(privacy_menu);
7697 gtk_widget_show_all(privacy_menuitem);
7700 void compose_reflect_prefs_all(void)
7705 for (cur = compose_list; cur != NULL; cur = cur->next) {
7706 compose = (Compose *)cur->data;
7707 compose_set_template_menu(compose);
7711 void compose_reflect_prefs_pixmap_theme(void)
7716 for (cur = compose_list; cur != NULL; cur = cur->next) {
7717 compose = (Compose *)cur->data;
7718 toolbar_update(TOOLBAR_COMPOSE, compose);
7722 static const gchar *compose_quote_char_from_context(Compose *compose)
7724 const gchar *qmark = NULL;
7726 g_return_val_if_fail(compose != NULL, NULL);
7728 switch (compose->mode) {
7729 /* use forward-specific quote char */
7730 case COMPOSE_FORWARD:
7731 case COMPOSE_FORWARD_AS_ATTACH:
7732 case COMPOSE_FORWARD_INLINE:
7733 if (compose->folder && compose->folder->prefs &&
7734 compose->folder->prefs->forward_with_format)
7735 qmark = compose->folder->prefs->forward_quotemark;
7736 else if (compose->account->forward_with_format)
7737 qmark = compose->account->forward_quotemark;
7739 qmark = prefs_common.fw_quotemark;
7742 /* use reply-specific quote char in all other modes */
7744 if (compose->folder && compose->folder->prefs &&
7745 compose->folder->prefs->reply_with_format)
7746 qmark = compose->folder->prefs->reply_quotemark;
7747 else if (compose->account->reply_with_format)
7748 qmark = compose->account->reply_quotemark;
7750 qmark = prefs_common.quotemark;
7754 if (qmark == NULL || *qmark == '\0')
7760 static void compose_template_apply(Compose *compose, Template *tmpl,
7764 GtkTextBuffer *buffer;
7768 gchar *parsed_str = NULL;
7769 gint cursor_pos = 0;
7770 const gchar *err_msg = _("Template body format error at line %d.");
7773 /* process the body */
7775 text = GTK_TEXT_VIEW(compose->text);
7776 buffer = gtk_text_view_get_buffer(text);
7779 qmark = compose_quote_char_from_context(compose);
7781 if (compose->replyinfo != NULL) {
7784 gtk_text_buffer_set_text(buffer, "", -1);
7785 mark = gtk_text_buffer_get_insert(buffer);
7786 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7788 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7789 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7791 } else if (compose->fwdinfo != NULL) {
7794 gtk_text_buffer_set_text(buffer, "", -1);
7795 mark = gtk_text_buffer_get_insert(buffer);
7796 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7798 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7799 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7802 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7804 GtkTextIter start, end;
7807 gtk_text_buffer_get_start_iter(buffer, &start);
7808 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7809 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7811 /* clear the buffer now */
7813 gtk_text_buffer_set_text(buffer, "", -1);
7815 parsed_str = compose_quote_fmt(compose, dummyinfo,
7816 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7817 procmsg_msginfo_free( dummyinfo );
7823 gtk_text_buffer_set_text(buffer, "", -1);
7824 mark = gtk_text_buffer_get_insert(buffer);
7825 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7828 if (replace && parsed_str && compose->account->auto_sig)
7829 compose_insert_sig(compose, FALSE);
7831 if (replace && parsed_str) {
7832 gtk_text_buffer_get_start_iter(buffer, &iter);
7833 gtk_text_buffer_place_cursor(buffer, &iter);
7837 cursor_pos = quote_fmt_get_cursor_pos();
7838 compose->set_cursor_pos = cursor_pos;
7839 if (cursor_pos == -1)
7841 gtk_text_buffer_get_start_iter(buffer, &iter);
7842 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7843 gtk_text_buffer_place_cursor(buffer, &iter);
7846 /* process the other fields */
7848 compose_template_apply_fields(compose, tmpl);
7849 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7850 quote_fmt_reset_vartable();
7851 compose_changed_cb(NULL, compose);
7854 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7856 MsgInfo* dummyinfo = NULL;
7857 MsgInfo *msginfo = NULL;
7860 if (compose->replyinfo != NULL)
7861 msginfo = compose->replyinfo;
7862 else if (compose->fwdinfo != NULL)
7863 msginfo = compose->fwdinfo;
7865 dummyinfo = compose_msginfo_new_from_compose(compose);
7866 msginfo = dummyinfo;
7869 if (tmpl->from && *tmpl->from != '\0') {
7871 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7872 compose->gtkaspell);
7874 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7876 quote_fmt_scan_string(tmpl->from);
7879 buf = quote_fmt_get_buffer();
7881 alertpanel_error(_("Template From format error."));
7883 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
7887 if (tmpl->to && *tmpl->to != '\0') {
7889 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7890 compose->gtkaspell);
7892 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7894 quote_fmt_scan_string(tmpl->to);
7897 buf = quote_fmt_get_buffer();
7899 alertpanel_error(_("Template To format error."));
7901 compose_entry_append(compose, buf, COMPOSE_TO);
7905 if (tmpl->cc && *tmpl->cc != '\0') {
7907 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7908 compose->gtkaspell);
7910 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7912 quote_fmt_scan_string(tmpl->cc);
7915 buf = quote_fmt_get_buffer();
7917 alertpanel_error(_("Template Cc format error."));
7919 compose_entry_append(compose, buf, COMPOSE_CC);
7923 if (tmpl->bcc && *tmpl->bcc != '\0') {
7925 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7926 compose->gtkaspell);
7928 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7930 quote_fmt_scan_string(tmpl->bcc);
7933 buf = quote_fmt_get_buffer();
7935 alertpanel_error(_("Template Bcc format error."));
7937 compose_entry_append(compose, buf, COMPOSE_BCC);
7941 /* process the subject */
7942 if (tmpl->subject && *tmpl->subject != '\0') {
7944 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7945 compose->gtkaspell);
7947 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7949 quote_fmt_scan_string(tmpl->subject);
7952 buf = quote_fmt_get_buffer();
7954 alertpanel_error(_("Template subject format error."));
7956 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
7960 procmsg_msginfo_free( dummyinfo );
7963 static void compose_destroy(Compose *compose)
7965 GtkTextBuffer *buffer;
7966 GtkClipboard *clipboard;
7968 compose_list = g_list_remove(compose_list, compose);
7970 if (compose->updating) {
7971 debug_print("danger, not destroying anything now\n");
7972 compose->deferred_destroy = TRUE;
7975 /* NOTE: address_completion_end() does nothing with the window
7976 * however this may change. */
7977 address_completion_end(compose->window);
7979 slist_free_strings(compose->to_list);
7980 g_slist_free(compose->to_list);
7981 slist_free_strings(compose->newsgroup_list);
7982 g_slist_free(compose->newsgroup_list);
7983 slist_free_strings(compose->header_list);
7984 g_slist_free(compose->header_list);
7986 procmsg_msginfo_free(compose->targetinfo);
7987 procmsg_msginfo_free(compose->replyinfo);
7988 procmsg_msginfo_free(compose->fwdinfo);
7990 g_free(compose->replyto);
7991 g_free(compose->cc);
7992 g_free(compose->bcc);
7993 g_free(compose->newsgroups);
7994 g_free(compose->followup_to);
7996 g_free(compose->ml_post);
7998 g_free(compose->inreplyto);
7999 g_free(compose->references);
8000 g_free(compose->msgid);
8001 g_free(compose->boundary);
8003 g_free(compose->redirect_filename);
8004 if (compose->undostruct)
8005 undo_destroy(compose->undostruct);
8007 g_free(compose->sig_str);
8009 g_free(compose->exteditor_file);
8011 g_free(compose->orig_charset);
8013 g_free(compose->privacy_system);
8015 if (addressbook_get_target_compose() == compose)
8016 addressbook_set_target_compose(NULL);
8019 if (compose->gtkaspell) {
8020 gtkaspell_delete(compose->gtkaspell);
8021 compose->gtkaspell = NULL;
8025 if (!compose->batch) {
8026 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8027 prefs_common.compose_height = compose->window->allocation.height;
8030 if (!gtk_widget_get_parent(compose->paned))
8031 gtk_widget_destroy(compose->paned);
8032 gtk_widget_destroy(compose->popupmenu);
8034 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8035 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8036 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8038 gtk_widget_destroy(compose->window);
8039 toolbar_destroy(compose->toolbar);
8040 g_free(compose->toolbar);
8041 g_mutex_free(compose->mutex);
8045 static void compose_attach_info_free(AttachInfo *ainfo)
8047 g_free(ainfo->file);
8048 g_free(ainfo->content_type);
8049 g_free(ainfo->name);
8053 static void compose_attach_update_label(Compose *compose)
8058 GtkTreeModel *model;
8063 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8064 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8065 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8069 while(gtk_tree_model_iter_next(model, &iter))
8072 text = g_strdup_printf("(%d)", i);
8073 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8077 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8079 Compose *compose = (Compose *)data;
8080 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8081 GtkTreeSelection *selection;
8083 GtkTreeModel *model;
8085 selection = gtk_tree_view_get_selection(tree_view);
8086 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8091 for (cur = sel; cur != NULL; cur = cur->next) {
8092 GtkTreePath *path = cur->data;
8093 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8096 gtk_tree_path_free(path);
8099 for (cur = sel; cur != NULL; cur = cur->next) {
8100 GtkTreeRowReference *ref = cur->data;
8101 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8104 if (gtk_tree_model_get_iter(model, &iter, path))
8105 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8107 gtk_tree_path_free(path);
8108 gtk_tree_row_reference_free(ref);
8112 compose_attach_update_label(compose);
8115 static struct _AttachProperty
8118 GtkWidget *mimetype_entry;
8119 GtkWidget *encoding_optmenu;
8120 GtkWidget *path_entry;
8121 GtkWidget *filename_entry;
8123 GtkWidget *cancel_btn;
8126 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8128 gtk_tree_path_free((GtkTreePath *)ptr);
8131 static void compose_attach_property(GtkAction *action, gpointer data)
8133 Compose *compose = (Compose *)data;
8134 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8136 GtkComboBox *optmenu;
8137 GtkTreeSelection *selection;
8139 GtkTreeModel *model;
8142 static gboolean cancelled;
8144 /* only if one selected */
8145 selection = gtk_tree_view_get_selection(tree_view);
8146 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8149 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8153 path = (GtkTreePath *) sel->data;
8154 gtk_tree_model_get_iter(model, &iter, path);
8155 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8158 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8164 if (!attach_prop.window)
8165 compose_attach_property_create(&cancelled);
8166 gtk_widget_grab_focus(attach_prop.ok_btn);
8167 gtk_widget_show(attach_prop.window);
8168 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8170 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8171 if (ainfo->encoding == ENC_UNKNOWN)
8172 combobox_select_by_data(optmenu, ENC_BASE64);
8174 combobox_select_by_data(optmenu, ainfo->encoding);
8176 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8177 ainfo->content_type ? ainfo->content_type : "");
8178 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8179 ainfo->file ? ainfo->file : "");
8180 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8181 ainfo->name ? ainfo->name : "");
8184 const gchar *entry_text;
8186 gchar *cnttype = NULL;
8193 gtk_widget_hide(attach_prop.window);
8198 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8199 if (*entry_text != '\0') {
8202 text = g_strstrip(g_strdup(entry_text));
8203 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8204 cnttype = g_strdup(text);
8207 alertpanel_error(_("Invalid MIME type."));
8213 ainfo->encoding = combobox_get_active_data(optmenu);
8215 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8216 if (*entry_text != '\0') {
8217 if (is_file_exist(entry_text) &&
8218 (size = get_file_size(entry_text)) > 0)
8219 file = g_strdup(entry_text);
8222 (_("File doesn't exist or is empty."));
8228 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8229 if (*entry_text != '\0') {
8230 g_free(ainfo->name);
8231 ainfo->name = g_strdup(entry_text);
8235 g_free(ainfo->content_type);
8236 ainfo->content_type = cnttype;
8239 g_free(ainfo->file);
8243 ainfo->size = (goffset)size;
8245 /* update tree store */
8246 text = to_human_readable(ainfo->size);
8247 gtk_tree_model_get_iter(model, &iter, path);
8248 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8249 COL_MIMETYPE, ainfo->content_type,
8251 COL_NAME, ainfo->name,
8257 gtk_tree_path_free(path);
8260 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8262 label = gtk_label_new(str); \
8263 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8264 GTK_FILL, 0, 0, 0); \
8265 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8267 entry = gtk_entry_new(); \
8268 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8269 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8272 static void compose_attach_property_create(gboolean *cancelled)
8278 GtkWidget *mimetype_entry;
8281 GtkListStore *optmenu_menu;
8282 GtkWidget *path_entry;
8283 GtkWidget *filename_entry;
8286 GtkWidget *cancel_btn;
8287 GList *mime_type_list, *strlist;
8290 debug_print("Creating attach_property window...\n");
8292 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8293 gtk_widget_set_size_request(window, 480, -1);
8294 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8295 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8296 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8297 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8298 g_signal_connect(G_OBJECT(window), "delete_event",
8299 G_CALLBACK(attach_property_delete_event),
8301 g_signal_connect(G_OBJECT(window), "key_press_event",
8302 G_CALLBACK(attach_property_key_pressed),
8305 vbox = gtk_vbox_new(FALSE, 8);
8306 gtk_container_add(GTK_CONTAINER(window), vbox);
8308 table = gtk_table_new(4, 2, FALSE);
8309 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8310 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8311 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8313 label = gtk_label_new(_("MIME type"));
8314 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8316 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8317 mimetype_entry = gtk_combo_box_entry_new_text();
8318 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8319 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8321 /* stuff with list */
8322 mime_type_list = procmime_get_mime_type_list();
8324 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8325 MimeType *type = (MimeType *) mime_type_list->data;
8328 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8330 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8333 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8334 (GCompareFunc)strcmp2);
8337 for (mime_type_list = strlist; mime_type_list != NULL;
8338 mime_type_list = mime_type_list->next) {
8339 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8340 g_free(mime_type_list->data);
8342 g_list_free(strlist);
8343 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8344 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8346 label = gtk_label_new(_("Encoding"));
8347 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8349 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8351 hbox = gtk_hbox_new(FALSE, 0);
8352 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8353 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8355 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8356 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8358 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8359 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8360 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8361 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8362 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8364 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8366 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8367 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8369 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8370 &ok_btn, GTK_STOCK_OK,
8372 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8373 gtk_widget_grab_default(ok_btn);
8375 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8376 G_CALLBACK(attach_property_ok),
8378 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8379 G_CALLBACK(attach_property_cancel),
8382 gtk_widget_show_all(vbox);
8384 attach_prop.window = window;
8385 attach_prop.mimetype_entry = mimetype_entry;
8386 attach_prop.encoding_optmenu = optmenu;
8387 attach_prop.path_entry = path_entry;
8388 attach_prop.filename_entry = filename_entry;
8389 attach_prop.ok_btn = ok_btn;
8390 attach_prop.cancel_btn = cancel_btn;
8393 #undef SET_LABEL_AND_ENTRY
8395 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8401 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8407 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8408 gboolean *cancelled)
8416 static gboolean attach_property_key_pressed(GtkWidget *widget,
8418 gboolean *cancelled)
8420 if (event && event->keyval == GDK_Escape) {
8427 static void compose_exec_ext_editor(Compose *compose)
8434 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8435 G_DIR_SEPARATOR, compose);
8437 if (pipe(pipe_fds) < 0) {
8443 if ((pid = fork()) < 0) {
8450 /* close the write side of the pipe */
8453 compose->exteditor_file = g_strdup(tmp);
8454 compose->exteditor_pid = pid;
8456 compose_set_ext_editor_sensitive(compose, FALSE);
8458 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8459 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8463 } else { /* process-monitoring process */
8469 /* close the read side of the pipe */
8472 if (compose_write_body_to_file(compose, tmp) < 0) {
8473 fd_write_all(pipe_fds[1], "2\n", 2);
8477 pid_ed = compose_exec_ext_editor_real(tmp);
8479 fd_write_all(pipe_fds[1], "1\n", 2);
8483 /* wait until editor is terminated */
8484 waitpid(pid_ed, NULL, 0);
8486 fd_write_all(pipe_fds[1], "0\n", 2);
8493 #endif /* G_OS_UNIX */
8497 static gint compose_exec_ext_editor_real(const gchar *file)
8504 g_return_val_if_fail(file != NULL, -1);
8506 if ((pid = fork()) < 0) {
8511 if (pid != 0) return pid;
8513 /* grandchild process */
8515 if (setpgid(0, getppid()))
8518 if (prefs_common_get_ext_editor_cmd() &&
8519 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8520 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8521 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8523 if (prefs_common_get_ext_editor_cmd())
8524 g_warning("External editor command line is invalid: '%s'\n",
8525 prefs_common_get_ext_editor_cmd());
8526 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8529 cmdline = strsplit_with_quote(buf, " ", 1024);
8530 execvp(cmdline[0], cmdline);
8533 g_strfreev(cmdline);
8538 static gboolean compose_ext_editor_kill(Compose *compose)
8540 pid_t pgid = compose->exteditor_pid * -1;
8543 ret = kill(pgid, 0);
8545 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8549 msg = g_strdup_printf
8550 (_("The external editor is still working.\n"
8551 "Force terminating the process?\n"
8552 "process group id: %d"), -pgid);
8553 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8554 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8558 if (val == G_ALERTALTERNATE) {
8559 g_source_remove(compose->exteditor_tag);
8560 g_io_channel_shutdown(compose->exteditor_ch,
8562 g_io_channel_unref(compose->exteditor_ch);
8564 if (kill(pgid, SIGTERM) < 0) perror("kill");
8565 waitpid(compose->exteditor_pid, NULL, 0);
8567 g_warning("Terminated process group id: %d", -pgid);
8568 g_warning("Temporary file: %s",
8569 compose->exteditor_file);
8571 compose_set_ext_editor_sensitive(compose, TRUE);
8573 g_free(compose->exteditor_file);
8574 compose->exteditor_file = NULL;
8575 compose->exteditor_pid = -1;
8576 compose->exteditor_ch = NULL;
8577 compose->exteditor_tag = -1;
8585 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8589 Compose *compose = (Compose *)data;
8592 debug_print(_("Compose: input from monitoring process\n"));
8594 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8596 g_io_channel_shutdown(source, FALSE, NULL);
8597 g_io_channel_unref(source);
8599 waitpid(compose->exteditor_pid, NULL, 0);
8601 if (buf[0] == '0') { /* success */
8602 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8603 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8605 gtk_text_buffer_set_text(buffer, "", -1);
8606 compose_insert_file(compose, compose->exteditor_file);
8607 compose_changed_cb(NULL, compose);
8609 if (claws_unlink(compose->exteditor_file) < 0)
8610 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8611 } else if (buf[0] == '1') { /* failed */
8612 g_warning("Couldn't exec external editor\n");
8613 if (claws_unlink(compose->exteditor_file) < 0)
8614 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8615 } else if (buf[0] == '2') {
8616 g_warning("Couldn't write to file\n");
8617 } else if (buf[0] == '3') {
8618 g_warning("Pipe read failed\n");
8621 compose_set_ext_editor_sensitive(compose, TRUE);
8623 g_free(compose->exteditor_file);
8624 compose->exteditor_file = NULL;
8625 compose->exteditor_pid = -1;
8626 compose->exteditor_ch = NULL;
8627 compose->exteditor_tag = -1;
8632 static void compose_set_ext_editor_sensitive(Compose *compose,
8635 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8636 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8637 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8638 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8639 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8640 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8641 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8643 gtk_widget_set_sensitive(compose->text, sensitive);
8644 if (compose->toolbar->send_btn)
8645 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8646 if (compose->toolbar->sendl_btn)
8647 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8648 if (compose->toolbar->draft_btn)
8649 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8650 if (compose->toolbar->insert_btn)
8651 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8652 if (compose->toolbar->sig_btn)
8653 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8654 if (compose->toolbar->exteditor_btn)
8655 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8656 if (compose->toolbar->linewrap_current_btn)
8657 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8658 if (compose->toolbar->linewrap_all_btn)
8659 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8661 #endif /* G_OS_UNIX */
8664 * compose_undo_state_changed:
8666 * Change the sensivity of the menuentries undo and redo
8668 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8669 gint redo_state, gpointer data)
8671 Compose *compose = (Compose *)data;
8673 switch (undo_state) {
8674 case UNDO_STATE_TRUE:
8675 if (!undostruct->undo_state) {
8676 undostruct->undo_state = TRUE;
8677 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8680 case UNDO_STATE_FALSE:
8681 if (undostruct->undo_state) {
8682 undostruct->undo_state = FALSE;
8683 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8686 case UNDO_STATE_UNCHANGED:
8688 case UNDO_STATE_REFRESH:
8689 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8692 g_warning("Undo state not recognized");
8696 switch (redo_state) {
8697 case UNDO_STATE_TRUE:
8698 if (!undostruct->redo_state) {
8699 undostruct->redo_state = TRUE;
8700 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8703 case UNDO_STATE_FALSE:
8704 if (undostruct->redo_state) {
8705 undostruct->redo_state = FALSE;
8706 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8709 case UNDO_STATE_UNCHANGED:
8711 case UNDO_STATE_REFRESH:
8712 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8715 g_warning("Redo state not recognized");
8720 /* callback functions */
8722 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8723 * includes "non-client" (windows-izm) in calculation, so this calculation
8724 * may not be accurate.
8726 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8727 GtkAllocation *allocation,
8728 GtkSHRuler *shruler)
8730 if (prefs_common.show_ruler) {
8731 gint char_width = 0, char_height = 0;
8732 gint line_width_in_chars;
8734 gtkut_get_font_size(GTK_WIDGET(widget),
8735 &char_width, &char_height);
8736 line_width_in_chars =
8737 (allocation->width - allocation->x) / char_width;
8739 /* got the maximum */
8740 gtk_ruler_set_range(GTK_RULER(shruler),
8741 0.0, line_width_in_chars, 0,
8742 /*line_width_in_chars*/ char_width);
8748 static void account_activated(GtkComboBox *optmenu, gpointer data)
8750 Compose *compose = (Compose *)data;
8753 gchar *folderidentifier;
8754 gint account_id = 0;
8758 /* Get ID of active account in the combo box */
8759 menu = gtk_combo_box_get_model(optmenu);
8760 gtk_combo_box_get_active_iter(optmenu, &iter);
8761 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8763 ac = account_find_from_id(account_id);
8764 g_return_if_fail(ac != NULL);
8766 if (ac != compose->account)
8767 compose_select_account(compose, ac, FALSE);
8769 /* Set message save folder */
8770 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8771 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8773 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8774 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8776 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8777 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8778 folderidentifier = folder_item_get_identifier(account_get_special_folder
8779 (compose->account, F_OUTBOX));
8780 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
8781 g_free(folderidentifier);
8785 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8786 GtkTreeViewColumn *column, Compose *compose)
8788 compose_attach_property(NULL, compose);
8791 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8794 Compose *compose = (Compose *)data;
8795 GtkTreeSelection *attach_selection;
8796 gint attach_nr_selected;
8798 if (!event) return FALSE;
8800 if (event->button == 3) {
8801 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8802 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8804 if (attach_nr_selected > 0)
8806 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8807 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8809 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8810 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8813 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8814 NULL, NULL, event->button, event->time);
8821 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8824 Compose *compose = (Compose *)data;
8826 if (!event) return FALSE;
8828 switch (event->keyval) {
8830 compose_attach_remove_selected(NULL, compose);
8836 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8838 toolbar_comp_set_sensitive(compose, allow);
8839 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8840 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8842 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8844 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8845 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8846 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8848 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8852 static void compose_send_cb(GtkAction *action, gpointer data)
8854 Compose *compose = (Compose *)data;
8856 if (prefs_common.work_offline &&
8857 !inc_offline_should_override(TRUE,
8858 _("Claws Mail needs network access in order "
8859 "to send this email.")))
8862 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8863 g_source_remove(compose->draft_timeout_tag);
8864 compose->draft_timeout_tag = -1;
8867 compose_send(compose);
8870 static void compose_send_later_cb(GtkAction *action, gpointer data)
8872 Compose *compose = (Compose *)data;
8876 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8880 compose_close(compose);
8881 } else if (val == -1) {
8882 alertpanel_error(_("Could not queue message."));
8883 } else if (val == -2) {
8884 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8885 } else if (val == -3) {
8886 if (privacy_peek_error())
8887 alertpanel_error(_("Could not queue message for sending:\n\n"
8888 "Signature failed: %s"), privacy_get_error());
8889 } else if (val == -4) {
8890 alertpanel_error(_("Could not queue message for sending:\n\n"
8891 "Charset conversion failed."));
8892 } else if (val == -5) {
8893 alertpanel_error(_("Could not queue message for sending:\n\n"
8894 "Couldn't get recipient encryption key."));
8895 } else if (val == -6) {
8898 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
8901 #define DRAFTED_AT_EXIT "drafted_at_exit"
8902 static void compose_register_draft(MsgInfo *info)
8904 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8905 DRAFTED_AT_EXIT, NULL);
8906 FILE *fp = fopen(filepath, "ab");
8909 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
8917 gboolean compose_draft (gpointer data, guint action)
8919 Compose *compose = (Compose *)data;
8923 MsgFlags flag = {0, 0};
8924 static gboolean lock = FALSE;
8925 MsgInfo *newmsginfo;
8927 gboolean target_locked = FALSE;
8928 gboolean err = FALSE;
8930 if (lock) return FALSE;
8932 if (compose->sending)
8935 draft = account_get_special_folder(compose->account, F_DRAFT);
8936 g_return_val_if_fail(draft != NULL, FALSE);
8938 if (!g_mutex_trylock(compose->mutex)) {
8939 /* we don't want to lock the mutex once it's available,
8940 * because as the only other part of compose.c locking
8941 * it is compose_close - which means once unlocked,
8942 * the compose struct will be freed */
8943 debug_print("couldn't lock mutex, probably sending\n");
8949 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
8950 G_DIR_SEPARATOR, compose);
8951 if ((fp = g_fopen(tmp, "wb")) == NULL) {
8952 FILE_OP_ERROR(tmp, "fopen");
8956 /* chmod for security */
8957 if (change_file_mode_rw(fp, tmp) < 0) {
8958 FILE_OP_ERROR(tmp, "chmod");
8959 g_warning("can't change file mode\n");
8962 /* Save draft infos */
8963 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
8964 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
8966 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
8967 gchar *savefolderid;
8969 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8970 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
8971 g_free(savefolderid);
8973 if (compose->return_receipt) {
8974 err |= (fprintf(fp, "RRCPT:1\n") < 0);
8976 if (compose->privacy_system) {
8977 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
8978 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
8979 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
8982 /* Message-ID of message replying to */
8983 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
8986 folderid = folder_item_get_identifier(compose->replyinfo->folder);
8987 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
8990 /* Message-ID of message forwarding to */
8991 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
8994 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
8995 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
8999 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9000 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9002 /* end of headers */
9003 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9010 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9014 if (fclose(fp) == EOF) {
9018 if (compose->targetinfo) {
9019 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9020 flag.perm_flags = target_locked?MSG_LOCKED:0;
9022 flag.tmp_flags = MSG_DRAFT;
9024 folder_item_scan(draft);
9025 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9026 MsgInfo *tmpinfo = NULL;
9027 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9028 if (compose->msgid) {
9029 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9032 msgnum = tmpinfo->msgnum;
9033 procmsg_msginfo_free(tmpinfo);
9034 debug_print("got draft msgnum %d from scanning\n", msgnum);
9036 debug_print("didn't get draft msgnum after scanning\n");
9039 debug_print("got draft msgnum %d from adding\n", msgnum);
9045 if (action != COMPOSE_AUTO_SAVE) {
9046 if (action != COMPOSE_DRAFT_FOR_EXIT)
9047 alertpanel_error(_("Could not save draft."));
9050 gtkut_window_popup(compose->window);
9051 val = alertpanel_full(_("Could not save draft"),
9052 _("Could not save draft.\n"
9053 "Do you want to cancel exit or discard this email?"),
9054 _("_Cancel exit"), _("_Discard email"), NULL,
9055 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9056 if (val == G_ALERTALTERNATE) {
9058 g_mutex_unlock(compose->mutex); /* must be done before closing */
9059 compose_close(compose);
9063 g_mutex_unlock(compose->mutex); /* must be done before closing */
9072 if (compose->mode == COMPOSE_REEDIT) {
9073 compose_remove_reedit_target(compose, TRUE);
9076 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9079 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9081 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9083 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9084 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9085 procmsg_msginfo_set_flags(newmsginfo, 0,
9086 MSG_HAS_ATTACHMENT);
9088 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9089 compose_register_draft(newmsginfo);
9091 procmsg_msginfo_free(newmsginfo);
9094 folder_item_scan(draft);
9096 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9098 g_mutex_unlock(compose->mutex); /* must be done before closing */
9099 compose_close(compose);
9105 path = folder_item_fetch_msg(draft, msgnum);
9107 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9110 if (g_stat(path, &s) < 0) {
9111 FILE_OP_ERROR(path, "stat");
9117 procmsg_msginfo_free(compose->targetinfo);
9118 compose->targetinfo = procmsg_msginfo_new();
9119 compose->targetinfo->msgnum = msgnum;
9120 compose->targetinfo->size = (goffset)s.st_size;
9121 compose->targetinfo->mtime = s.st_mtime;
9122 compose->targetinfo->folder = draft;
9124 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9125 compose->mode = COMPOSE_REEDIT;
9127 if (action == COMPOSE_AUTO_SAVE) {
9128 compose->autosaved_draft = compose->targetinfo;
9130 compose->modified = FALSE;
9131 compose_set_title(compose);
9135 g_mutex_unlock(compose->mutex);
9139 void compose_clear_exit_drafts(void)
9141 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9142 DRAFTED_AT_EXIT, NULL);
9143 if (is_file_exist(filepath))
9144 claws_unlink(filepath);
9149 void compose_reopen_exit_drafts(void)
9151 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9152 DRAFTED_AT_EXIT, NULL);
9153 FILE *fp = fopen(filepath, "rb");
9157 while (fgets(buf, sizeof(buf), fp)) {
9158 gchar **parts = g_strsplit(buf, "\t", 2);
9159 const gchar *folder = parts[0];
9160 int msgnum = parts[1] ? atoi(parts[1]):-1;
9162 if (folder && *folder && msgnum > -1) {
9163 FolderItem *item = folder_find_item_from_identifier(folder);
9164 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9166 compose_reedit(info, FALSE);
9173 compose_clear_exit_drafts();
9176 static void compose_save_cb(GtkAction *action, gpointer data)
9178 compose_draft(data, COMPOSE_KEEP_EDITING);
9181 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9183 if (compose && file_list) {
9186 for ( tmp = file_list; tmp; tmp = tmp->next) {
9187 gchar *file = (gchar *) tmp->data;
9188 gchar *utf8_filename = conv_filename_to_utf8(file);
9189 compose_attach_append(compose, file, utf8_filename, NULL);
9190 compose_changed_cb(NULL, compose);
9195 g_free(utf8_filename);
9200 static void compose_attach_cb(GtkAction *action, gpointer data)
9202 Compose *compose = (Compose *)data;
9205 if (compose->redirect_filename != NULL)
9208 file_list = filesel_select_multiple_files_open(_("Select file"));
9211 compose_attach_from_list(compose, file_list, TRUE);
9212 g_list_free(file_list);
9216 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9218 Compose *compose = (Compose *)data;
9221 file_list = filesel_select_multiple_files_open(_("Select file"));
9226 for ( tmp = file_list; tmp; tmp = tmp->next) {
9227 gchar *file = (gchar *) tmp->data;
9228 gchar *filedup = g_strdup(file);
9229 gchar *shortfile = g_path_get_basename(filedup);
9230 ComposeInsertResult res;
9232 res = compose_insert_file(compose, file);
9233 if (res == COMPOSE_INSERT_READ_ERROR) {
9234 alertpanel_error(_("File '%s' could not be read."), shortfile);
9235 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9236 alertpanel_error(_("File '%s' contained invalid characters\n"
9237 "for the current encoding, insertion may be incorrect."), shortfile);
9243 g_list_free(file_list);
9247 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9249 Compose *compose = (Compose *)data;
9251 compose_insert_sig(compose, FALSE);
9254 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9258 Compose *compose = (Compose *)data;
9260 gtkut_widget_get_uposition(widget, &x, &y);
9261 if (!compose->batch) {
9262 prefs_common.compose_x = x;
9263 prefs_common.compose_y = y;
9265 if (compose->sending || compose->updating)
9267 compose_close_cb(NULL, compose);
9271 void compose_close_toolbar(Compose *compose)
9273 compose_close_cb(NULL, compose);
9276 static void compose_close_cb(GtkAction *action, gpointer data)
9278 Compose *compose = (Compose *)data;
9282 if (compose->exteditor_tag != -1) {
9283 if (!compose_ext_editor_kill(compose))
9288 if (compose->modified) {
9289 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9290 if (!g_mutex_trylock(compose->mutex)) {
9291 /* we don't want to lock the mutex once it's available,
9292 * because as the only other part of compose.c locking
9293 * it is compose_close - which means once unlocked,
9294 * the compose struct will be freed */
9295 debug_print("couldn't lock mutex, probably sending\n");
9299 val = alertpanel(_("Discard message"),
9300 _("This message has been modified. Discard it?"),
9301 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9303 val = alertpanel(_("Save changes"),
9304 _("This message has been modified. Save the latest changes?"),
9305 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9307 g_mutex_unlock(compose->mutex);
9309 case G_ALERTDEFAULT:
9310 if (prefs_common.autosave && !reedit)
9311 compose_remove_draft(compose);
9313 case G_ALERTALTERNATE:
9314 compose_draft(data, COMPOSE_QUIT_EDITING);
9321 compose_close(compose);
9324 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9326 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9327 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9328 Compose *compose = (Compose *) data;
9331 compose->out_encoding = (CharSet)value;
9334 static void compose_address_cb(GtkAction *action, gpointer data)
9336 Compose *compose = (Compose *)data;
9338 addressbook_open(compose);
9341 static void about_show_cb(GtkAction *action, gpointer data)
9346 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9348 Compose *compose = (Compose *)data;
9353 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9354 g_return_if_fail(tmpl != NULL);
9356 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9358 val = alertpanel(_("Apply template"), msg,
9359 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9362 if (val == G_ALERTDEFAULT)
9363 compose_template_apply(compose, tmpl, TRUE);
9364 else if (val == G_ALERTALTERNATE)
9365 compose_template_apply(compose, tmpl, FALSE);
9368 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9370 Compose *compose = (Compose *)data;
9372 compose_exec_ext_editor(compose);
9375 static void compose_undo_cb(GtkAction *action, gpointer data)
9377 Compose *compose = (Compose *)data;
9378 gboolean prev_autowrap = compose->autowrap;
9380 compose->autowrap = FALSE;
9381 undo_undo(compose->undostruct);
9382 compose->autowrap = prev_autowrap;
9385 static void compose_redo_cb(GtkAction *action, gpointer data)
9387 Compose *compose = (Compose *)data;
9388 gboolean prev_autowrap = compose->autowrap;
9390 compose->autowrap = FALSE;
9391 undo_redo(compose->undostruct);
9392 compose->autowrap = prev_autowrap;
9395 static void entry_cut_clipboard(GtkWidget *entry)
9397 if (GTK_IS_EDITABLE(entry))
9398 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9399 else if (GTK_IS_TEXT_VIEW(entry))
9400 gtk_text_buffer_cut_clipboard(
9401 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9402 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9406 static void entry_copy_clipboard(GtkWidget *entry)
9408 if (GTK_IS_EDITABLE(entry))
9409 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9410 else if (GTK_IS_TEXT_VIEW(entry))
9411 gtk_text_buffer_copy_clipboard(
9412 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9413 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9416 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9417 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9419 if (GTK_IS_TEXT_VIEW(entry)) {
9420 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9421 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9422 GtkTextIter start_iter, end_iter;
9424 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9426 if (contents == NULL)
9429 /* we shouldn't delete the selection when middle-click-pasting, or we
9430 * can't mid-click-paste our own selection */
9431 if (clip != GDK_SELECTION_PRIMARY) {
9432 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9433 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9436 if (insert_place == NULL) {
9437 /* if insert_place isn't specified, insert at the cursor.
9438 * used for Ctrl-V pasting */
9439 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9440 start = gtk_text_iter_get_offset(&start_iter);
9441 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9443 /* if insert_place is specified, paste here.
9444 * used for mid-click-pasting */
9445 start = gtk_text_iter_get_offset(insert_place);
9446 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9450 /* paste unwrapped: mark the paste so it's not wrapped later */
9451 end = start + strlen(contents);
9452 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9453 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9454 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9455 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9456 /* rewrap paragraph now (after a mid-click-paste) */
9457 mark_start = gtk_text_buffer_get_insert(buffer);
9458 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9459 gtk_text_iter_backward_char(&start_iter);
9460 compose_beautify_paragraph(compose, &start_iter, TRUE);
9462 } else if (GTK_IS_EDITABLE(entry))
9463 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9465 compose->modified = TRUE;
9468 static void entry_allsel(GtkWidget *entry)
9470 if (GTK_IS_EDITABLE(entry))
9471 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9472 else if (GTK_IS_TEXT_VIEW(entry)) {
9473 GtkTextIter startiter, enditer;
9474 GtkTextBuffer *textbuf;
9476 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9477 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9478 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9480 gtk_text_buffer_move_mark_by_name(textbuf,
9481 "selection_bound", &startiter);
9482 gtk_text_buffer_move_mark_by_name(textbuf,
9483 "insert", &enditer);
9487 static void compose_cut_cb(GtkAction *action, gpointer data)
9489 Compose *compose = (Compose *)data;
9490 if (compose->focused_editable
9491 #ifndef GENERIC_UMPC
9492 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9495 entry_cut_clipboard(compose->focused_editable);
9498 static void compose_copy_cb(GtkAction *action, gpointer data)
9500 Compose *compose = (Compose *)data;
9501 if (compose->focused_editable
9502 #ifndef GENERIC_UMPC
9503 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9506 entry_copy_clipboard(compose->focused_editable);
9509 static void compose_paste_cb(GtkAction *action, gpointer data)
9511 Compose *compose = (Compose *)data;
9513 GtkTextBuffer *buffer;
9515 if (compose->focused_editable &&
9516 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9517 entry_paste_clipboard(compose, compose->focused_editable,
9518 prefs_common.linewrap_pastes,
9519 GDK_SELECTION_CLIPBOARD, NULL);
9523 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9525 Compose *compose = (Compose *)data;
9526 gint wrap_quote = prefs_common.linewrap_quote;
9527 if (compose->focused_editable
9528 #ifndef GENERIC_UMPC
9529 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9532 /* let text_insert() (called directly or at a later time
9533 * after the gtk_editable_paste_clipboard) know that
9534 * text is to be inserted as a quotation. implemented
9535 * by using a simple refcount... */
9536 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9537 G_OBJECT(compose->focused_editable),
9538 "paste_as_quotation"));
9539 g_object_set_data(G_OBJECT(compose->focused_editable),
9540 "paste_as_quotation",
9541 GINT_TO_POINTER(paste_as_quotation + 1));
9542 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9543 entry_paste_clipboard(compose, compose->focused_editable,
9544 prefs_common.linewrap_pastes,
9545 GDK_SELECTION_CLIPBOARD, NULL);
9546 prefs_common.linewrap_quote = wrap_quote;
9550 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9552 Compose *compose = (Compose *)data;
9554 GtkTextBuffer *buffer;
9556 if (compose->focused_editable
9557 #ifndef GENERIC_UMPC
9558 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9561 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9562 GDK_SELECTION_CLIPBOARD, NULL);
9566 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9568 Compose *compose = (Compose *)data;
9570 GtkTextBuffer *buffer;
9572 if (compose->focused_editable
9573 #ifndef GENERIC_UMPC
9574 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9577 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9578 GDK_SELECTION_CLIPBOARD, NULL);
9582 static void compose_allsel_cb(GtkAction *action, gpointer data)
9584 Compose *compose = (Compose *)data;
9585 if (compose->focused_editable
9586 #ifndef GENERIC_UMPC
9587 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9590 entry_allsel(compose->focused_editable);
9593 static void textview_move_beginning_of_line (GtkTextView *text)
9595 GtkTextBuffer *buffer;
9599 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9601 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9602 mark = gtk_text_buffer_get_insert(buffer);
9603 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9604 gtk_text_iter_set_line_offset(&ins, 0);
9605 gtk_text_buffer_place_cursor(buffer, &ins);
9608 static void textview_move_forward_character (GtkTextView *text)
9610 GtkTextBuffer *buffer;
9614 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9616 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9617 mark = gtk_text_buffer_get_insert(buffer);
9618 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9619 if (gtk_text_iter_forward_cursor_position(&ins))
9620 gtk_text_buffer_place_cursor(buffer, &ins);
9623 static void textview_move_backward_character (GtkTextView *text)
9625 GtkTextBuffer *buffer;
9629 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9631 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9632 mark = gtk_text_buffer_get_insert(buffer);
9633 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9634 if (gtk_text_iter_backward_cursor_position(&ins))
9635 gtk_text_buffer_place_cursor(buffer, &ins);
9638 static void textview_move_forward_word (GtkTextView *text)
9640 GtkTextBuffer *buffer;
9645 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9647 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9648 mark = gtk_text_buffer_get_insert(buffer);
9649 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9650 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9651 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9652 gtk_text_iter_backward_word_start(&ins);
9653 gtk_text_buffer_place_cursor(buffer, &ins);
9657 static void textview_move_backward_word (GtkTextView *text)
9659 GtkTextBuffer *buffer;
9664 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9666 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9667 mark = gtk_text_buffer_get_insert(buffer);
9668 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9669 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9670 if (gtk_text_iter_backward_word_starts(&ins, 1))
9671 gtk_text_buffer_place_cursor(buffer, &ins);
9674 static void textview_move_end_of_line (GtkTextView *text)
9676 GtkTextBuffer *buffer;
9680 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9682 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9683 mark = gtk_text_buffer_get_insert(buffer);
9684 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9685 if (gtk_text_iter_forward_to_line_end(&ins))
9686 gtk_text_buffer_place_cursor(buffer, &ins);
9689 static void textview_move_next_line (GtkTextView *text)
9691 GtkTextBuffer *buffer;
9696 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9698 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9699 mark = gtk_text_buffer_get_insert(buffer);
9700 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9701 offset = gtk_text_iter_get_line_offset(&ins);
9702 if (gtk_text_iter_forward_line(&ins)) {
9703 gtk_text_iter_set_line_offset(&ins, offset);
9704 gtk_text_buffer_place_cursor(buffer, &ins);
9708 static void textview_move_previous_line (GtkTextView *text)
9710 GtkTextBuffer *buffer;
9715 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9717 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9718 mark = gtk_text_buffer_get_insert(buffer);
9719 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9720 offset = gtk_text_iter_get_line_offset(&ins);
9721 if (gtk_text_iter_backward_line(&ins)) {
9722 gtk_text_iter_set_line_offset(&ins, offset);
9723 gtk_text_buffer_place_cursor(buffer, &ins);
9727 static void textview_delete_forward_character (GtkTextView *text)
9729 GtkTextBuffer *buffer;
9731 GtkTextIter ins, end_iter;
9733 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9735 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9736 mark = gtk_text_buffer_get_insert(buffer);
9737 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9739 if (gtk_text_iter_forward_char(&end_iter)) {
9740 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9744 static void textview_delete_backward_character (GtkTextView *text)
9746 GtkTextBuffer *buffer;
9748 GtkTextIter ins, end_iter;
9750 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9752 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9753 mark = gtk_text_buffer_get_insert(buffer);
9754 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9756 if (gtk_text_iter_backward_char(&end_iter)) {
9757 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9761 static void textview_delete_forward_word (GtkTextView *text)
9763 GtkTextBuffer *buffer;
9765 GtkTextIter ins, end_iter;
9767 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9769 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9770 mark = gtk_text_buffer_get_insert(buffer);
9771 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9773 if (gtk_text_iter_forward_word_end(&end_iter)) {
9774 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9778 static void textview_delete_backward_word (GtkTextView *text)
9780 GtkTextBuffer *buffer;
9782 GtkTextIter ins, end_iter;
9784 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9786 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9787 mark = gtk_text_buffer_get_insert(buffer);
9788 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9790 if (gtk_text_iter_backward_word_start(&end_iter)) {
9791 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9795 static void textview_delete_line (GtkTextView *text)
9797 GtkTextBuffer *buffer;
9799 GtkTextIter ins, start_iter, end_iter;
9801 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9803 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9804 mark = gtk_text_buffer_get_insert(buffer);
9805 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9808 gtk_text_iter_set_line_offset(&start_iter, 0);
9811 if (gtk_text_iter_ends_line(&end_iter)){
9812 if (!gtk_text_iter_forward_char(&end_iter))
9813 gtk_text_iter_backward_char(&start_iter);
9816 gtk_text_iter_forward_to_line_end(&end_iter);
9817 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9820 static void textview_delete_to_line_end (GtkTextView *text)
9822 GtkTextBuffer *buffer;
9824 GtkTextIter ins, end_iter;
9826 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9828 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9829 mark = gtk_text_buffer_get_insert(buffer);
9830 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9832 if (gtk_text_iter_ends_line(&end_iter))
9833 gtk_text_iter_forward_char(&end_iter);
9835 gtk_text_iter_forward_to_line_end(&end_iter);
9836 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9839 #define DO_ACTION(name, act) { \
9840 if(!strcmp(name, a_name)) { \
9844 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
9846 const gchar *a_name = gtk_action_get_name(action);
9847 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
9848 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
9849 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
9850 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
9851 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
9852 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
9853 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
9854 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
9855 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
9856 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
9857 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
9858 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
9859 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
9860 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
9864 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
9866 Compose *compose = (Compose *)data;
9867 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9868 ComposeCallAdvancedAction action = -1;
9870 action = compose_call_advanced_action_from_path(gaction);
9873 void (*do_action) (GtkTextView *text);
9874 } action_table[] = {
9875 {textview_move_beginning_of_line},
9876 {textview_move_forward_character},
9877 {textview_move_backward_character},
9878 {textview_move_forward_word},
9879 {textview_move_backward_word},
9880 {textview_move_end_of_line},
9881 {textview_move_next_line},
9882 {textview_move_previous_line},
9883 {textview_delete_forward_character},
9884 {textview_delete_backward_character},
9885 {textview_delete_forward_word},
9886 {textview_delete_backward_word},
9887 {textview_delete_line},
9888 {textview_delete_to_line_end}
9891 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
9893 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
9894 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
9895 if (action_table[action].do_action)
9896 action_table[action].do_action(text);
9898 g_warning("Not implemented yet.");
9902 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
9906 if (GTK_IS_EDITABLE(widget)) {
9907 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
9908 gtk_editable_set_position(GTK_EDITABLE(widget),
9911 if (widget->parent && widget->parent->parent
9912 && widget->parent->parent->parent) {
9913 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
9914 gint y = widget->allocation.y;
9915 gint height = widget->allocation.height;
9916 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
9917 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
9919 if (y < (int)shown->value) {
9920 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
9922 if (y + height > (int)shown->value + (int)shown->page_size) {
9923 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
9924 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9925 y + height - (int)shown->page_size - 1);
9927 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9928 (int)shown->upper - (int)shown->page_size - 1);
9935 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
9936 compose->focused_editable = widget;
9939 if (GTK_IS_TEXT_VIEW(widget)
9940 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
9941 g_object_ref(compose->notebook);
9942 g_object_ref(compose->edit_vbox);
9943 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9944 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9945 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
9946 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
9947 g_object_unref(compose->notebook);
9948 g_object_unref(compose->edit_vbox);
9949 g_signal_handlers_block_by_func(G_OBJECT(widget),
9950 G_CALLBACK(compose_grab_focus_cb),
9952 gtk_widget_grab_focus(widget);
9953 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9954 G_CALLBACK(compose_grab_focus_cb),
9956 } else if (!GTK_IS_TEXT_VIEW(widget)
9957 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
9958 g_object_ref(compose->notebook);
9959 g_object_ref(compose->edit_vbox);
9960 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9961 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9962 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
9963 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
9964 g_object_unref(compose->notebook);
9965 g_object_unref(compose->edit_vbox);
9966 g_signal_handlers_block_by_func(G_OBJECT(widget),
9967 G_CALLBACK(compose_grab_focus_cb),
9969 gtk_widget_grab_focus(widget);
9970 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9971 G_CALLBACK(compose_grab_focus_cb),
9977 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
9979 compose->modified = TRUE;
9980 #ifndef GENERIC_UMPC
9981 compose_set_title(compose);
9985 static void compose_wrap_cb(GtkAction *action, gpointer data)
9987 Compose *compose = (Compose *)data;
9988 compose_beautify_paragraph(compose, NULL, TRUE);
9991 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
9993 Compose *compose = (Compose *)data;
9994 compose_wrap_all_full(compose, TRUE);
9997 static void compose_find_cb(GtkAction *action, gpointer data)
9999 Compose *compose = (Compose *)data;
10001 message_search_compose(compose);
10004 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10007 Compose *compose = (Compose *)data;
10008 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10009 if (compose->autowrap)
10010 compose_wrap_all_full(compose, TRUE);
10011 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10014 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10017 Compose *compose = (Compose *)data;
10018 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10021 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10023 Compose *compose = (Compose *)data;
10025 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10028 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10030 Compose *compose = (Compose *)data;
10032 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10035 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10037 g_free(compose->privacy_system);
10039 compose->privacy_system = g_strdup(account->default_privacy_system);
10040 compose_update_privacy_system_menu_item(compose, warn);
10043 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10045 Compose *compose = (Compose *)data;
10047 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10048 gtk_widget_show(compose->ruler_hbox);
10049 prefs_common.show_ruler = TRUE;
10051 gtk_widget_hide(compose->ruler_hbox);
10052 gtk_widget_queue_resize(compose->edit_vbox);
10053 prefs_common.show_ruler = FALSE;
10057 static void compose_attach_drag_received_cb (GtkWidget *widget,
10058 GdkDragContext *context,
10061 GtkSelectionData *data,
10064 gpointer user_data)
10066 Compose *compose = (Compose *)user_data;
10069 if (gdk_atom_name(data->type) &&
10070 !strcmp(gdk_atom_name(data->type), "text/uri-list")
10071 && gtk_drag_get_source_widget(context) !=
10072 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10073 list = uri_list_extract_filenames((const gchar *)data->data);
10074 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10075 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10076 compose_attach_append
10077 (compose, (const gchar *)tmp->data,
10078 utf8_filename, NULL);
10079 g_free(utf8_filename);
10081 if (list) compose_changed_cb(NULL, compose);
10082 list_free_strings(list);
10084 } else if (gtk_drag_get_source_widget(context)
10085 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10086 /* comes from our summaryview */
10087 SummaryView * summaryview = NULL;
10088 GSList * list = NULL, *cur = NULL;
10090 if (mainwindow_get_mainwindow())
10091 summaryview = mainwindow_get_mainwindow()->summaryview;
10094 list = summary_get_selected_msg_list(summaryview);
10096 for (cur = list; cur; cur = cur->next) {
10097 MsgInfo *msginfo = (MsgInfo *)cur->data;
10098 gchar *file = NULL;
10100 file = procmsg_get_message_file_full(msginfo,
10103 compose_attach_append(compose, (const gchar *)file,
10104 (const gchar *)file, "message/rfc822");
10108 g_slist_free(list);
10112 static gboolean compose_drag_drop(GtkWidget *widget,
10113 GdkDragContext *drag_context,
10115 guint time, gpointer user_data)
10117 /* not handling this signal makes compose_insert_drag_received_cb
10122 static void compose_insert_drag_received_cb (GtkWidget *widget,
10123 GdkDragContext *drag_context,
10126 GtkSelectionData *data,
10129 gpointer user_data)
10131 Compose *compose = (Compose *)user_data;
10134 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10136 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10137 AlertValue val = G_ALERTDEFAULT;
10139 list = uri_list_extract_filenames((const gchar *)data->data);
10141 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10142 /* Assume a list of no files, and data has ://, is a remote link */
10143 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10144 gchar *tmpfile = get_tmp_file();
10145 str_write_to_file(tmpdata, tmpfile);
10147 compose_insert_file(compose, tmpfile);
10148 claws_unlink(tmpfile);
10150 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10151 compose_beautify_paragraph(compose, NULL, TRUE);
10154 switch (prefs_common.compose_dnd_mode) {
10155 case COMPOSE_DND_ASK:
10156 val = alertpanel_full(_("Insert or attach?"),
10157 _("Do you want to insert the contents of the file(s) "
10158 "into the message body, or attach it to the email?"),
10159 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10160 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10162 case COMPOSE_DND_INSERT:
10163 val = G_ALERTALTERNATE;
10165 case COMPOSE_DND_ATTACH:
10166 val = G_ALERTOTHER;
10169 /* unexpected case */
10170 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10173 if (val & G_ALERTDISABLE) {
10174 val &= ~G_ALERTDISABLE;
10175 /* remember what action to perform by default, only if we don't click Cancel */
10176 if (val == G_ALERTALTERNATE)
10177 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10178 else if (val == G_ALERTOTHER)
10179 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10182 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10183 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10184 list_free_strings(list);
10187 } else if (val == G_ALERTOTHER) {
10188 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10189 list_free_strings(list);
10194 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10195 compose_insert_file(compose, (const gchar *)tmp->data);
10197 list_free_strings(list);
10199 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10202 #if GTK_CHECK_VERSION(2, 8, 0)
10203 /* do nothing, handled by GTK */
10205 gchar *tmpfile = get_tmp_file();
10206 str_write_to_file((const gchar *)data->data, tmpfile);
10207 compose_insert_file(compose, tmpfile);
10208 claws_unlink(tmpfile);
10210 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10214 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10217 static void compose_header_drag_received_cb (GtkWidget *widget,
10218 GdkDragContext *drag_context,
10221 GtkSelectionData *data,
10224 gpointer user_data)
10226 GtkEditable *entry = (GtkEditable *)user_data;
10227 gchar *email = (gchar *)data->data;
10229 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10232 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10233 gchar *decoded=g_new(gchar, strlen(email));
10236 email += strlen("mailto:");
10237 decode_uri(decoded, email); /* will fit */
10238 gtk_editable_delete_text(entry, 0, -1);
10239 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10240 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10244 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10247 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10249 Compose *compose = (Compose *)data;
10251 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10252 compose->return_receipt = TRUE;
10254 compose->return_receipt = FALSE;
10257 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10259 Compose *compose = (Compose *)data;
10261 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10262 compose->remove_references = TRUE;
10264 compose->remove_references = FALSE;
10267 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10268 GdkEventKey *event,
10269 ComposeHeaderEntry *headerentry)
10271 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10272 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10273 !(event->state & GDK_MODIFIER_MASK) &&
10274 (event->keyval == GDK_BackSpace) &&
10275 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10276 gtk_container_remove
10277 (GTK_CONTAINER(headerentry->compose->header_table),
10278 headerentry->combo);
10279 gtk_container_remove
10280 (GTK_CONTAINER(headerentry->compose->header_table),
10281 headerentry->entry);
10282 headerentry->compose->header_list =
10283 g_slist_remove(headerentry->compose->header_list,
10285 g_free(headerentry);
10286 } else if (event->keyval == GDK_Tab) {
10287 if (headerentry->compose->header_last == headerentry) {
10288 /* Override default next focus, and give it to subject_entry
10289 * instead of notebook tabs
10291 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10292 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10299 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10300 ComposeHeaderEntry *headerentry)
10302 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10303 compose_create_header_entry(headerentry->compose);
10304 g_signal_handlers_disconnect_matched
10305 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10306 0, 0, NULL, NULL, headerentry);
10308 /* Automatically scroll down */
10309 compose_show_first_last_header(headerentry->compose, FALSE);
10315 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10317 GtkAdjustment *vadj;
10319 g_return_if_fail(compose);
10320 g_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10321 g_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10323 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10324 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10325 gtk_adjustment_changed(vadj);
10328 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10329 const gchar *text, gint len, Compose *compose)
10331 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10332 (G_OBJECT(compose->text), "paste_as_quotation"));
10335 g_return_if_fail(text != NULL);
10337 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10338 G_CALLBACK(text_inserted),
10340 if (paste_as_quotation) {
10342 const gchar *qmark;
10344 GtkTextIter start_iter;
10347 len = strlen(text);
10349 new_text = g_strndup(text, len);
10351 qmark = compose_quote_char_from_context(compose);
10353 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10354 gtk_text_buffer_place_cursor(buffer, iter);
10356 pos = gtk_text_iter_get_offset(iter);
10358 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10359 _("Quote format error at line %d."));
10360 quote_fmt_reset_vartable();
10362 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10363 GINT_TO_POINTER(paste_as_quotation - 1));
10365 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10366 gtk_text_buffer_place_cursor(buffer, iter);
10367 gtk_text_buffer_delete_mark(buffer, mark);
10369 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10370 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10371 compose_beautify_paragraph(compose, &start_iter, FALSE);
10372 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10373 gtk_text_buffer_delete_mark(buffer, mark);
10375 if (strcmp(text, "\n") || compose->automatic_break
10376 || gtk_text_iter_starts_line(iter)) {
10377 GtkTextIter before_ins;
10378 gtk_text_buffer_insert(buffer, iter, text, len);
10379 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10380 before_ins = *iter;
10381 gtk_text_iter_backward_chars(&before_ins, len);
10382 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10385 /* check if the preceding is just whitespace or quote */
10386 GtkTextIter start_line;
10387 gchar *tmp = NULL, *quote = NULL;
10388 gint quote_len = 0, is_normal = 0;
10389 start_line = *iter;
10390 gtk_text_iter_set_line_offset(&start_line, 0);
10391 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10394 if (*tmp == '\0') {
10397 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10405 gtk_text_buffer_insert(buffer, iter, text, len);
10407 gtk_text_buffer_insert_with_tags_by_name(buffer,
10408 iter, text, len, "no_join", NULL);
10413 if (!paste_as_quotation) {
10414 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10415 compose_beautify_paragraph(compose, iter, FALSE);
10416 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10417 gtk_text_buffer_delete_mark(buffer, mark);
10420 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10421 G_CALLBACK(text_inserted),
10423 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10425 if (prefs_common.autosave &&
10426 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10427 compose->draft_timeout_tag != -2 /* disabled while loading */)
10428 compose->draft_timeout_tag = g_timeout_add
10429 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10431 static gint compose_defer_auto_save_draft(Compose *compose)
10433 compose->draft_timeout_tag = -1;
10434 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10439 static void compose_check_all(GtkAction *action, gpointer data)
10441 Compose *compose = (Compose *)data;
10442 if (compose->gtkaspell)
10443 gtkaspell_check_all(compose->gtkaspell);
10446 static void compose_highlight_all(GtkAction *action, gpointer data)
10448 Compose *compose = (Compose *)data;
10449 if (compose->gtkaspell)
10450 gtkaspell_highlight_all(compose->gtkaspell);
10453 static void compose_check_backwards(GtkAction *action, gpointer data)
10455 Compose *compose = (Compose *)data;
10456 if (compose->gtkaspell)
10457 gtkaspell_check_backwards(compose->gtkaspell);
10459 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10463 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10465 Compose *compose = (Compose *)data;
10466 if (compose->gtkaspell)
10467 gtkaspell_check_forwards_go(compose->gtkaspell);
10469 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10475 *\brief Guess originating forward account from MsgInfo and several
10476 * "common preference" settings. Return NULL if no guess.
10478 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10480 PrefsAccount *account = NULL;
10482 g_return_val_if_fail(msginfo, NULL);
10483 g_return_val_if_fail(msginfo->folder, NULL);
10484 g_return_val_if_fail(msginfo->folder->prefs, NULL);
10486 if (msginfo->folder->prefs->enable_default_account)
10487 account = account_find_from_id(msginfo->folder->prefs->default_account);
10490 account = msginfo->folder->folder->account;
10492 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10494 Xstrdup_a(to, msginfo->to, return NULL);
10495 extract_address(to);
10496 account = account_find_from_address(to, FALSE);
10499 if (!account && prefs_common.forward_account_autosel) {
10500 gchar cc[BUFFSIZE];
10501 if (!procheader_get_header_from_msginfo
10502 (msginfo, cc,sizeof cc , "Cc:")) {
10503 gchar *buf = cc + strlen("Cc:");
10504 extract_address(buf);
10505 account = account_find_from_address(buf, FALSE);
10509 if (!account && prefs_common.forward_account_autosel) {
10510 gchar deliveredto[BUFFSIZE];
10511 if (!procheader_get_header_from_msginfo
10512 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10513 gchar *buf = deliveredto + strlen("Delivered-To:");
10514 extract_address(buf);
10515 account = account_find_from_address(buf, FALSE);
10522 gboolean compose_close(Compose *compose)
10526 if (!g_mutex_trylock(compose->mutex)) {
10527 /* we have to wait for the (possibly deferred by auto-save)
10528 * drafting to be done, before destroying the compose under
10530 debug_print("waiting for drafting to finish...\n");
10531 compose_allow_user_actions(compose, FALSE);
10532 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10535 g_return_val_if_fail(compose, FALSE);
10536 gtkut_widget_get_uposition(compose->window, &x, &y);
10537 if (!compose->batch) {
10538 prefs_common.compose_x = x;
10539 prefs_common.compose_y = y;
10541 g_mutex_unlock(compose->mutex);
10542 compose_destroy(compose);
10547 * Add entry field for each address in list.
10548 * \param compose E-Mail composition object.
10549 * \param listAddress List of (formatted) E-Mail addresses.
10551 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10554 node = listAddress;
10556 addr = ( gchar * ) node->data;
10557 compose_entry_append( compose, addr, COMPOSE_TO );
10558 node = g_list_next( node );
10562 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10563 guint action, gboolean opening_multiple)
10565 gchar *body = NULL;
10566 GSList *new_msglist = NULL;
10567 MsgInfo *tmp_msginfo = NULL;
10568 gboolean originally_enc = FALSE;
10569 gboolean originally_sig = FALSE;
10570 Compose *compose = NULL;
10572 g_return_if_fail(msgview != NULL);
10574 g_return_if_fail(msginfo_list != NULL);
10576 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10577 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10578 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10580 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10581 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10582 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10583 orig_msginfo, mimeinfo);
10584 if (tmp_msginfo != NULL) {
10585 new_msglist = g_slist_append(NULL, tmp_msginfo);
10587 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10588 privacy_msginfo_get_signed_state(orig_msginfo);
10589 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
10591 tmp_msginfo->folder = orig_msginfo->folder;
10592 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10593 if (orig_msginfo->tags)
10594 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10599 if (!opening_multiple)
10600 body = messageview_get_selection(msgview);
10603 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10604 procmsg_msginfo_free(tmp_msginfo);
10605 g_slist_free(new_msglist);
10607 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10609 if (compose && originally_enc) {
10610 compose_force_encryption(compose, compose->account, FALSE);
10613 if (compose && originally_sig && compose->account->default_sign_reply) {
10614 compose_force_signing(compose, compose->account);
10620 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10623 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
10624 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10625 GSList *cur = msginfo_list;
10626 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10627 "messages. Opening the windows "
10628 "could take some time. Do you "
10629 "want to continue?"),
10630 g_slist_length(msginfo_list));
10631 if (g_slist_length(msginfo_list) > 9
10632 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10633 != G_ALERTALTERNATE) {
10638 /* We'll open multiple compose windows */
10639 /* let the WM place the next windows */
10640 compose_force_window_origin = FALSE;
10641 for (; cur; cur = cur->next) {
10643 tmplist.data = cur->data;
10644 tmplist.next = NULL;
10645 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10647 compose_force_window_origin = TRUE;
10649 /* forwarding multiple mails as attachments is done via a
10650 * single compose window */
10651 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10655 void compose_set_position(Compose *compose, gint pos)
10657 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10659 gtkut_text_view_set_position(text, pos);
10662 gboolean compose_search_string(Compose *compose,
10663 const gchar *str, gboolean case_sens)
10665 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10667 return gtkut_text_view_search_string(text, str, case_sens);
10670 gboolean compose_search_string_backward(Compose *compose,
10671 const gchar *str, gboolean case_sens)
10673 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10675 return gtkut_text_view_search_string_backward(text, str, case_sens);
10678 /* allocate a msginfo structure and populate its data from a compose data structure */
10679 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10681 MsgInfo *newmsginfo;
10683 gchar buf[BUFFSIZE];
10685 g_return_val_if_fail( compose != NULL, NULL );
10687 newmsginfo = procmsg_msginfo_new();
10690 get_rfc822_date(buf, sizeof(buf));
10691 newmsginfo->date = g_strdup(buf);
10694 if (compose->from_name) {
10695 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10696 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10700 if (compose->subject_entry)
10701 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10703 /* to, cc, reply-to, newsgroups */
10704 for (list = compose->header_list; list; list = list->next) {
10705 gchar *header = gtk_editable_get_chars(
10707 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
10708 gchar *entry = gtk_editable_get_chars(
10709 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10711 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10712 if ( newmsginfo->to == NULL ) {
10713 newmsginfo->to = g_strdup(entry);
10714 } else if (entry && *entry) {
10715 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10716 g_free(newmsginfo->to);
10717 newmsginfo->to = tmp;
10720 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10721 if ( newmsginfo->cc == NULL ) {
10722 newmsginfo->cc = g_strdup(entry);
10723 } else if (entry && *entry) {
10724 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10725 g_free(newmsginfo->cc);
10726 newmsginfo->cc = tmp;
10729 if ( strcasecmp(header,
10730 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10731 if ( newmsginfo->newsgroups == NULL ) {
10732 newmsginfo->newsgroups = g_strdup(entry);
10733 } else if (entry && *entry) {
10734 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10735 g_free(newmsginfo->newsgroups);
10736 newmsginfo->newsgroups = tmp;
10744 /* other data is unset */
10750 /* update compose's dictionaries from folder dict settings */
10751 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10752 FolderItem *folder_item)
10754 g_return_if_fail(compose != NULL);
10756 if (compose->gtkaspell && folder_item && folder_item->prefs) {
10757 FolderItemPrefs *prefs = folder_item->prefs;
10759 if (prefs->enable_default_dictionary)
10760 gtkaspell_change_dict(compose->gtkaspell,
10761 prefs->default_dictionary, FALSE);
10762 if (folder_item->prefs->enable_default_alt_dictionary)
10763 gtkaspell_change_alt_dict(compose->gtkaspell,
10764 prefs->default_alt_dictionary);
10765 if (prefs->enable_default_dictionary
10766 || prefs->enable_default_alt_dictionary)
10767 compose_spell_menu_changed(compose);