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