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,
427 static void compose_toggle_ruler_cb (GtkToggleAction *action,
429 static void compose_toggle_sign_cb (GtkToggleAction *action,
431 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
433 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
434 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
435 static void activate_privacy_system (Compose *compose,
436 PrefsAccount *account,
438 static void compose_use_signing(Compose *compose, gboolean use_signing);
439 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
440 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
442 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
444 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
445 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
446 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
448 static void compose_attach_drag_received_cb (GtkWidget *widget,
449 GdkDragContext *drag_context,
452 GtkSelectionData *data,
456 static void compose_insert_drag_received_cb (GtkWidget *widget,
457 GdkDragContext *drag_context,
460 GtkSelectionData *data,
464 static void compose_header_drag_received_cb (GtkWidget *widget,
465 GdkDragContext *drag_context,
468 GtkSelectionData *data,
473 static gboolean compose_drag_drop (GtkWidget *widget,
474 GdkDragContext *drag_context,
476 guint time, gpointer user_data);
478 static void text_inserted (GtkTextBuffer *buffer,
483 static Compose *compose_generic_reply(MsgInfo *msginfo,
484 ComposeQuoteMode quote_mode,
488 gboolean followup_and_reply_to,
491 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
492 ComposeHeaderEntry *headerentry);
493 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
495 ComposeHeaderEntry *headerentry);
497 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
499 static void compose_allow_user_actions (Compose *compose, gboolean allow);
501 static void compose_nothing_cb (GtkAction *action, gpointer data)
507 static void compose_check_all (GtkAction *action, gpointer data);
508 static void compose_highlight_all (GtkAction *action, gpointer data);
509 static void compose_check_backwards (GtkAction *action, gpointer data);
510 static void compose_check_forwards_go (GtkAction *action, gpointer data);
513 static gint compose_defer_auto_save_draft (Compose *compose);
514 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
516 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
519 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
520 FolderItem *folder_item);
522 static void compose_attach_update_label(Compose *compose);
524 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data);
526 static GtkActionEntry compose_popup_entries[] =
528 {"Compose", NULL, "Compose" },
529 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
530 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
531 {"Compose/---", NULL, "---", NULL, NULL, NULL },
532 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
535 static GtkActionEntry compose_entries[] =
537 {"Menu", NULL, "Menu" },
539 {"Message", NULL, N_("_Message") },
540 {"Edit", NULL, N_("_Edit") },
542 {"Spelling", NULL, N_("_Spelling") },
544 {"Options", NULL, N_("_Options") },
545 {"Tools", NULL, N_("_Tools") },
546 {"Help", NULL, N_("_Help") },
548 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
549 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
550 {"Message/---", NULL, "---" },
552 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
553 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
554 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
555 /* {"Message/---", NULL, "---" }, */
556 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
557 /* {"Message/---", NULL, "---" }, */
558 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
561 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
562 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
563 {"Edit/---", NULL, "---" },
565 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
566 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
567 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
569 {"Edit/SpecialPaste", NULL, N_("Special paste") },
570 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
571 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
572 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
574 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
576 {"Edit/Advanced", NULL, N_("A_dvanced") },
577 {"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*/
578 {"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*/
579 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
580 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
581 {"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*/
582 {"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*/
583 {"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*/
584 {"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*/
585 {"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*/
586 {"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*/
587 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
588 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
589 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
590 {"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*/
592 /* {"Edit/---", NULL, "---" }, */
593 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
595 /* {"Edit/---", NULL, "---" }, */
596 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
597 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
598 /* {"Edit/---", NULL, "---" }, */
599 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
602 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
603 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
604 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
605 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
607 {"Spelling/---", NULL, "---" },
608 {"Spelling/Options", NULL, N_("_Options") },
613 {"Options/ReplyMode", NULL, N_("Reply _mode") },
614 {"Options/---", NULL, "---" },
615 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
616 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
618 /* {"Options/---", NULL, "---" }, */
620 {"Options/Priority", NULL, N_("_Priority") },
622 {"Options/Encoding", NULL, N_("Character _encoding") },
623 {"Options/Encoding/---", NULL, "---" },
624 #define ENC_ACTION(cs_char,c_char,string) \
625 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
627 {"Options/Encoding/Western", NULL, N_("Western European") },
628 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
629 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
630 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
631 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
632 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
633 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
634 {"Options/Encoding/Korean", NULL, N_("Korean") },
635 {"Options/Encoding/Thai", NULL, N_("Thai") },
638 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
640 {"Tools/Template", NULL, N_("_Template") },
641 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
642 {"Tools/Actions", NULL, N_("Actio_ns") },
643 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
646 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
649 static GtkToggleActionEntry compose_toggle_entries[] =
651 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
652 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
653 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
654 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
655 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
656 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
659 static GtkRadioActionEntry compose_radio_rm_entries[] =
661 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
662 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
663 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
664 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
667 static GtkRadioActionEntry compose_radio_prio_entries[] =
669 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
670 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
671 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
672 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
673 {"Options/Priority/Lowest", NULL, N_("Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
676 static GtkRadioActionEntry compose_radio_enc_entries[] =
678 ENC_ACTION(CS_AUTO, C_AUTO, "_Automatic"), /* RADIO compose_set_encoding_cb */
679 ENC_ACTION(CS_US_ASCII, C_US_ASCII, "7bit ASCII (US-ASC_II)"), /* RADIO compose_set_encoding_cb */
680 ENC_ACTION(CS_UTF_8, C_UTF_8, "Unicode (_UTF-8)"), /* RADIO compose_set_encoding_cb */
681 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
682 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
683 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
684 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, "Central European (ISO-8859-_2)"), /* RADIO compose_set_encoding_cb */
685 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
686 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
687 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, "Greek (ISO-8859-_7)"), /* RADIO compose_set_encoding_cb */
688 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
689 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
690 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
691 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
692 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, "Turkish (ISO-8859-_9)"), /* RADIO compose_set_encoding_cb */
693 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
711 static GtkTargetEntry compose_mime_types[] =
713 {"text/uri-list", 0, 0},
714 {"UTF8_STRING", 0, 0},
718 static gboolean compose_put_existing_to_front(MsgInfo *info)
720 GList *compose_list = compose_get_compose_list();
724 for (elem = compose_list; elem != NULL && elem->data != NULL;
726 Compose *c = (Compose*)elem->data;
728 if (!c->targetinfo || !c->targetinfo->msgid ||
732 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
733 gtkut_window_popup(c->window);
741 static GdkColor quote_color1 =
742 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
743 static GdkColor quote_color2 =
744 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
745 static GdkColor quote_color3 =
746 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
748 static GdkColor quote_bgcolor1 =
749 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
750 static GdkColor quote_bgcolor2 =
751 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
752 static GdkColor quote_bgcolor3 =
753 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
755 static GdkColor signature_color = {
762 static GdkColor uri_color = {
769 static void compose_create_tags(GtkTextView *text, Compose *compose)
771 GtkTextBuffer *buffer;
772 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
778 buffer = gtk_text_view_get_buffer(text);
780 if (prefs_common.enable_color) {
781 /* grab the quote colors, converting from an int to a GdkColor */
782 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
784 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
786 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
788 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
790 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
792 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
794 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
796 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
799 signature_color = quote_color1 = quote_color2 = quote_color3 =
800 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
803 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
804 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
805 "foreground-gdk", "e_color1,
806 "paragraph-background-gdk", "e_bgcolor1,
808 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
809 "foreground-gdk", "e_color2,
810 "paragraph-background-gdk", "e_bgcolor2,
812 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
813 "foreground-gdk", "e_color3,
814 "paragraph-background-gdk", "e_bgcolor3,
817 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
818 "foreground-gdk", "e_color1,
820 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
821 "foreground-gdk", "e_color2,
823 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
824 "foreground-gdk", "e_color3,
828 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
829 "foreground-gdk", &signature_color,
832 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
833 "foreground-gdk", &uri_color,
835 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
836 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
838 color[0] = quote_color1;
839 color[1] = quote_color2;
840 color[2] = quote_color3;
841 color[3] = quote_bgcolor1;
842 color[4] = quote_bgcolor2;
843 color[5] = quote_bgcolor3;
844 color[6] = signature_color;
845 color[7] = uri_color;
846 cmap = gdk_drawable_get_colormap(compose->window->window);
847 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
849 for (i = 0; i < 8; i++) {
850 if (success[i] == FALSE) {
853 g_warning("Compose: color allocation failed.\n");
854 style = gtk_widget_get_style(GTK_WIDGET(text));
855 quote_color1 = quote_color2 = quote_color3 =
856 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
857 signature_color = uri_color = black;
862 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
863 GPtrArray *attach_files)
865 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
868 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
870 return compose_generic_new(account, mailto, item, NULL, NULL);
873 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
875 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
878 #define SCROLL_TO_CURSOR(compose) { \
879 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
880 gtk_text_view_get_buffer( \
881 GTK_TEXT_VIEW(compose->text))); \
882 gtk_text_view_scroll_mark_onscreen( \
883 GTK_TEXT_VIEW(compose->text), \
887 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
888 GPtrArray *attach_files, GList *listAddress )
891 GtkTextView *textview;
892 GtkTextBuffer *textbuf;
894 const gchar *subject_format = NULL;
895 const gchar *body_format = NULL;
896 gchar *mailto_from = NULL;
897 PrefsAccount *mailto_account = NULL;
898 MsgInfo* dummyinfo = NULL;
900 /* check if mailto defines a from */
901 if (mailto && *mailto != '\0') {
902 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL);
903 /* mailto defines a from, check if we can get account prefs from it,
904 if not, the account prefs will be guessed using other ways, but we'll keep
907 mailto_account = account_find_from_address(mailto_from, TRUE);
909 account = mailto_account;
912 /* if no account prefs set from mailto, set if from folder prefs (if any) */
913 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
914 account = account_find_from_id(item->prefs->default_account);
916 /* if no account prefs set, fallback to the current one */
917 if (!account) account = cur_account;
918 g_return_val_if_fail(account != NULL, NULL);
920 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
922 /* override from name if mailto asked for it */
924 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
927 /* override from name according to folder properties */
928 if (item && item->prefs &&
929 item->prefs->compose_with_format &&
930 item->prefs->compose_override_from_format &&
931 *item->prefs->compose_override_from_format != '\0') {
936 dummyinfo = compose_msginfo_new_from_compose(compose);
938 /* decode \-escape sequences in the internal representation of the quote format */
939 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
940 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
943 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
946 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
948 quote_fmt_scan_string(tmp);
951 buf = quote_fmt_get_buffer();
953 alertpanel_error(_("New message From format error."));
955 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
956 quote_fmt_reset_vartable();
961 compose->replyinfo = NULL;
962 compose->fwdinfo = NULL;
964 textview = GTK_TEXT_VIEW(compose->text);
965 textbuf = gtk_text_view_get_buffer(textview);
966 compose_create_tags(textview, compose);
968 undo_block(compose->undostruct);
970 compose_set_dictionaries_from_folder_prefs(compose, item);
973 if (account->auto_sig)
974 compose_insert_sig(compose, FALSE);
975 gtk_text_buffer_get_start_iter(textbuf, &iter);
976 gtk_text_buffer_place_cursor(textbuf, &iter);
978 if (account->protocol != A_NNTP) {
979 if (mailto && *mailto != '\0') {
980 compose_entries_set(compose, mailto, COMPOSE_TO);
982 } else if (item && item->prefs->enable_default_to) {
983 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
984 compose_entry_mark_default_to(compose, item->prefs->default_to);
986 if (item && item->ret_rcpt) {
987 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
990 if (mailto && *mailto != '\0') {
991 if (!strchr(mailto, '@'))
992 compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
994 compose_entries_set(compose, mailto, COMPOSE_TO);
995 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
996 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
999 * CLAWS: just don't allow return receipt request, even if the user
1000 * may want to send an email. simple but foolproof.
1002 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1004 compose_add_field_list( compose, listAddress );
1006 if (item && item->prefs && item->prefs->compose_with_format) {
1007 subject_format = item->prefs->compose_subject_format;
1008 body_format = item->prefs->compose_body_format;
1009 } else if (account->compose_with_format) {
1010 subject_format = account->compose_subject_format;
1011 body_format = account->compose_body_format;
1012 } else if (prefs_common.compose_with_format) {
1013 subject_format = prefs_common.compose_subject_format;
1014 body_format = prefs_common.compose_body_format;
1017 if (subject_format || body_format) {
1020 && *subject_format != '\0' )
1022 gchar *subject = NULL;
1027 dummyinfo = compose_msginfo_new_from_compose(compose);
1029 /* decode \-escape sequences in the internal representation of the quote format */
1030 tmp = malloc(strlen(subject_format)+1);
1031 pref_get_unescaped_pref(tmp, subject_format);
1033 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1035 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1036 compose->gtkaspell);
1038 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1040 quote_fmt_scan_string(tmp);
1043 buf = quote_fmt_get_buffer();
1045 alertpanel_error(_("New message subject format error."));
1047 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1048 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1049 quote_fmt_reset_vartable();
1056 && *body_format != '\0' )
1059 GtkTextBuffer *buffer;
1060 GtkTextIter start, end;
1064 dummyinfo = compose_msginfo_new_from_compose(compose);
1066 text = GTK_TEXT_VIEW(compose->text);
1067 buffer = gtk_text_view_get_buffer(text);
1068 gtk_text_buffer_get_start_iter(buffer, &start);
1069 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1070 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1072 compose_quote_fmt(compose, dummyinfo,
1074 NULL, tmp, FALSE, TRUE,
1075 _("New message body format error at line %d."));
1076 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1077 quote_fmt_reset_vartable();
1083 procmsg_msginfo_free( dummyinfo );
1089 for (i = 0; i < attach_files->len; i++) {
1090 file = g_ptr_array_index(attach_files, i);
1091 compose_attach_append(compose, file, file, NULL);
1095 compose_show_first_last_header(compose, TRUE);
1097 /* Set save folder */
1098 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1099 gchar *folderidentifier;
1101 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1102 folderidentifier = folder_item_get_identifier(item);
1103 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1104 g_free(folderidentifier);
1107 gtk_widget_grab_focus(compose->header_last->entry);
1109 undo_unblock(compose->undostruct);
1111 if (prefs_common.auto_exteditor)
1112 compose_exec_ext_editor(compose);
1114 compose->draft_timeout_tag = -1;
1115 SCROLL_TO_CURSOR(compose);
1117 compose->modified = FALSE;
1118 compose_set_title(compose);
1122 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1123 gboolean override_pref)
1125 gchar *privacy = NULL;
1127 g_return_if_fail(compose != NULL);
1128 g_return_if_fail(account != NULL);
1130 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1133 if (account->default_privacy_system
1134 && strlen(account->default_privacy_system)) {
1135 privacy = account->default_privacy_system;
1137 GSList *privacy_avail = privacy_get_system_ids();
1138 if (privacy_avail && g_slist_length(privacy_avail)) {
1139 privacy = (gchar *)(privacy_avail->data);
1142 if (privacy != NULL) {
1143 if (compose->privacy_system == NULL)
1144 compose->privacy_system = g_strdup(privacy);
1145 else if (*(compose->privacy_system) == '\0') {
1146 g_free(compose->privacy_system);
1147 compose->privacy_system = g_strdup(privacy);
1149 compose_update_privacy_system_menu_item(compose, FALSE);
1150 compose_use_encryption(compose, TRUE);
1154 static void compose_force_signing(Compose *compose, PrefsAccount *account)
1156 gchar *privacy = NULL;
1158 if (account->default_privacy_system
1159 && strlen(account->default_privacy_system)) {
1160 privacy = account->default_privacy_system;
1162 GSList *privacy_avail = privacy_get_system_ids();
1163 if (privacy_avail && g_slist_length(privacy_avail)) {
1164 privacy = (gchar *)(privacy_avail->data);
1167 if (privacy != NULL) {
1168 if (compose->privacy_system == NULL)
1169 compose->privacy_system = g_strdup(privacy);
1170 compose_update_privacy_system_menu_item(compose, FALSE);
1171 compose_use_signing(compose, TRUE);
1175 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1179 Compose *compose = NULL;
1181 g_return_val_if_fail(msginfo_list != NULL, NULL);
1183 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1184 g_return_val_if_fail(msginfo != NULL, NULL);
1186 list_len = g_slist_length(msginfo_list);
1190 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1191 FALSE, prefs_common.default_reply_list, FALSE, body);
1193 case COMPOSE_REPLY_WITH_QUOTE:
1194 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1195 FALSE, prefs_common.default_reply_list, FALSE, body);
1197 case COMPOSE_REPLY_WITHOUT_QUOTE:
1198 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1199 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1201 case COMPOSE_REPLY_TO_SENDER:
1202 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1203 FALSE, FALSE, TRUE, body);
1205 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1206 compose = compose_followup_and_reply_to(msginfo,
1207 COMPOSE_QUOTE_CHECK,
1208 FALSE, FALSE, body);
1210 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1211 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1212 FALSE, FALSE, TRUE, body);
1214 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1215 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1216 FALSE, FALSE, TRUE, NULL);
1218 case COMPOSE_REPLY_TO_ALL:
1219 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1220 TRUE, FALSE, FALSE, body);
1222 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1223 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1224 TRUE, FALSE, FALSE, body);
1226 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1227 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1228 TRUE, FALSE, FALSE, NULL);
1230 case COMPOSE_REPLY_TO_LIST:
1231 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1232 FALSE, TRUE, FALSE, body);
1234 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1235 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1236 FALSE, TRUE, FALSE, body);
1238 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1239 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1240 FALSE, TRUE, FALSE, NULL);
1242 case COMPOSE_FORWARD:
1243 if (prefs_common.forward_as_attachment) {
1244 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1247 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1251 case COMPOSE_FORWARD_INLINE:
1252 /* check if we reply to more than one Message */
1253 if (list_len == 1) {
1254 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1257 /* more messages FALL THROUGH */
1258 case COMPOSE_FORWARD_AS_ATTACH:
1259 compose = compose_forward_multiple(NULL, msginfo_list);
1261 case COMPOSE_REDIRECT:
1262 compose = compose_redirect(NULL, msginfo, FALSE);
1265 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1268 if (compose == NULL) {
1269 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1273 compose->rmode = mode;
1274 switch (compose->rmode) {
1276 case COMPOSE_REPLY_WITH_QUOTE:
1277 case COMPOSE_REPLY_WITHOUT_QUOTE:
1278 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1279 debug_print("reply mode Normal\n");
1280 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1281 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1283 case COMPOSE_REPLY_TO_SENDER:
1284 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1285 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1286 debug_print("reply mode Sender\n");
1287 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1289 case COMPOSE_REPLY_TO_ALL:
1290 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1291 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1292 debug_print("reply mode All\n");
1293 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1295 case COMPOSE_REPLY_TO_LIST:
1296 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1297 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1298 debug_print("reply mode List\n");
1299 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1307 static Compose *compose_reply(MsgInfo *msginfo,
1308 ComposeQuoteMode quote_mode,
1314 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1315 to_sender, FALSE, body);
1318 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1319 ComposeQuoteMode quote_mode,
1324 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1325 to_sender, TRUE, body);
1328 static void compose_extract_original_charset(Compose *compose)
1330 MsgInfo *info = NULL;
1331 if (compose->replyinfo) {
1332 info = compose->replyinfo;
1333 } else if (compose->fwdinfo) {
1334 info = compose->fwdinfo;
1335 } else if (compose->targetinfo) {
1336 info = compose->targetinfo;
1339 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1340 MimeInfo *partinfo = mimeinfo;
1341 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1342 partinfo = procmime_mimeinfo_next(partinfo);
1344 compose->orig_charset =
1345 g_strdup(procmime_mimeinfo_get_parameter(
1346 partinfo, "charset"));
1348 procmime_mimeinfo_free_all(mimeinfo);
1352 #define SIGNAL_BLOCK(buffer) { \
1353 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1354 G_CALLBACK(compose_changed_cb), \
1356 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1357 G_CALLBACK(text_inserted), \
1361 #define SIGNAL_UNBLOCK(buffer) { \
1362 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1363 G_CALLBACK(compose_changed_cb), \
1365 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1366 G_CALLBACK(text_inserted), \
1370 static Compose *compose_generic_reply(MsgInfo *msginfo,
1371 ComposeQuoteMode quote_mode,
1372 gboolean to_all, gboolean to_ml,
1374 gboolean followup_and_reply_to,
1378 PrefsAccount *account = NULL;
1379 GtkTextView *textview;
1380 GtkTextBuffer *textbuf;
1381 gboolean quote = FALSE;
1382 const gchar *qmark = NULL;
1383 const gchar *body_fmt = NULL;
1385 g_return_val_if_fail(msginfo != NULL, NULL);
1386 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1388 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1390 g_return_val_if_fail(account != NULL, NULL);
1392 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1394 compose->updating = TRUE;
1396 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1397 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1399 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1400 if (!compose->replyinfo)
1401 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1403 compose_extract_original_charset(compose);
1405 if (msginfo->folder && msginfo->folder->ret_rcpt)
1406 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1408 /* Set save folder */
1409 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1410 gchar *folderidentifier;
1412 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1413 folderidentifier = folder_item_get_identifier(msginfo->folder);
1414 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1415 g_free(folderidentifier);
1418 if (compose_parse_header(compose, msginfo) < 0) {
1419 compose->updating = FALSE;
1420 compose_destroy(compose);
1424 /* override from name according to folder properties */
1425 if (msginfo->folder && msginfo->folder->prefs &&
1426 msginfo->folder->prefs->reply_with_format &&
1427 msginfo->folder->prefs->reply_override_from_format &&
1428 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1433 /* decode \-escape sequences in the internal representation of the quote format */
1434 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1435 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1438 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1439 compose->gtkaspell);
1441 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1443 quote_fmt_scan_string(tmp);
1446 buf = quote_fmt_get_buffer();
1448 alertpanel_error(_("Message reply From format error."));
1450 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1451 quote_fmt_reset_vartable();
1456 textview = (GTK_TEXT_VIEW(compose->text));
1457 textbuf = gtk_text_view_get_buffer(textview);
1458 compose_create_tags(textview, compose);
1460 undo_block(compose->undostruct);
1462 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1465 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1466 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1467 /* use the reply format of folder (if enabled), or the account's one
1468 (if enabled) or fallback to the global reply format, which is always
1469 enabled (even if empty), and use the relevant quotemark */
1471 if (msginfo->folder && msginfo->folder->prefs &&
1472 msginfo->folder->prefs->reply_with_format) {
1473 qmark = msginfo->folder->prefs->reply_quotemark;
1474 body_fmt = msginfo->folder->prefs->reply_body_format;
1476 } else if (account->reply_with_format) {
1477 qmark = account->reply_quotemark;
1478 body_fmt = account->reply_body_format;
1481 qmark = prefs_common.quotemark;
1482 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1483 body_fmt = gettext(prefs_common.quotefmt);
1490 /* empty quotemark is not allowed */
1491 if (qmark == NULL || *qmark == '\0')
1493 compose_quote_fmt(compose, compose->replyinfo,
1494 body_fmt, qmark, body, FALSE, TRUE,
1495 _("Message reply format error at line %d."));
1496 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1497 quote_fmt_reset_vartable();
1500 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1501 compose_force_encryption(compose, account, FALSE);
1504 SIGNAL_BLOCK(textbuf);
1506 if (account->auto_sig)
1507 compose_insert_sig(compose, FALSE);
1509 compose_wrap_all(compose);
1511 SIGNAL_UNBLOCK(textbuf);
1513 gtk_widget_grab_focus(compose->text);
1515 undo_unblock(compose->undostruct);
1517 if (prefs_common.auto_exteditor)
1518 compose_exec_ext_editor(compose);
1520 compose->modified = FALSE;
1521 compose_set_title(compose);
1523 compose->updating = FALSE;
1524 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1525 SCROLL_TO_CURSOR(compose);
1527 if (compose->deferred_destroy) {
1528 compose_destroy(compose);
1535 #define INSERT_FW_HEADER(var, hdr) \
1536 if (msginfo->var && *msginfo->var) { \
1537 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1538 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1539 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1542 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1543 gboolean as_attach, const gchar *body,
1544 gboolean no_extedit,
1548 GtkTextView *textview;
1549 GtkTextBuffer *textbuf;
1552 g_return_val_if_fail(msginfo != NULL, NULL);
1553 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1556 !(account = compose_guess_forward_account_from_msginfo
1558 account = cur_account;
1560 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1562 compose->updating = TRUE;
1563 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1564 if (!compose->fwdinfo)
1565 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1567 compose_extract_original_charset(compose);
1569 if (msginfo->subject && *msginfo->subject) {
1570 gchar *buf, *buf2, *p;
1572 buf = p = g_strdup(msginfo->subject);
1573 p += subject_get_prefix_length(p);
1574 memmove(buf, p, strlen(p) + 1);
1576 buf2 = g_strdup_printf("Fw: %s", buf);
1577 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1583 /* override from name according to folder properties */
1584 if (msginfo->folder && msginfo->folder->prefs &&
1585 msginfo->folder->prefs->forward_with_format &&
1586 msginfo->folder->prefs->forward_override_from_format &&
1587 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1591 MsgInfo *full_msginfo = NULL;
1594 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1596 full_msginfo = procmsg_msginfo_copy(msginfo);
1598 /* decode \-escape sequences in the internal representation of the quote format */
1599 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1600 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1603 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1604 compose->gtkaspell);
1606 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1608 quote_fmt_scan_string(tmp);
1611 buf = quote_fmt_get_buffer();
1613 alertpanel_error(_("Message forward From format error."));
1615 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1616 quote_fmt_reset_vartable();
1619 procmsg_msginfo_free(full_msginfo);
1622 textview = GTK_TEXT_VIEW(compose->text);
1623 textbuf = gtk_text_view_get_buffer(textview);
1624 compose_create_tags(textview, compose);
1626 undo_block(compose->undostruct);
1630 msgfile = procmsg_get_message_file(msginfo);
1631 if (!is_file_exist(msgfile))
1632 g_warning("%s: file not exist\n", msgfile);
1634 compose_attach_append(compose, msgfile, msgfile,
1639 const gchar *qmark = NULL;
1640 const gchar *body_fmt = NULL;
1641 MsgInfo *full_msginfo;
1643 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1644 body_fmt = gettext(prefs_common.fw_quotefmt);
1648 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1650 full_msginfo = procmsg_msginfo_copy(msginfo);
1652 /* use the forward format of folder (if enabled), or the account's one
1653 (if enabled) or fallback to the global forward format, which is always
1654 enabled (even if empty), and use the relevant quotemark */
1655 if (msginfo->folder && msginfo->folder->prefs &&
1656 msginfo->folder->prefs->forward_with_format) {
1657 qmark = msginfo->folder->prefs->forward_quotemark;
1658 body_fmt = msginfo->folder->prefs->forward_body_format;
1660 } else if (account->forward_with_format) {
1661 qmark = account->forward_quotemark;
1662 body_fmt = account->forward_body_format;
1665 qmark = prefs_common.fw_quotemark;
1666 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1667 body_fmt = gettext(prefs_common.fw_quotefmt);
1672 /* empty quotemark is not allowed */
1673 if (qmark == NULL || *qmark == '\0')
1676 compose_quote_fmt(compose, full_msginfo,
1677 body_fmt, qmark, body, FALSE, TRUE,
1678 _("Message forward format error at line %d."));
1679 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1680 quote_fmt_reset_vartable();
1681 compose_attach_parts(compose, msginfo);
1683 procmsg_msginfo_free(full_msginfo);
1686 SIGNAL_BLOCK(textbuf);
1688 if (account->auto_sig)
1689 compose_insert_sig(compose, FALSE);
1691 compose_wrap_all(compose);
1693 SIGNAL_UNBLOCK(textbuf);
1695 gtk_text_buffer_get_start_iter(textbuf, &iter);
1696 gtk_text_buffer_place_cursor(textbuf, &iter);
1698 gtk_widget_grab_focus(compose->header_last->entry);
1700 if (!no_extedit && prefs_common.auto_exteditor)
1701 compose_exec_ext_editor(compose);
1704 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1705 gchar *folderidentifier;
1707 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1708 folderidentifier = folder_item_get_identifier(msginfo->folder);
1709 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1710 g_free(folderidentifier);
1713 undo_unblock(compose->undostruct);
1715 compose->modified = FALSE;
1716 compose_set_title(compose);
1718 compose->updating = FALSE;
1719 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1720 SCROLL_TO_CURSOR(compose);
1722 if (compose->deferred_destroy) {
1723 compose_destroy(compose);
1730 #undef INSERT_FW_HEADER
1732 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1735 GtkTextView *textview;
1736 GtkTextBuffer *textbuf;
1740 gboolean single_mail = TRUE;
1742 g_return_val_if_fail(msginfo_list != NULL, NULL);
1744 if (g_slist_length(msginfo_list) > 1)
1745 single_mail = FALSE;
1747 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1748 if (((MsgInfo *)msginfo->data)->folder == NULL)
1751 /* guess account from first selected message */
1753 !(account = compose_guess_forward_account_from_msginfo
1754 (msginfo_list->data)))
1755 account = cur_account;
1757 g_return_val_if_fail(account != NULL, NULL);
1759 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1760 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1761 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1764 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1766 compose->updating = TRUE;
1768 /* override from name according to folder properties */
1769 if (msginfo_list->data) {
1770 MsgInfo *msginfo = msginfo_list->data;
1772 if (msginfo->folder && msginfo->folder->prefs &&
1773 msginfo->folder->prefs->forward_with_format &&
1774 msginfo->folder->prefs->forward_override_from_format &&
1775 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1780 /* decode \-escape sequences in the internal representation of the quote format */
1781 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1782 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1785 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1786 compose->gtkaspell);
1788 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1790 quote_fmt_scan_string(tmp);
1793 buf = quote_fmt_get_buffer();
1795 alertpanel_error(_("Message forward From format error."));
1797 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1798 quote_fmt_reset_vartable();
1804 textview = GTK_TEXT_VIEW(compose->text);
1805 textbuf = gtk_text_view_get_buffer(textview);
1806 compose_create_tags(textview, compose);
1808 undo_block(compose->undostruct);
1809 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1810 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1812 if (!is_file_exist(msgfile))
1813 g_warning("%s: file not exist\n", msgfile);
1815 compose_attach_append(compose, msgfile, msgfile,
1821 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1822 if (info->subject && *info->subject) {
1823 gchar *buf, *buf2, *p;
1825 buf = p = g_strdup(info->subject);
1826 p += subject_get_prefix_length(p);
1827 memmove(buf, p, strlen(p) + 1);
1829 buf2 = g_strdup_printf("Fw: %s", buf);
1830 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1836 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1837 _("Fw: multiple emails"));
1840 SIGNAL_BLOCK(textbuf);
1842 if (account->auto_sig)
1843 compose_insert_sig(compose, FALSE);
1845 compose_wrap_all(compose);
1847 SIGNAL_UNBLOCK(textbuf);
1849 gtk_text_buffer_get_start_iter(textbuf, &iter);
1850 gtk_text_buffer_place_cursor(textbuf, &iter);
1852 gtk_widget_grab_focus(compose->header_last->entry);
1853 undo_unblock(compose->undostruct);
1854 compose->modified = FALSE;
1855 compose_set_title(compose);
1857 compose->updating = FALSE;
1858 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1859 SCROLL_TO_CURSOR(compose);
1861 if (compose->deferred_destroy) {
1862 compose_destroy(compose);
1869 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1871 GtkTextIter start = *iter;
1872 GtkTextIter end_iter;
1873 int start_pos = gtk_text_iter_get_offset(&start);
1875 if (!compose->account->sig_sep)
1878 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1879 start_pos+strlen(compose->account->sig_sep));
1881 /* check sig separator */
1882 str = gtk_text_iter_get_text(&start, &end_iter);
1883 if (!strcmp(str, compose->account->sig_sep)) {
1885 /* check end of line (\n) */
1886 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1887 start_pos+strlen(compose->account->sig_sep));
1888 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1889 start_pos+strlen(compose->account->sig_sep)+1);
1890 tmp = gtk_text_iter_get_text(&start, &end_iter);
1891 if (!strcmp(tmp,"\n")) {
1903 static void compose_colorize_signature(Compose *compose)
1905 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1907 GtkTextIter end_iter;
1908 gtk_text_buffer_get_start_iter(buffer, &iter);
1909 while (gtk_text_iter_forward_line(&iter))
1910 if (compose_is_sig_separator(compose, buffer, &iter)) {
1911 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1912 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1916 #define BLOCK_WRAP() { \
1917 prev_autowrap = compose->autowrap; \
1918 buffer = gtk_text_view_get_buffer( \
1919 GTK_TEXT_VIEW(compose->text)); \
1920 compose->autowrap = FALSE; \
1922 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1923 G_CALLBACK(compose_changed_cb), \
1925 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1926 G_CALLBACK(text_inserted), \
1929 #define UNBLOCK_WRAP() { \
1930 compose->autowrap = prev_autowrap; \
1931 if (compose->autowrap) { \
1932 gint old = compose->draft_timeout_tag; \
1933 compose->draft_timeout_tag = -2; \
1934 compose_wrap_all(compose); \
1935 compose->draft_timeout_tag = old; \
1938 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1939 G_CALLBACK(compose_changed_cb), \
1941 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1942 G_CALLBACK(text_inserted), \
1946 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
1948 Compose *compose = NULL;
1949 PrefsAccount *account = NULL;
1950 GtkTextView *textview;
1951 GtkTextBuffer *textbuf;
1955 gchar buf[BUFFSIZE];
1956 gboolean use_signing = FALSE;
1957 gboolean use_encryption = FALSE;
1958 gchar *privacy_system = NULL;
1959 int priority = PRIORITY_NORMAL;
1960 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
1962 g_return_val_if_fail(msginfo != NULL, NULL);
1963 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1965 if (compose_put_existing_to_front(msginfo)) {
1969 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
1970 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
1971 gchar queueheader_buf[BUFFSIZE];
1974 /* Select Account from queue headers */
1975 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1976 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
1977 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
1978 account = account_find_from_id(id);
1980 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1981 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
1982 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
1983 account = account_find_from_id(id);
1985 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1986 sizeof(queueheader_buf), "NAID:")) {
1987 id = atoi(&queueheader_buf[strlen("NAID:")]);
1988 account = account_find_from_id(id);
1990 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1991 sizeof(queueheader_buf), "MAID:")) {
1992 id = atoi(&queueheader_buf[strlen("MAID:")]);
1993 account = account_find_from_id(id);
1995 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1996 sizeof(queueheader_buf), "S:")) {
1997 account = account_find_from_address(queueheader_buf, FALSE);
1999 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2000 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2001 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2002 use_signing = param;
2005 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2006 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2007 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2008 use_signing = param;
2011 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2012 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2013 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2014 use_encryption = param;
2016 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2017 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2018 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2019 use_encryption = param;
2021 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2022 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2023 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2025 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2026 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2027 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2029 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2030 sizeof(queueheader_buf), "X-Priority: ")) {
2031 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2034 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2035 sizeof(queueheader_buf), "RMID:")) {
2036 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2037 if (tokens[0] && tokens[1] && tokens[2]) {
2038 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2039 if (orig_item != NULL) {
2040 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2045 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2046 sizeof(queueheader_buf), "FMID:")) {
2047 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2048 if (tokens[0] && tokens[1] && tokens[2]) {
2049 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2050 if (orig_item != NULL) {
2051 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2057 account = msginfo->folder->folder->account;
2060 if (!account && prefs_common.reedit_account_autosel) {
2061 gchar from[BUFFSIZE];
2062 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2063 extract_address(from);
2064 account = account_find_from_address(from, FALSE);
2068 account = cur_account;
2070 g_return_val_if_fail(account != NULL, NULL);
2072 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2074 compose->replyinfo = replyinfo;
2075 compose->fwdinfo = fwdinfo;
2077 compose->updating = TRUE;
2078 compose->priority = priority;
2080 if (privacy_system != NULL) {
2081 compose->privacy_system = privacy_system;
2082 compose_use_signing(compose, use_signing);
2083 compose_use_encryption(compose, use_encryption);
2084 compose_update_privacy_system_menu_item(compose, FALSE);
2086 activate_privacy_system(compose, account, FALSE);
2089 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2091 compose_extract_original_charset(compose);
2093 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2094 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2095 gchar queueheader_buf[BUFFSIZE];
2097 /* Set message save folder */
2098 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2101 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2102 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
2103 gtk_editable_insert_text(GTK_EDITABLE(compose->savemsg_entry), &queueheader_buf[4], strlen(&queueheader_buf[4]), &startpos);
2105 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2106 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2108 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2113 if (compose_parse_header(compose, msginfo) < 0) {
2114 compose->updating = FALSE;
2115 compose_destroy(compose);
2118 compose_reedit_set_entry(compose, msginfo);
2120 textview = GTK_TEXT_VIEW(compose->text);
2121 textbuf = gtk_text_view_get_buffer(textview);
2122 compose_create_tags(textview, compose);
2124 mark = gtk_text_buffer_get_insert(textbuf);
2125 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2127 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2128 G_CALLBACK(compose_changed_cb),
2131 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2132 fp = procmime_get_first_encrypted_text_content(msginfo);
2134 compose_force_encryption(compose, account, TRUE);
2137 fp = procmime_get_first_text_content(msginfo);
2140 g_warning("Can't get text part\n");
2144 gboolean prev_autowrap = compose->autowrap;
2145 GtkTextBuffer *buffer = textbuf;
2147 while (fgets(buf, sizeof(buf), fp) != NULL) {
2149 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2155 compose_attach_parts(compose, msginfo);
2157 compose_colorize_signature(compose);
2159 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2160 G_CALLBACK(compose_changed_cb),
2163 gtk_widget_grab_focus(compose->text);
2165 if (prefs_common.auto_exteditor) {
2166 compose_exec_ext_editor(compose);
2168 compose->modified = FALSE;
2169 compose_set_title(compose);
2171 compose->updating = FALSE;
2172 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2173 SCROLL_TO_CURSOR(compose);
2175 if (compose->deferred_destroy) {
2176 compose_destroy(compose);
2180 compose->sig_str = compose_get_signature_str(compose);
2185 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2192 g_return_val_if_fail(msginfo != NULL, NULL);
2195 account = account_get_reply_account(msginfo,
2196 prefs_common.reply_account_autosel);
2197 g_return_val_if_fail(account != NULL, NULL);
2199 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2201 compose->updating = TRUE;
2203 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2204 compose->replyinfo = NULL;
2205 compose->fwdinfo = NULL;
2207 compose_show_first_last_header(compose, TRUE);
2209 gtk_widget_grab_focus(compose->header_last->entry);
2211 filename = procmsg_get_message_file(msginfo);
2213 if (filename == NULL) {
2214 compose->updating = FALSE;
2215 compose_destroy(compose);
2220 compose->redirect_filename = filename;
2222 /* Set save folder */
2223 item = msginfo->folder;
2224 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2225 gchar *folderidentifier;
2227 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2228 folderidentifier = folder_item_get_identifier(item);
2229 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
2230 g_free(folderidentifier);
2233 compose_attach_parts(compose, msginfo);
2235 if (msginfo->subject)
2236 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2238 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2240 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2241 _("Message redirect format error at line %d."));
2242 quote_fmt_reset_vartable();
2243 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2245 compose_colorize_signature(compose);
2248 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2249 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2250 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2252 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2253 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2254 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2255 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2256 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2257 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2258 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2259 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2261 if (compose->toolbar->draft_btn)
2262 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2263 if (compose->toolbar->insert_btn)
2264 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2265 if (compose->toolbar->attach_btn)
2266 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2267 if (compose->toolbar->sig_btn)
2268 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2269 if (compose->toolbar->exteditor_btn)
2270 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2271 if (compose->toolbar->linewrap_current_btn)
2272 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2273 if (compose->toolbar->linewrap_all_btn)
2274 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2276 compose->modified = FALSE;
2277 compose_set_title(compose);
2278 compose->updating = FALSE;
2279 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2280 SCROLL_TO_CURSOR(compose);
2282 if (compose->deferred_destroy) {
2283 compose_destroy(compose);
2290 GList *compose_get_compose_list(void)
2292 return compose_list;
2295 void compose_entry_append(Compose *compose, const gchar *address,
2296 ComposeEntryType type)
2298 const gchar *header;
2300 gboolean in_quote = FALSE;
2301 if (!address || *address == '\0') return;
2308 header = N_("Bcc:");
2310 case COMPOSE_REPLYTO:
2311 header = N_("Reply-To:");
2313 case COMPOSE_NEWSGROUPS:
2314 header = N_("Newsgroups:");
2316 case COMPOSE_FOLLOWUPTO:
2317 header = N_( "Followup-To:");
2324 header = prefs_common_translated_header_name(header);
2326 cur = begin = (gchar *)address;
2328 /* we separate the line by commas, but not if we're inside a quoted
2330 while (*cur != '\0') {
2332 in_quote = !in_quote;
2333 if (*cur == ',' && !in_quote) {
2334 gchar *tmp = g_strdup(begin);
2336 tmp[cur-begin]='\0';
2339 while (*tmp == ' ' || *tmp == '\t')
2341 compose_add_header_entry(compose, header, tmp);
2348 gchar *tmp = g_strdup(begin);
2350 tmp[cur-begin]='\0';
2353 while (*tmp == ' ' || *tmp == '\t')
2355 compose_add_header_entry(compose, header, tmp);
2360 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2362 static GdkColor yellow;
2363 static GdkColor black;
2364 static gboolean yellow_initialised = FALSE;
2368 if (!yellow_initialised) {
2369 gdk_color_parse("#f5f6be", &yellow);
2370 gdk_color_parse("#000000", &black);
2371 yellow_initialised = gdk_colormap_alloc_color(
2372 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2373 yellow_initialised &= gdk_colormap_alloc_color(
2374 gdk_colormap_get_system(), &black, FALSE, TRUE);
2377 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2378 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2379 if (gtk_entry_get_text(entry) &&
2380 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2381 if (yellow_initialised) {
2382 gtk_widget_modify_base(
2383 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2384 GTK_STATE_NORMAL, &yellow);
2385 gtk_widget_modify_text(
2386 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2387 GTK_STATE_NORMAL, &black);
2393 void compose_toolbar_cb(gint action, gpointer data)
2395 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2396 Compose *compose = (Compose*)toolbar_item->parent;
2398 g_return_if_fail(compose != NULL);
2402 compose_send_cb(NULL, compose);
2405 compose_send_later_cb(NULL, compose);
2408 compose_draft(compose, COMPOSE_QUIT_EDITING);
2411 compose_insert_file_cb(NULL, compose);
2414 compose_attach_cb(NULL, compose);
2417 compose_insert_sig(compose, FALSE);
2420 compose_ext_editor_cb(NULL, compose);
2422 case A_LINEWRAP_CURRENT:
2423 compose_beautify_paragraph(compose, NULL, TRUE);
2425 case A_LINEWRAP_ALL:
2426 compose_wrap_all_full(compose, TRUE);
2429 compose_address_cb(NULL, compose);
2432 case A_CHECK_SPELLING:
2433 compose_check_all(NULL, compose);
2441 static void compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2446 gchar *subject = NULL;
2450 gchar **attach = NULL;
2452 /* get mailto parts but skip from */
2453 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2456 compose_entry_append(compose, to, to_type);
2458 compose_entry_append(compose, cc, COMPOSE_CC);
2460 compose_entry_append(compose, bcc, COMPOSE_BCC);
2462 if (!g_utf8_validate (subject, -1, NULL)) {
2463 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2464 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2467 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2471 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2472 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2475 gboolean prev_autowrap = compose->autowrap;
2477 compose->autowrap = FALSE;
2479 mark = gtk_text_buffer_get_insert(buffer);
2480 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2482 if (!g_utf8_validate (body, -1, NULL)) {
2483 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2484 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2487 gtk_text_buffer_insert(buffer, &iter, body, -1);
2489 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2491 compose->autowrap = prev_autowrap;
2492 if (compose->autowrap)
2493 compose_wrap_all(compose);
2497 gint i = 0, att = 0;
2498 gchar *warn_files = NULL;
2499 while (attach[i] != NULL) {
2500 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2501 if (utf8_filename) {
2502 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2503 gchar *tmp = g_strdup_printf("%s%s\n",
2504 warn_files?warn_files:"",
2510 g_free(utf8_filename);
2512 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2517 alertpanel_notice(ngettext(
2518 "The following file has been attached: \n%s",
2519 "The following files have been attached: \n%s", att), warn_files);
2531 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2533 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2534 {"Cc:", NULL, TRUE},
2535 {"References:", NULL, FALSE},
2536 {"Bcc:", NULL, TRUE},
2537 {"Newsgroups:", NULL, TRUE},
2538 {"Followup-To:", NULL, TRUE},
2539 {"List-Post:", NULL, FALSE},
2540 {"X-Priority:", NULL, FALSE},
2541 {NULL, NULL, FALSE}};
2557 g_return_val_if_fail(msginfo != NULL, -1);
2559 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2560 procheader_get_header_fields(fp, hentry);
2563 if (hentry[H_REPLY_TO].body != NULL) {
2564 if (hentry[H_REPLY_TO].body[0] != '\0') {
2566 conv_unmime_header(hentry[H_REPLY_TO].body,
2569 g_free(hentry[H_REPLY_TO].body);
2570 hentry[H_REPLY_TO].body = NULL;
2572 if (hentry[H_CC].body != NULL) {
2573 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2574 g_free(hentry[H_CC].body);
2575 hentry[H_CC].body = NULL;
2577 if (hentry[H_REFERENCES].body != NULL) {
2578 if (compose->mode == COMPOSE_REEDIT)
2579 compose->references = hentry[H_REFERENCES].body;
2581 compose->references = compose_parse_references
2582 (hentry[H_REFERENCES].body, msginfo->msgid);
2583 g_free(hentry[H_REFERENCES].body);
2585 hentry[H_REFERENCES].body = NULL;
2587 if (hentry[H_BCC].body != NULL) {
2588 if (compose->mode == COMPOSE_REEDIT)
2590 conv_unmime_header(hentry[H_BCC].body, NULL);
2591 g_free(hentry[H_BCC].body);
2592 hentry[H_BCC].body = NULL;
2594 if (hentry[H_NEWSGROUPS].body != NULL) {
2595 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2596 hentry[H_NEWSGROUPS].body = NULL;
2598 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2599 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2600 compose->followup_to =
2601 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2604 g_free(hentry[H_FOLLOWUP_TO].body);
2605 hentry[H_FOLLOWUP_TO].body = NULL;
2607 if (hentry[H_LIST_POST].body != NULL) {
2610 extract_address(hentry[H_LIST_POST].body);
2611 if (hentry[H_LIST_POST].body[0] != '\0') {
2612 scan_mailto_url(hentry[H_LIST_POST].body,
2613 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2615 g_free(compose->ml_post);
2616 compose->ml_post = to;
2619 g_free(hentry[H_LIST_POST].body);
2620 hentry[H_LIST_POST].body = NULL;
2623 /* CLAWS - X-Priority */
2624 if (compose->mode == COMPOSE_REEDIT)
2625 if (hentry[H_X_PRIORITY].body != NULL) {
2628 priority = atoi(hentry[H_X_PRIORITY].body);
2629 g_free(hentry[H_X_PRIORITY].body);
2631 hentry[H_X_PRIORITY].body = NULL;
2633 if (priority < PRIORITY_HIGHEST ||
2634 priority > PRIORITY_LOWEST)
2635 priority = PRIORITY_NORMAL;
2637 compose->priority = priority;
2640 if (compose->mode == COMPOSE_REEDIT) {
2641 if (msginfo->inreplyto && *msginfo->inreplyto)
2642 compose->inreplyto = g_strdup(msginfo->inreplyto);
2646 if (msginfo->msgid && *msginfo->msgid)
2647 compose->inreplyto = g_strdup(msginfo->msgid);
2649 if (!compose->references) {
2650 if (msginfo->msgid && *msginfo->msgid) {
2651 if (msginfo->inreplyto && *msginfo->inreplyto)
2652 compose->references =
2653 g_strdup_printf("<%s>\n\t<%s>",
2657 compose->references =
2658 g_strconcat("<", msginfo->msgid, ">",
2660 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2661 compose->references =
2662 g_strconcat("<", msginfo->inreplyto, ">",
2670 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2672 GSList *ref_id_list, *cur;
2676 ref_id_list = references_list_append(NULL, ref);
2677 if (!ref_id_list) return NULL;
2678 if (msgid && *msgid)
2679 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2684 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2685 /* "<" + Message-ID + ">" + CR+LF+TAB */
2686 len += strlen((gchar *)cur->data) + 5;
2688 if (len > MAX_REFERENCES_LEN) {
2689 /* remove second message-ID */
2690 if (ref_id_list && ref_id_list->next &&
2691 ref_id_list->next->next) {
2692 g_free(ref_id_list->next->data);
2693 ref_id_list = g_slist_remove
2694 (ref_id_list, ref_id_list->next->data);
2696 slist_free_strings(ref_id_list);
2697 g_slist_free(ref_id_list);
2704 new_ref = g_string_new("");
2705 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2706 if (new_ref->len > 0)
2707 g_string_append(new_ref, "\n\t");
2708 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2711 slist_free_strings(ref_id_list);
2712 g_slist_free(ref_id_list);
2714 new_ref_str = new_ref->str;
2715 g_string_free(new_ref, FALSE);
2720 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2721 const gchar *fmt, const gchar *qmark,
2722 const gchar *body, gboolean rewrap,
2723 gboolean need_unescape,
2724 const gchar *err_msg)
2726 MsgInfo* dummyinfo = NULL;
2727 gchar *quote_str = NULL;
2729 gboolean prev_autowrap;
2730 const gchar *trimmed_body = body;
2731 gint cursor_pos = -1;
2732 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2733 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2738 SIGNAL_BLOCK(buffer);
2741 dummyinfo = compose_msginfo_new_from_compose(compose);
2742 msginfo = dummyinfo;
2745 if (qmark != NULL) {
2747 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2748 compose->gtkaspell);
2750 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2752 quote_fmt_scan_string(qmark);
2755 buf = quote_fmt_get_buffer();
2757 alertpanel_error(_("Quote mark format error."));
2759 Xstrdup_a(quote_str, buf, goto error)
2762 if (fmt && *fmt != '\0') {
2765 while (*trimmed_body == '\n')
2769 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2770 compose->gtkaspell);
2772 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2774 if (need_unescape) {
2777 /* decode \-escape sequences in the internal representation of the quote format */
2778 tmp = malloc(strlen(fmt)+1);
2779 pref_get_unescaped_pref(tmp, fmt);
2780 quote_fmt_scan_string(tmp);
2784 quote_fmt_scan_string(fmt);
2788 buf = quote_fmt_get_buffer();
2790 gint line = quote_fmt_get_line();
2791 alertpanel_error(err_msg, line);
2797 prev_autowrap = compose->autowrap;
2798 compose->autowrap = FALSE;
2800 mark = gtk_text_buffer_get_insert(buffer);
2801 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2802 if (g_utf8_validate(buf, -1, NULL)) {
2803 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2805 gchar *tmpout = NULL;
2806 tmpout = conv_codeset_strdup
2807 (buf, conv_get_locale_charset_str_no_utf8(),
2809 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2811 tmpout = g_malloc(strlen(buf)*2+1);
2812 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2814 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2818 cursor_pos = quote_fmt_get_cursor_pos();
2819 if (cursor_pos == -1)
2820 cursor_pos = gtk_text_iter_get_offset(&iter);
2821 compose->set_cursor_pos = cursor_pos;
2823 gtk_text_buffer_get_start_iter(buffer, &iter);
2824 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2825 gtk_text_buffer_place_cursor(buffer, &iter);
2827 compose->autowrap = prev_autowrap;
2828 if (compose->autowrap && rewrap)
2829 compose_wrap_all(compose);
2836 SIGNAL_UNBLOCK(buffer);
2838 procmsg_msginfo_free( dummyinfo );
2843 /* if ml_post is of type addr@host and from is of type
2844 * addr-anything@host, return TRUE
2846 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2848 gchar *left_ml = NULL;
2849 gchar *right_ml = NULL;
2850 gchar *left_from = NULL;
2851 gchar *right_from = NULL;
2852 gboolean result = FALSE;
2854 if (!ml_post || !from)
2857 left_ml = g_strdup(ml_post);
2858 if (strstr(left_ml, "@")) {
2859 right_ml = strstr(left_ml, "@")+1;
2860 *(strstr(left_ml, "@")) = '\0';
2863 left_from = g_strdup(from);
2864 if (strstr(left_from, "@")) {
2865 right_from = strstr(left_from, "@")+1;
2866 *(strstr(left_from, "@")) = '\0';
2869 if (left_ml && left_from && right_ml && right_from
2870 && !strncmp(left_from, left_ml, strlen(left_ml))
2871 && !strcmp(right_from, right_ml)) {
2880 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2882 gchar *my_addr1, *my_addr2;
2884 if (!addr1 || !addr2)
2887 Xstrdup_a(my_addr1, addr1, return FALSE);
2888 Xstrdup_a(my_addr2, addr2, return FALSE);
2890 extract_address(my_addr1);
2891 extract_address(my_addr2);
2893 return !strcasecmp(my_addr1, my_addr2);
2896 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2897 gboolean to_all, gboolean to_ml,
2899 gboolean followup_and_reply_to)
2901 GSList *cc_list = NULL;
2904 gchar *replyto = NULL;
2905 GHashTable *to_table;
2907 gboolean reply_to_ml = FALSE;
2908 gboolean default_reply_to = FALSE;
2910 g_return_if_fail(compose->account != NULL);
2911 g_return_if_fail(msginfo != NULL);
2913 reply_to_ml = to_ml && compose->ml_post;
2915 default_reply_to = msginfo->folder &&
2916 msginfo->folder->prefs->enable_default_reply_to;
2918 if (compose->account->protocol != A_NNTP) {
2919 if (reply_to_ml && !default_reply_to) {
2921 gboolean is_subscr = is_subscription(compose->ml_post,
2924 /* normal answer to ml post with a reply-to */
2925 compose_entry_append(compose,
2928 if (compose->replyto
2929 && !same_address(compose->ml_post, compose->replyto))
2930 compose_entry_append(compose,
2934 /* answer to subscription confirmation */
2935 if (compose->replyto)
2936 compose_entry_append(compose,
2939 else if (msginfo->from)
2940 compose_entry_append(compose,
2945 else if (!(to_all || to_sender) && default_reply_to) {
2946 compose_entry_append(compose,
2947 msginfo->folder->prefs->default_reply_to,
2949 compose_entry_mark_default_to(compose,
2950 msginfo->folder->prefs->default_reply_to);
2955 Xstrdup_a(tmp1, msginfo->from, return);
2956 extract_address(tmp1);
2957 if (to_all || to_sender ||
2958 !account_find_from_address(tmp1, FALSE))
2959 compose_entry_append(compose,
2960 (compose->replyto && !to_sender)
2961 ? compose->replyto :
2962 msginfo->from ? msginfo->from : "",
2964 else if (!to_all && !to_sender) {
2965 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2966 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
2967 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2968 if (compose->replyto) {
2969 compose_entry_append(compose,
2973 compose_entry_append(compose,
2974 msginfo->from ? msginfo->from : "",
2978 /* replying to own mail, use original recp */
2979 compose_entry_append(compose,
2980 msginfo->to ? msginfo->to : "",
2982 compose_entry_append(compose,
2983 msginfo->cc ? msginfo->cc : "",
2989 if (to_sender || (compose->followup_to &&
2990 !strncmp(compose->followup_to, "poster", 6)))
2991 compose_entry_append
2993 (compose->replyto ? compose->replyto :
2994 msginfo->from ? msginfo->from : ""),
2997 else if (followup_and_reply_to || to_all) {
2998 compose_entry_append
3000 (compose->replyto ? compose->replyto :
3001 msginfo->from ? msginfo->from : ""),
3004 compose_entry_append
3006 compose->followup_to ? compose->followup_to :
3007 compose->newsgroups ? compose->newsgroups : "",
3008 COMPOSE_NEWSGROUPS);
3011 compose_entry_append
3013 compose->followup_to ? compose->followup_to :
3014 compose->newsgroups ? compose->newsgroups : "",
3015 COMPOSE_NEWSGROUPS);
3018 if (msginfo->subject && *msginfo->subject) {
3022 buf = p = g_strdup(msginfo->subject);
3023 p += subject_get_prefix_length(p);
3024 memmove(buf, p, strlen(p) + 1);
3026 buf2 = g_strdup_printf("Re: %s", buf);
3027 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3032 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3034 if (to_ml && compose->ml_post) return;
3035 if (!to_all || compose->account->protocol == A_NNTP) return;
3037 if (compose->replyto) {
3038 Xstrdup_a(replyto, compose->replyto, return);
3039 extract_address(replyto);
3041 if (msginfo->from) {
3042 Xstrdup_a(from, msginfo->from, return);
3043 extract_address(from);
3046 if (replyto && from)
3047 cc_list = address_list_append_with_comments(cc_list, from);
3048 if (to_all && msginfo->folder &&
3049 msginfo->folder->prefs->enable_default_reply_to)
3050 cc_list = address_list_append_with_comments(cc_list,
3051 msginfo->folder->prefs->default_reply_to);
3052 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3053 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3055 to_table = g_hash_table_new(g_str_hash, g_str_equal);
3057 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
3058 if (compose->account) {
3059 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
3060 GINT_TO_POINTER(1));
3062 /* remove address on To: and that of current account */
3063 for (cur = cc_list; cur != NULL; ) {
3064 GSList *next = cur->next;
3067 addr = g_utf8_strdown(cur->data, -1);
3068 extract_address(addr);
3070 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3071 cc_list = g_slist_remove(cc_list, cur->data);
3073 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3077 hash_free_strings(to_table);
3078 g_hash_table_destroy(to_table);
3081 for (cur = cc_list; cur != NULL; cur = cur->next)
3082 compose_entry_append(compose, (gchar *)cur->data,
3084 slist_free_strings(cc_list);
3085 g_slist_free(cc_list);
3090 #define SET_ENTRY(entry, str) \
3093 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3096 #define SET_ADDRESS(type, str) \
3099 compose_entry_append(compose, str, type); \
3102 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3104 g_return_if_fail(msginfo != NULL);
3106 SET_ENTRY(subject_entry, msginfo->subject);
3107 SET_ENTRY(from_name, msginfo->from);
3108 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3109 SET_ADDRESS(COMPOSE_CC, compose->cc);
3110 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3111 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3112 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3113 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3115 compose_update_priority_menu_item(compose);
3116 compose_update_privacy_system_menu_item(compose, FALSE);
3117 compose_show_first_last_header(compose, TRUE);
3123 static void compose_insert_sig(Compose *compose, gboolean replace)
3125 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3126 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3128 GtkTextIter iter, iter_end;
3129 gint cur_pos, ins_pos;
3130 gboolean prev_autowrap;
3131 gboolean found = FALSE;
3132 gboolean exists = FALSE;
3134 g_return_if_fail(compose->account != NULL);
3138 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3139 G_CALLBACK(compose_changed_cb),
3142 mark = gtk_text_buffer_get_insert(buffer);
3143 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3144 cur_pos = gtk_text_iter_get_offset (&iter);
3147 gtk_text_buffer_get_end_iter(buffer, &iter);
3149 exists = (compose->sig_str != NULL);
3152 GtkTextIter first_iter, start_iter, end_iter;
3154 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3156 if (!exists || compose->sig_str[0] == '\0')
3159 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3160 compose->signature_tag);
3163 /* include previous \n\n */
3164 gtk_text_iter_backward_chars(&first_iter, 1);
3165 start_iter = first_iter;
3166 end_iter = first_iter;
3168 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3169 compose->signature_tag);
3170 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3171 compose->signature_tag);
3173 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3179 g_free(compose->sig_str);
3180 compose->sig_str = compose_get_signature_str(compose);
3182 cur_pos = gtk_text_iter_get_offset(&iter);
3184 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3185 g_free(compose->sig_str);
3186 compose->sig_str = NULL;
3188 if (compose->sig_inserted == FALSE)
3189 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3190 compose->sig_inserted = TRUE;
3192 cur_pos = gtk_text_iter_get_offset(&iter);
3193 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3195 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3196 gtk_text_iter_forward_chars(&iter, 1);
3197 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3198 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3200 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3201 cur_pos = gtk_text_buffer_get_char_count (buffer);
3204 /* put the cursor where it should be
3205 * either where the quote_fmt says, either where it was */
3206 if (compose->set_cursor_pos < 0)
3207 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3209 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3210 compose->set_cursor_pos);
3212 gtk_text_buffer_place_cursor(buffer, &iter);
3213 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3214 G_CALLBACK(compose_changed_cb),
3220 static gchar *compose_get_signature_str(Compose *compose)
3222 gchar *sig_body = NULL;
3223 gchar *sig_str = NULL;
3224 gchar *utf8_sig_str = NULL;
3226 g_return_val_if_fail(compose->account != NULL, NULL);
3228 if (!compose->account->sig_path)
3231 if (compose->account->sig_type == SIG_FILE) {
3232 if (!is_file_or_fifo_exist(compose->account->sig_path)) {
3233 g_warning("can't open signature file: %s\n",
3234 compose->account->sig_path);
3239 if (compose->account->sig_type == SIG_COMMAND)
3240 sig_body = get_command_output(compose->account->sig_path);
3244 tmp = file_read_to_str(compose->account->sig_path);
3247 sig_body = normalize_newlines(tmp);
3251 if (compose->account->sig_sep) {
3252 sig_str = g_strconcat("\n", compose->account->sig_sep, "\n", sig_body,
3256 sig_str = g_strconcat("\n", sig_body, NULL);
3259 if (g_utf8_validate(sig_str, -1, NULL) == TRUE)
3260 utf8_sig_str = sig_str;
3262 utf8_sig_str = conv_codeset_strdup
3263 (sig_str, conv_get_locale_charset_str_no_utf8(),
3269 return utf8_sig_str;
3272 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3275 GtkTextBuffer *buffer;
3278 const gchar *cur_encoding;
3279 gchar buf[BUFFSIZE];
3282 gboolean prev_autowrap;
3283 gboolean badtxt = FALSE;
3285 g_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3287 if ((fp = g_fopen(file, "rb")) == NULL) {
3288 FILE_OP_ERROR(file, "fopen");
3289 return COMPOSE_INSERT_READ_ERROR;
3292 prev_autowrap = compose->autowrap;
3293 compose->autowrap = FALSE;
3295 text = GTK_TEXT_VIEW(compose->text);
3296 buffer = gtk_text_view_get_buffer(text);
3297 mark = gtk_text_buffer_get_insert(buffer);
3298 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3300 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3301 G_CALLBACK(text_inserted),
3304 cur_encoding = conv_get_locale_charset_str_no_utf8();
3306 while (fgets(buf, sizeof(buf), fp) != NULL) {
3309 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3310 str = g_strdup(buf);
3312 str = conv_codeset_strdup
3313 (buf, cur_encoding, CS_INTERNAL);
3316 /* strip <CR> if DOS/Windows file,
3317 replace <CR> with <LF> if Macintosh file. */
3320 if (len > 0 && str[len - 1] != '\n') {
3322 if (str[len] == '\r') str[len] = '\n';
3325 gtk_text_buffer_insert(buffer, &iter, str, -1);
3329 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3330 G_CALLBACK(text_inserted),
3332 compose->autowrap = prev_autowrap;
3333 if (compose->autowrap)
3334 compose_wrap_all(compose);
3339 return COMPOSE_INSERT_INVALID_CHARACTER;
3341 return COMPOSE_INSERT_SUCCESS;
3344 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3345 const gchar *filename,
3346 const gchar *content_type)
3354 GtkListStore *store;
3356 gboolean has_binary = FALSE;
3358 if (!is_file_exist(file)) {
3359 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3360 gboolean result = FALSE;
3361 if (file_from_uri && is_file_exist(file_from_uri)) {
3362 result = compose_attach_append(
3363 compose, file_from_uri,
3367 g_free(file_from_uri);
3370 alertpanel_error("File %s doesn't exist\n", filename);
3373 if ((size = get_file_size(file)) < 0) {
3374 alertpanel_error("Can't get file size of %s\n", filename);
3378 alertpanel_error(_("File %s is empty."), filename);
3381 if ((fp = g_fopen(file, "rb")) == NULL) {
3382 alertpanel_error(_("Can't read %s."), filename);
3387 ainfo = g_new0(AttachInfo, 1);
3388 auto_ainfo = g_auto_pointer_new_with_free
3389 (ainfo, (GFreeFunc) compose_attach_info_free);
3390 ainfo->file = g_strdup(file);
3393 ainfo->content_type = g_strdup(content_type);
3394 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3396 MsgFlags flags = {0, 0};
3398 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3399 ainfo->encoding = ENC_7BIT;
3401 ainfo->encoding = ENC_8BIT;
3403 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3404 if (msginfo && msginfo->subject)
3405 name = g_strdup(msginfo->subject);
3407 name = g_path_get_basename(filename ? filename : file);
3409 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3411 procmsg_msginfo_free(msginfo);
3413 if (!g_ascii_strncasecmp(content_type, "text", 4))
3414 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3416 ainfo->encoding = ENC_BASE64;
3417 name = g_path_get_basename(filename ? filename : file);
3418 ainfo->name = g_strdup(name);
3422 ainfo->content_type = procmime_get_mime_type(file);
3423 if (!ainfo->content_type) {
3424 ainfo->content_type =
3425 g_strdup("application/octet-stream");
3426 ainfo->encoding = ENC_BASE64;
3427 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3429 procmime_get_encoding_for_text_file(file, &has_binary);
3431 ainfo->encoding = ENC_BASE64;
3432 name = g_path_get_basename(filename ? filename : file);
3433 ainfo->name = g_strdup(name);
3437 if (ainfo->name != NULL
3438 && !strcmp(ainfo->name, ".")) {
3439 g_free(ainfo->name);
3443 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3444 g_free(ainfo->content_type);
3445 ainfo->content_type = g_strdup("application/octet-stream");
3448 ainfo->size = (goffset)size;
3449 size_text = to_human_readable((goffset)size);
3451 store = GTK_LIST_STORE(gtk_tree_view_get_model
3452 (GTK_TREE_VIEW(compose->attach_clist)));
3454 gtk_list_store_append(store, &iter);
3455 gtk_list_store_set(store, &iter,
3456 COL_MIMETYPE, ainfo->content_type,
3457 COL_SIZE, size_text,
3458 COL_NAME, ainfo->name,
3460 COL_AUTODATA, auto_ainfo,
3463 g_auto_pointer_free(auto_ainfo);
3464 compose_attach_update_label(compose);
3468 static void compose_use_signing(Compose *compose, gboolean use_signing)
3470 compose->use_signing = use_signing;
3471 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3474 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3476 compose->use_encryption = use_encryption;
3477 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3480 #define NEXT_PART_NOT_CHILD(info) \
3482 node = info->node; \
3483 while (node->children) \
3484 node = g_node_last_child(node); \
3485 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3488 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3492 MimeInfo *firsttext = NULL;
3493 MimeInfo *encrypted = NULL;
3496 const gchar *partname = NULL;
3498 mimeinfo = procmime_scan_message(msginfo);
3499 if (!mimeinfo) return;
3501 if (mimeinfo->node->children == NULL) {
3502 procmime_mimeinfo_free_all(mimeinfo);
3506 /* find first content part */
3507 child = (MimeInfo *) mimeinfo->node->children->data;
3508 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3509 child = (MimeInfo *)child->node->children->data;
3511 if (child->type == MIMETYPE_TEXT) {
3513 debug_print("First text part found\n");
3514 } else if (compose->mode == COMPOSE_REEDIT &&
3515 child->type == MIMETYPE_APPLICATION &&
3516 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3517 encrypted = (MimeInfo *)child->node->parent->data;
3520 child = (MimeInfo *) mimeinfo->node->children->data;
3521 while (child != NULL) {
3524 if (child == encrypted) {
3525 /* skip this part of tree */
3526 NEXT_PART_NOT_CHILD(child);
3530 if (child->type == MIMETYPE_MULTIPART) {
3531 /* get the actual content */
3532 child = procmime_mimeinfo_next(child);
3536 if (child == firsttext) {
3537 child = procmime_mimeinfo_next(child);
3541 outfile = procmime_get_tmp_file_name(child);
3542 if ((err = procmime_get_part(outfile, child)) < 0)
3543 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3545 gchar *content_type;
3547 content_type = procmime_get_content_type_str(child->type, child->subtype);
3549 /* if we meet a pgp signature, we don't attach it, but
3550 * we force signing. */
3551 if ((strcmp(content_type, "application/pgp-signature") &&
3552 strcmp(content_type, "application/pkcs7-signature") &&
3553 strcmp(content_type, "application/x-pkcs7-signature"))
3554 || compose->mode == COMPOSE_REDIRECT) {
3555 partname = procmime_mimeinfo_get_parameter(child, "filename");
3556 if (partname == NULL)
3557 partname = procmime_mimeinfo_get_parameter(child, "name");
3558 if (partname == NULL)
3560 compose_attach_append(compose, outfile,
3561 partname, content_type);
3563 compose_force_signing(compose, compose->account);
3565 g_free(content_type);
3568 NEXT_PART_NOT_CHILD(child);
3570 procmime_mimeinfo_free_all(mimeinfo);
3573 #undef NEXT_PART_NOT_CHILD
3578 WAIT_FOR_INDENT_CHAR,
3579 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3582 /* return indent length, we allow:
3583 indent characters followed by indent characters or spaces/tabs,
3584 alphabets and numbers immediately followed by indent characters,
3585 and the repeating sequences of the above
3586 If quote ends with multiple spaces, only the first one is included. */
3587 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3588 const GtkTextIter *start, gint *len)
3590 GtkTextIter iter = *start;
3594 IndentState state = WAIT_FOR_INDENT_CHAR;
3597 gint alnum_count = 0;
3598 gint space_count = 0;
3601 if (prefs_common.quote_chars == NULL) {
3605 while (!gtk_text_iter_ends_line(&iter)) {
3606 wc = gtk_text_iter_get_char(&iter);
3607 if (g_unichar_iswide(wc))
3609 clen = g_unichar_to_utf8(wc, ch);
3613 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3614 is_space = g_unichar_isspace(wc);
3616 if (state == WAIT_FOR_INDENT_CHAR) {
3617 if (!is_indent && !g_unichar_isalnum(wc))
3620 quote_len += alnum_count + space_count + 1;
3621 alnum_count = space_count = 0;
3622 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3625 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3626 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3630 else if (is_indent) {
3631 quote_len += alnum_count + space_count + 1;
3632 alnum_count = space_count = 0;
3635 state = WAIT_FOR_INDENT_CHAR;
3639 gtk_text_iter_forward_char(&iter);
3642 if (quote_len > 0 && space_count > 0)
3648 if (quote_len > 0) {
3650 gtk_text_iter_forward_chars(&iter, quote_len);
3651 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3657 /* return TRUE if the line is itemized */
3658 static gboolean compose_is_itemized(GtkTextBuffer *buffer,
3659 const GtkTextIter *start)
3661 GtkTextIter iter = *start;
3666 if (gtk_text_iter_ends_line(&iter))
3670 wc = gtk_text_iter_get_char(&iter);
3671 if (!g_unichar_isspace(wc))
3673 gtk_text_iter_forward_char(&iter);
3674 if (gtk_text_iter_ends_line(&iter))
3678 clen = g_unichar_to_utf8(wc, ch);
3682 if (!strchr("*-+", ch[0]))
3685 gtk_text_iter_forward_char(&iter);
3686 if (gtk_text_iter_ends_line(&iter))
3688 wc = gtk_text_iter_get_char(&iter);
3689 if (g_unichar_isspace(wc))
3695 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3696 const GtkTextIter *start,
3697 GtkTextIter *break_pos,
3701 GtkTextIter iter = *start, line_end = *start;
3702 PangoLogAttr *attrs;
3709 gboolean can_break = FALSE;
3710 gboolean do_break = FALSE;
3711 gboolean was_white = FALSE;
3712 gboolean prev_dont_break = FALSE;
3714 gtk_text_iter_forward_to_line_end(&line_end);
3715 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3716 len = g_utf8_strlen(str, -1);
3720 g_warning("compose_get_line_break_pos: len = 0!\n");
3724 /* g_print("breaking line: %d: %s (len = %d)\n",
3725 gtk_text_iter_get_line(&iter), str, len); */
3727 attrs = g_new(PangoLogAttr, len + 1);
3729 pango_default_break(str, -1, NULL, attrs, len + 1);
3733 /* skip quote and leading spaces */
3734 for (i = 0; *p != '\0' && i < len; i++) {
3737 wc = g_utf8_get_char(p);
3738 if (i >= quote_len && !g_unichar_isspace(wc))
3740 if (g_unichar_iswide(wc))
3742 else if (*p == '\t')
3746 p = g_utf8_next_char(p);
3749 for (; *p != '\0' && i < len; i++) {
3750 PangoLogAttr *attr = attrs + i;
3754 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3757 was_white = attr->is_white;
3759 /* don't wrap URI */
3760 if ((uri_len = get_uri_len(p)) > 0) {
3762 if (pos > 0 && col > max_col) {
3772 wc = g_utf8_get_char(p);
3773 if (g_unichar_iswide(wc)) {
3775 if (prev_dont_break && can_break && attr->is_line_break)
3777 } else if (*p == '\t')
3781 if (pos > 0 && col > max_col) {
3786 if (*p == '-' || *p == '/')
3787 prev_dont_break = TRUE;
3789 prev_dont_break = FALSE;
3791 p = g_utf8_next_char(p);
3795 debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3800 *break_pos = *start;
3801 gtk_text_iter_set_line_offset(break_pos, pos);
3806 static gboolean compose_join_next_line(Compose *compose,
3807 GtkTextBuffer *buffer,
3809 const gchar *quote_str)
3811 GtkTextIter iter_ = *iter, cur, prev, next, end;
3812 PangoLogAttr attrs[3];
3814 gchar *next_quote_str;
3817 gboolean keep_cursor = FALSE;
3819 if (!gtk_text_iter_forward_line(&iter_) ||
3820 gtk_text_iter_ends_line(&iter_)) {
3823 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
3825 if ((quote_str || next_quote_str) &&
3826 strcmp2(quote_str, next_quote_str) != 0) {
3827 g_free(next_quote_str);
3830 g_free(next_quote_str);
3833 if (quote_len > 0) {
3834 gtk_text_iter_forward_chars(&end, quote_len);
3835 if (gtk_text_iter_ends_line(&end)) {
3840 /* don't join itemized lines */
3841 if (compose_is_itemized(buffer, &end)) {
3845 /* don't join signature separator */
3846 if (compose_is_sig_separator(compose, buffer, &iter_)) {
3849 /* delete quote str */
3851 gtk_text_buffer_delete(buffer, &iter_, &end);
3853 /* don't join line breaks put by the user */
3855 gtk_text_iter_backward_char(&cur);
3856 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
3857 gtk_text_iter_forward_char(&cur);
3861 gtk_text_iter_forward_char(&cur);
3862 /* delete linebreak and extra spaces */
3863 while (gtk_text_iter_backward_char(&cur)) {
3864 wc1 = gtk_text_iter_get_char(&cur);
3865 if (!g_unichar_isspace(wc1))
3870 while (!gtk_text_iter_ends_line(&cur)) {
3871 wc1 = gtk_text_iter_get_char(&cur);
3872 if (!g_unichar_isspace(wc1))
3874 gtk_text_iter_forward_char(&cur);
3877 if (!gtk_text_iter_equal(&prev, &next)) {
3880 mark = gtk_text_buffer_get_insert(buffer);
3881 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
3882 if (gtk_text_iter_equal(&prev, &cur))
3884 gtk_text_buffer_delete(buffer, &prev, &next);
3888 /* insert space if required */
3889 gtk_text_iter_backward_char(&prev);
3890 wc1 = gtk_text_iter_get_char(&prev);
3891 wc2 = gtk_text_iter_get_char(&next);
3892 gtk_text_iter_forward_char(&next);
3893 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
3894 pango_default_break(str, -1, NULL, attrs, 3);
3895 if (!attrs[1].is_line_break ||
3896 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
3897 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
3899 gtk_text_iter_backward_char(&iter_);
3900 gtk_text_buffer_place_cursor(buffer, &iter_);
3909 #define ADD_TXT_POS(bp_, ep_, pti_) \
3910 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
3911 last = last->next; \
3912 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
3913 last->next = NULL; \
3915 g_warning("alloc error scanning URIs\n"); \
3918 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
3920 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3921 GtkTextBuffer *buffer;
3922 GtkTextIter iter, break_pos, end_of_line;
3923 gchar *quote_str = NULL;
3925 gboolean wrap_quote = prefs_common.linewrap_quote;
3926 gboolean prev_autowrap = compose->autowrap;
3927 gint startq_offset = -1, noq_offset = -1;
3928 gint uri_start = -1, uri_stop = -1;
3929 gint nouri_start = -1, nouri_stop = -1;
3930 gint num_blocks = 0;
3931 gint quotelevel = -1;
3932 gboolean modified = force;
3933 gboolean removed = FALSE;
3934 gboolean modified_before_remove = FALSE;
3936 gboolean start = TRUE;
3941 if (compose->draft_timeout_tag == -2) {
3945 compose->autowrap = FALSE;
3947 buffer = gtk_text_view_get_buffer(text);
3948 undo_wrapping(compose->undostruct, TRUE);
3953 mark = gtk_text_buffer_get_insert(buffer);
3954 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3958 if (compose->draft_timeout_tag == -2) {
3959 if (gtk_text_iter_ends_line(&iter)) {
3960 while (gtk_text_iter_ends_line(&iter) &&
3961 gtk_text_iter_forward_line(&iter))
3964 while (gtk_text_iter_backward_line(&iter)) {
3965 if (gtk_text_iter_ends_line(&iter)) {
3966 gtk_text_iter_forward_line(&iter);
3972 /* move to line start */
3973 gtk_text_iter_set_line_offset(&iter, 0);
3975 /* go until paragraph end (empty line) */
3976 while (start || !gtk_text_iter_ends_line(&iter)) {
3977 gchar *scanpos = NULL;
3978 /* parse table - in order of priority */
3980 const gchar *needle; /* token */
3982 /* token search function */
3983 gchar *(*search) (const gchar *haystack,
3984 const gchar *needle);
3985 /* part parsing function */
3986 gboolean (*parse) (const gchar *start,
3987 const gchar *scanpos,
3991 /* part to URI function */
3992 gchar *(*build_uri) (const gchar *bp,
3996 static struct table parser[] = {
3997 {"http://", strcasestr, get_uri_part, make_uri_string},
3998 {"https://", strcasestr, get_uri_part, make_uri_string},
3999 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4000 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4001 {"www.", strcasestr, get_uri_part, make_http_string},
4002 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4003 {"@", strcasestr, get_email_part, make_email_string}
4005 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4006 gint last_index = PARSE_ELEMS;
4008 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4012 if (!prev_autowrap && num_blocks == 0) {
4014 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4015 G_CALLBACK(text_inserted),
4018 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4021 uri_start = uri_stop = -1;
4023 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4026 debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4027 if (startq_offset == -1)
4028 startq_offset = gtk_text_iter_get_offset(&iter);
4029 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4030 if (quotelevel > 2) {
4031 /* recycle colors */
4032 if (prefs_common.recycle_quote_colors)
4041 if (startq_offset == -1)
4042 noq_offset = gtk_text_iter_get_offset(&iter);
4046 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4049 if (gtk_text_iter_ends_line(&iter)) {
4051 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4052 prefs_common.linewrap_len,
4054 GtkTextIter prev, next, cur;
4056 if (prev_autowrap != FALSE || force) {
4057 compose->automatic_break = TRUE;
4059 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4060 compose->automatic_break = FALSE;
4061 } else if (quote_str && wrap_quote) {
4062 compose->automatic_break = TRUE;
4064 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4065 compose->automatic_break = FALSE;
4068 /* remove trailing spaces */
4070 gtk_text_iter_backward_char(&cur);
4072 while (!gtk_text_iter_starts_line(&cur)) {
4075 gtk_text_iter_backward_char(&cur);
4076 wc = gtk_text_iter_get_char(&cur);
4077 if (!g_unichar_isspace(wc))
4081 if (!gtk_text_iter_equal(&prev, &next)) {
4082 gtk_text_buffer_delete(buffer, &prev, &next);
4084 gtk_text_iter_forward_char(&break_pos);
4088 gtk_text_buffer_insert(buffer, &break_pos,
4092 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4094 /* move iter to current line start */
4095 gtk_text_iter_set_line_offset(&iter, 0);
4102 /* move iter to next line start */
4108 if (!prev_autowrap && num_blocks > 0) {
4110 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4111 G_CALLBACK(text_inserted),
4115 while (!gtk_text_iter_ends_line(&end_of_line)) {
4116 gtk_text_iter_forward_char(&end_of_line);
4118 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4120 nouri_start = gtk_text_iter_get_offset(&iter);
4121 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4123 walk_pos = gtk_text_iter_get_offset(&iter);
4124 /* FIXME: this looks phony. scanning for anything in the parse table */
4125 for (n = 0; n < PARSE_ELEMS; n++) {
4128 tmp = parser[n].search(walk, parser[n].needle);
4130 if (scanpos == NULL || tmp < scanpos) {
4139 /* check if URI can be parsed */
4140 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4141 (const gchar **)&ep, FALSE)
4142 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4146 strlen(parser[last_index].needle);
4149 uri_start = walk_pos + (bp - o_walk);
4150 uri_stop = walk_pos + (ep - o_walk);
4154 gtk_text_iter_forward_line(&iter);
4157 if (startq_offset != -1) {
4158 GtkTextIter startquote, endquote;
4159 gtk_text_buffer_get_iter_at_offset(
4160 buffer, &startquote, startq_offset);
4163 switch (quotelevel) {
4165 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4166 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4167 gtk_text_buffer_apply_tag_by_name(
4168 buffer, "quote0", &startquote, &endquote);
4169 gtk_text_buffer_remove_tag_by_name(
4170 buffer, "quote1", &startquote, &endquote);
4171 gtk_text_buffer_remove_tag_by_name(
4172 buffer, "quote2", &startquote, &endquote);
4177 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4178 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4179 gtk_text_buffer_apply_tag_by_name(
4180 buffer, "quote1", &startquote, &endquote);
4181 gtk_text_buffer_remove_tag_by_name(
4182 buffer, "quote0", &startquote, &endquote);
4183 gtk_text_buffer_remove_tag_by_name(
4184 buffer, "quote2", &startquote, &endquote);
4189 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4190 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4191 gtk_text_buffer_apply_tag_by_name(
4192 buffer, "quote2", &startquote, &endquote);
4193 gtk_text_buffer_remove_tag_by_name(
4194 buffer, "quote0", &startquote, &endquote);
4195 gtk_text_buffer_remove_tag_by_name(
4196 buffer, "quote1", &startquote, &endquote);
4202 } else if (noq_offset != -1) {
4203 GtkTextIter startnoquote, endnoquote;
4204 gtk_text_buffer_get_iter_at_offset(
4205 buffer, &startnoquote, noq_offset);
4208 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4209 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4210 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4211 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4212 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4213 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4214 gtk_text_buffer_remove_tag_by_name(
4215 buffer, "quote0", &startnoquote, &endnoquote);
4216 gtk_text_buffer_remove_tag_by_name(
4217 buffer, "quote1", &startnoquote, &endnoquote);
4218 gtk_text_buffer_remove_tag_by_name(
4219 buffer, "quote2", &startnoquote, &endnoquote);
4225 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4226 GtkTextIter nouri_start_iter, nouri_end_iter;
4227 gtk_text_buffer_get_iter_at_offset(
4228 buffer, &nouri_start_iter, nouri_start);
4229 gtk_text_buffer_get_iter_at_offset(
4230 buffer, &nouri_end_iter, nouri_stop);
4231 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4232 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4233 gtk_text_buffer_remove_tag_by_name(
4234 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4235 modified_before_remove = modified;
4240 if (uri_start >= 0 && uri_stop > 0) {
4241 GtkTextIter uri_start_iter, uri_end_iter, back;
4242 gtk_text_buffer_get_iter_at_offset(
4243 buffer, &uri_start_iter, uri_start);
4244 gtk_text_buffer_get_iter_at_offset(
4245 buffer, &uri_end_iter, uri_stop);
4246 back = uri_end_iter;
4247 gtk_text_iter_backward_char(&back);
4248 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4249 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4250 gtk_text_buffer_apply_tag_by_name(
4251 buffer, "link", &uri_start_iter, &uri_end_iter);
4253 if (removed && !modified_before_remove) {
4259 debug_print("not modified, out after %d lines\n", lines);
4264 debug_print("modified, out after %d lines\n", lines);
4268 undo_wrapping(compose->undostruct, FALSE);
4269 compose->autowrap = prev_autowrap;
4274 void compose_action_cb(void *data)
4276 Compose *compose = (Compose *)data;
4277 compose_wrap_all(compose);
4280 static void compose_wrap_all(Compose *compose)
4282 compose_wrap_all_full(compose, FALSE);
4285 static void compose_wrap_all_full(Compose *compose, gboolean force)
4287 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4288 GtkTextBuffer *buffer;
4290 gboolean modified = TRUE;
4292 buffer = gtk_text_view_get_buffer(text);
4294 gtk_text_buffer_get_start_iter(buffer, &iter);
4295 while (!gtk_text_iter_is_end(&iter) && modified)
4296 modified = compose_beautify_paragraph(compose, &iter, force);
4300 static void compose_set_title(Compose *compose)
4306 edited = compose->modified ? _(" [Edited]") : "";
4308 subject = gtk_editable_get_chars(
4309 GTK_EDITABLE(compose->subject_entry), 0, -1);
4311 #ifndef GENERIC_UMPC
4312 if (subject && strlen(subject))
4313 str = g_strdup_printf(_("%s - Compose message%s"),
4316 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4318 str = g_strdup(_("Compose message"));
4321 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4327 * compose_current_mail_account:
4329 * Find a current mail account (the currently selected account, or the
4330 * default account, if a news account is currently selected). If a
4331 * mail account cannot be found, display an error message.
4333 * Return value: Mail account, or NULL if not found.
4335 static PrefsAccount *
4336 compose_current_mail_account(void)
4340 if (cur_account && cur_account->protocol != A_NNTP)
4343 ac = account_get_default();
4344 if (!ac || ac->protocol == A_NNTP) {
4345 alertpanel_error(_("Account for sending mail is not specified.\n"
4346 "Please select a mail account before sending."));
4353 #define QUOTE_IF_REQUIRED(out, str) \
4355 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4359 len = strlen(str) + 3; \
4360 if ((__tmp = alloca(len)) == NULL) { \
4361 g_warning("can't allocate memory\n"); \
4362 g_string_free(header, TRUE); \
4365 g_snprintf(__tmp, len, "\"%s\"", str); \
4370 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4371 g_warning("can't allocate memory\n"); \
4372 g_string_free(header, TRUE); \
4375 strcpy(__tmp, str); \
4381 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4383 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4387 len = strlen(str) + 3; \
4388 if ((__tmp = alloca(len)) == NULL) { \
4389 g_warning("can't allocate memory\n"); \
4392 g_snprintf(__tmp, len, "\"%s\"", str); \
4397 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4398 g_warning("can't allocate memory\n"); \
4401 strcpy(__tmp, str); \
4407 static void compose_select_account(Compose *compose, PrefsAccount *account,
4412 g_return_if_fail(account != NULL);
4414 compose->account = account;
4416 if (account->name && *account->name) {
4418 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4419 from = g_strdup_printf("%s <%s>",
4420 buf, account->address);
4421 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4423 from = g_strdup_printf("<%s>",
4425 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4430 compose_set_title(compose);
4432 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4433 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4435 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4436 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4437 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4439 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4441 activate_privacy_system(compose, account, FALSE);
4443 if (!init && compose->mode != COMPOSE_REDIRECT) {
4444 undo_block(compose->undostruct);
4445 compose_insert_sig(compose, TRUE);
4446 undo_unblock(compose->undostruct);
4450 /* use account's dict info if set */
4451 if (compose->gtkaspell) {
4452 if (account->enable_default_dictionary)
4453 gtkaspell_change_dict(compose->gtkaspell,
4454 account->default_dictionary, FALSE);
4455 if (account->enable_default_alt_dictionary)
4456 gtkaspell_change_alt_dict(compose->gtkaspell,
4457 account->default_alt_dictionary);
4458 if (account->enable_default_dictionary
4459 || account->enable_default_alt_dictionary)
4460 compose_spell_menu_changed(compose);
4465 gboolean compose_check_for_valid_recipient(Compose *compose) {
4466 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4467 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4468 gboolean recipient_found = FALSE;
4472 /* free to and newsgroup list */
4473 slist_free_strings(compose->to_list);
4474 g_slist_free(compose->to_list);
4475 compose->to_list = NULL;
4477 slist_free_strings(compose->newsgroup_list);
4478 g_slist_free(compose->newsgroup_list);
4479 compose->newsgroup_list = NULL;
4481 /* search header entries for to and newsgroup entries */
4482 for (list = compose->header_list; list; list = list->next) {
4485 header = gtk_editable_get_chars(GTK_EDITABLE(GTK_BIN(((ComposeHeaderEntry *)list->data)->combo)->child), 0, -1);
4486 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4489 if (entry[0] != '\0') {
4490 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4491 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4492 compose->to_list = address_list_append(compose->to_list, entry);
4493 recipient_found = TRUE;
4496 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4497 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4498 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4499 recipient_found = TRUE;
4506 return recipient_found;
4509 static gboolean compose_check_for_set_recipients(Compose *compose)
4511 if (compose->account->set_autocc && compose->account->auto_cc) {
4512 gboolean found_other = FALSE;
4514 /* search header entries for to and newsgroup entries */
4515 for (list = compose->header_list; list; list = list->next) {
4518 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4519 header = gtk_editable_get_chars(GTK_EDITABLE(GTK_BIN(((ComposeHeaderEntry *)list->data)->combo)->child), 0, -1);
4522 if (strcmp(entry, compose->account->auto_cc)
4523 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4533 if (compose->batch) {
4534 gtk_widget_show_all(compose->window);
4536 aval = alertpanel(_("Send"),
4537 _("The only recipient is the default CC address. Send anyway?"),
4538 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4539 if (aval != G_ALERTALTERNATE)
4543 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4544 gboolean found_other = FALSE;
4546 /* search header entries for to and newsgroup entries */
4547 for (list = compose->header_list; list; list = list->next) {
4550 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4551 header = gtk_editable_get_chars(GTK_EDITABLE(GTK_BIN(((ComposeHeaderEntry *)list->data)->combo)->child), 0, -1);
4554 if (strcmp(entry, compose->account->auto_bcc)
4555 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4565 if (compose->batch) {
4566 gtk_widget_show_all(compose->window);
4568 aval = alertpanel(_("Send"),
4569 _("The only recipient is the default BCC address. Send anyway?"),
4570 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4571 if (aval != G_ALERTALTERNATE)
4578 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4582 if (compose_check_for_valid_recipient(compose) == FALSE) {
4583 if (compose->batch) {
4584 gtk_widget_show_all(compose->window);
4586 alertpanel_error(_("Recipient is not specified."));
4590 if (compose_check_for_set_recipients(compose) == FALSE) {
4594 if (!compose->batch) {
4595 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4596 if (*str == '\0' && check_everything == TRUE &&
4597 compose->mode != COMPOSE_REDIRECT) {
4599 gchar *button_label;
4602 if (compose->sending)
4603 button_label = _("+_Send");
4605 button_label = _("+_Queue");
4606 message = g_strdup_printf(_("Subject is empty. %s"),
4607 compose->sending?_("Send it anyway?"):
4608 _("Queue it anyway?"));
4610 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4611 GTK_STOCK_CANCEL, button_label, NULL);
4613 if (aval != G_ALERTALTERNATE)
4618 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4624 gint compose_send(Compose *compose)
4627 FolderItem *folder = NULL;
4629 gchar *msgpath = NULL;
4630 gboolean discard_window = FALSE;
4631 gchar *errstr = NULL;
4632 gchar *tmsgid = NULL;
4633 MainWindow *mainwin = mainwindow_get_mainwindow();
4634 gboolean queued_removed = FALSE;
4636 if (prefs_common.send_dialog_invisible
4637 || compose->batch == TRUE)
4638 discard_window = TRUE;
4640 compose_allow_user_actions (compose, FALSE);
4641 compose->sending = TRUE;
4643 if (compose_check_entries(compose, TRUE) == FALSE) {
4644 if (compose->batch) {
4645 gtk_widget_show_all(compose->window);
4651 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4654 if (compose->batch) {
4655 gtk_widget_show_all(compose->window);
4658 alertpanel_error(_("Could not queue message for sending:\n\n"
4659 "Charset conversion failed."));
4660 } else if (val == -5) {
4661 alertpanel_error(_("Could not queue message for sending:\n\n"
4662 "Couldn't get recipient encryption key."));
4663 } else if (val == -6) {
4665 } else if (val == -3) {
4666 if (privacy_peek_error())
4667 alertpanel_error(_("Could not queue message for sending:\n\n"
4668 "Signature failed: %s"), privacy_get_error());
4669 } else if (val == -2 && errno != 0) {
4670 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4672 alertpanel_error(_("Could not queue message for sending."));
4677 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4678 if (discard_window) {
4679 compose->sending = FALSE;
4680 compose_close(compose);
4681 /* No more compose access in the normal codepath
4682 * after this point! */
4687 alertpanel_error(_("The message was queued but could not be "
4688 "sent.\nUse \"Send queued messages\" from "
4689 "the main window to retry."));
4690 if (!discard_window) {
4697 if (msgpath == NULL) {
4698 msgpath = folder_item_fetch_msg(folder, msgnum);
4699 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4702 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4703 claws_unlink(msgpath);
4706 if (!discard_window) {
4708 if (!queued_removed)
4709 folder_item_remove_msg(folder, msgnum);
4710 folder_item_scan(folder);
4712 /* make sure we delete that */
4713 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4715 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4716 folder_item_remove_msg(folder, tmp->msgnum);
4717 procmsg_msginfo_free(tmp);
4724 if (!queued_removed)
4725 folder_item_remove_msg(folder, msgnum);
4726 folder_item_scan(folder);
4728 /* make sure we delete that */
4729 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4731 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4732 folder_item_remove_msg(folder, tmp->msgnum);
4733 procmsg_msginfo_free(tmp);
4736 if (!discard_window) {
4737 compose->sending = FALSE;
4738 compose_allow_user_actions (compose, TRUE);
4739 compose_close(compose);
4743 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4744 "the main window to retry."), errstr);
4747 alertpanel_error_log(_("The message was queued but could not be "
4748 "sent.\nUse \"Send queued messages\" from "
4749 "the main window to retry."));
4751 if (!discard_window) {
4760 toolbar_main_set_sensitive(mainwin);
4761 main_window_set_menu_sensitive(mainwin);
4767 compose_allow_user_actions (compose, TRUE);
4768 compose->sending = FALSE;
4769 compose->modified = TRUE;
4770 toolbar_main_set_sensitive(mainwin);
4771 main_window_set_menu_sensitive(mainwin);
4776 static gboolean compose_use_attach(Compose *compose)
4778 GtkTreeModel *model = gtk_tree_view_get_model
4779 (GTK_TREE_VIEW(compose->attach_clist));
4780 return gtk_tree_model_iter_n_children(model, NULL) > 0;
4783 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
4786 gchar buf[BUFFSIZE];
4788 gboolean first_to_address;
4789 gboolean first_cc_address;
4791 ComposeHeaderEntry *headerentry;
4792 const gchar *headerentryname;
4793 const gchar *cc_hdr;
4794 const gchar *to_hdr;
4795 gboolean err = FALSE;
4797 debug_print("Writing redirect header\n");
4799 cc_hdr = prefs_common_translated_header_name("Cc:");
4800 to_hdr = prefs_common_translated_header_name("To:");
4802 first_to_address = TRUE;
4803 for (list = compose->header_list; list; list = list->next) {
4804 headerentry = ((ComposeHeaderEntry *)list->data);
4805 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(headerentry->combo)->child));
4807 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
4808 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4809 Xstrdup_a(str, entstr, return -1);
4811 if (str[0] != '\0') {
4812 compose_convert_header
4813 (compose, buf, sizeof(buf), str,
4814 strlen("Resent-To") + 2, TRUE);
4816 if (first_to_address) {
4817 err |= (fprintf(fp, "Resent-To: ") < 0);
4818 first_to_address = FALSE;
4820 err |= (fprintf(fp, ",") < 0);
4822 err |= (fprintf(fp, "%s", buf) < 0);
4826 if (!first_to_address) {
4827 err |= (fprintf(fp, "\n") < 0);
4830 first_cc_address = TRUE;
4831 for (list = compose->header_list; list; list = list->next) {
4832 headerentry = ((ComposeHeaderEntry *)list->data);
4833 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(headerentry->combo)->child));
4835 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
4836 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4837 Xstrdup_a(str, strg, return -1);
4839 if (str[0] != '\0') {
4840 compose_convert_header
4841 (compose, buf, sizeof(buf), str,
4842 strlen("Resent-Cc") + 2, TRUE);
4844 if (first_cc_address) {
4845 err |= (fprintf(fp, "Resent-Cc: ") < 0);
4846 first_cc_address = FALSE;
4848 err |= (fprintf(fp, ",") < 0);
4850 err |= (fprintf(fp, "%s", buf) < 0);
4854 if (!first_cc_address) {
4855 err |= (fprintf(fp, "\n") < 0);
4858 return (err ? -1:0);
4861 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
4863 gchar buf[BUFFSIZE];
4865 const gchar *entstr;
4866 /* struct utsname utsbuf; */
4867 gboolean err = FALSE;
4869 g_return_val_if_fail(fp != NULL, -1);
4870 g_return_val_if_fail(compose->account != NULL, -1);
4871 g_return_val_if_fail(compose->account->address != NULL, -1);
4874 get_rfc822_date(buf, sizeof(buf));
4875 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
4878 if (compose->account->name && *compose->account->name) {
4879 compose_convert_header
4880 (compose, buf, sizeof(buf), compose->account->name,
4881 strlen("From: "), TRUE);
4882 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
4883 buf, compose->account->address) < 0);
4885 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
4888 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4889 if (*entstr != '\0') {
4890 Xstrdup_a(str, entstr, return -1);
4893 compose_convert_header(compose, buf, sizeof(buf), str,
4894 strlen("Subject: "), FALSE);
4895 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
4899 /* Resent-Message-ID */
4900 if (compose->account->set_domain && compose->account->domain) {
4901 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
4902 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
4903 g_snprintf(buf, sizeof(buf), "%s",
4904 strchr(compose->account->address, '@') ?
4905 strchr(compose->account->address, '@')+1 :
4906 compose->account->address);
4908 g_snprintf(buf, sizeof(buf), "%s", "");
4911 if (compose->account->gen_msgid) {
4912 generate_msgid(buf, sizeof(buf));
4913 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
4914 compose->msgid = g_strdup(buf);
4916 compose->msgid = NULL;
4919 if (compose_redirect_write_headers_from_headerlist(compose, fp))
4922 /* separator between header and body */
4923 err |= (fputs("\n", fp) == EOF);
4925 return (err ? -1:0);
4928 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
4932 gchar buf[BUFFSIZE];
4934 gboolean skip = FALSE;
4935 gboolean err = FALSE;
4936 gchar *not_included[]={
4937 "Return-Path:", "Delivered-To:", "Received:",
4938 "Subject:", "X-UIDL:", "AF:",
4939 "NF:", "PS:", "SRH:",
4940 "SFN:", "DSR:", "MID:",
4941 "CFG:", "PT:", "S:",
4942 "RQ:", "SSV:", "NSV:",
4943 "SSH:", "R:", "MAID:",
4944 "NAID:", "RMID:", "FMID:",
4945 "SCF:", "RRCPT:", "NG:",
4946 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
4947 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
4948 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
4949 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
4952 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
4953 FILE_OP_ERROR(compose->redirect_filename, "fopen");
4957 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
4959 for (i = 0; not_included[i] != NULL; i++) {
4960 if (g_ascii_strncasecmp(buf, not_included[i],
4961 strlen(not_included[i])) == 0) {
4968 if (fputs(buf, fdest) == -1)
4971 if (!prefs_common.redirect_keep_from) {
4972 if (g_ascii_strncasecmp(buf, "From:",
4973 strlen("From:")) == 0) {
4974 err |= (fputs(" (by way of ", fdest) == EOF);
4975 if (compose->account->name
4976 && *compose->account->name) {
4977 compose_convert_header
4978 (compose, buf, sizeof(buf),
4979 compose->account->name,
4982 err |= (fprintf(fdest, "%s <%s>",
4984 compose->account->address) < 0);
4986 err |= (fprintf(fdest, "%s",
4987 compose->account->address) < 0);
4988 err |= (fputs(")", fdest) == EOF);
4992 if (fputs("\n", fdest) == -1)
4999 if (compose_redirect_write_headers(compose, fdest))
5002 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5003 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5016 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5018 GtkTextBuffer *buffer;
5019 GtkTextIter start, end;
5022 const gchar *out_codeset;
5023 EncodingType encoding = ENC_UNKNOWN;
5024 MimeInfo *mimemsg, *mimetext;
5026 const gchar *src_codeset = CS_INTERNAL;
5028 if (action == COMPOSE_WRITE_FOR_SEND)
5029 attach_parts = TRUE;
5031 /* create message MimeInfo */
5032 mimemsg = procmime_mimeinfo_new();
5033 mimemsg->type = MIMETYPE_MESSAGE;
5034 mimemsg->subtype = g_strdup("rfc822");
5035 mimemsg->content = MIMECONTENT_MEM;
5036 mimemsg->tmp = TRUE; /* must free content later */
5037 mimemsg->data.mem = compose_get_header(compose);
5039 /* Create text part MimeInfo */
5040 /* get all composed text */
5041 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5042 gtk_text_buffer_get_start_iter(buffer, &start);
5043 gtk_text_buffer_get_end_iter(buffer, &end);
5044 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5046 out_codeset = conv_get_charset_str(compose->out_encoding);
5048 if (!out_codeset && is_ascii_str(chars)) {
5049 out_codeset = CS_US_ASCII;
5050 } else if (prefs_common.outgoing_fallback_to_ascii &&
5051 is_ascii_str(chars)) {
5052 out_codeset = CS_US_ASCII;
5053 encoding = ENC_7BIT;
5057 gchar *test_conv_global_out = NULL;
5058 gchar *test_conv_reply = NULL;
5060 /* automatic mode. be automatic. */
5061 codeconv_set_strict(TRUE);
5063 out_codeset = conv_get_outgoing_charset_str();
5065 debug_print("trying to convert to %s\n", out_codeset);
5066 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5069 if (!test_conv_global_out && compose->orig_charset
5070 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5071 out_codeset = compose->orig_charset;
5072 debug_print("failure; trying to convert to %s\n", out_codeset);
5073 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5076 if (!test_conv_global_out && !test_conv_reply) {
5078 out_codeset = CS_INTERNAL;
5079 debug_print("failure; finally using %s\n", out_codeset);
5081 g_free(test_conv_global_out);
5082 g_free(test_conv_reply);
5083 codeconv_set_strict(FALSE);
5086 if (encoding == ENC_UNKNOWN) {
5087 if (prefs_common.encoding_method == CTE_BASE64)
5088 encoding = ENC_BASE64;
5089 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5090 encoding = ENC_QUOTED_PRINTABLE;
5091 else if (prefs_common.encoding_method == CTE_8BIT)
5092 encoding = ENC_8BIT;
5094 encoding = procmime_get_encoding_for_charset(out_codeset);
5097 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5098 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5100 if (action == COMPOSE_WRITE_FOR_SEND) {
5101 codeconv_set_strict(TRUE);
5102 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5103 codeconv_set_strict(FALSE);
5109 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5110 "to the specified %s charset.\n"
5111 "Send it as %s?"), out_codeset, src_codeset);
5112 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5113 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5116 if (aval != G_ALERTALTERNATE) {
5121 out_codeset = src_codeset;
5127 out_codeset = src_codeset;
5132 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5133 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5134 strstr(buf, "\nFrom ") != NULL) {
5135 encoding = ENC_QUOTED_PRINTABLE;
5139 mimetext = procmime_mimeinfo_new();
5140 mimetext->content = MIMECONTENT_MEM;
5141 mimetext->tmp = TRUE; /* must free content later */
5142 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5143 * and free the data, which we need later. */
5144 mimetext->data.mem = g_strdup(buf);
5145 mimetext->type = MIMETYPE_TEXT;
5146 mimetext->subtype = g_strdup("plain");
5147 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5148 g_strdup(out_codeset));
5150 /* protect trailing spaces when signing message */
5151 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5152 privacy_system_can_sign(compose->privacy_system)) {
5153 encoding = ENC_QUOTED_PRINTABLE;
5156 debug_print("main text: %zd bytes encoded as %s in %d\n",
5157 strlen(buf), out_codeset, encoding);
5159 /* check for line length limit */
5160 if (action == COMPOSE_WRITE_FOR_SEND &&
5161 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5162 check_line_length(buf, 1000, &line) < 0) {
5166 msg = g_strdup_printf
5167 (_("Line %d exceeds the line length limit (998 bytes).\n"
5168 "The contents of the message might be broken on the way to the delivery.\n"
5170 "Send it anyway?"), line + 1);
5171 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5173 if (aval != G_ALERTALTERNATE) {
5179 if (encoding != ENC_UNKNOWN)
5180 procmime_encode_content(mimetext, encoding);
5182 /* append attachment parts */
5183 if (compose_use_attach(compose) && attach_parts) {
5184 MimeInfo *mimempart;
5185 gchar *boundary = NULL;
5186 mimempart = procmime_mimeinfo_new();
5187 mimempart->content = MIMECONTENT_EMPTY;
5188 mimempart->type = MIMETYPE_MULTIPART;
5189 mimempart->subtype = g_strdup("mixed");
5193 boundary = generate_mime_boundary(NULL);
5194 } while (strstr(buf, boundary) != NULL);
5196 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5199 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5201 g_node_append(mimempart->node, mimetext->node);
5202 g_node_append(mimemsg->node, mimempart->node);
5204 compose_add_attachments(compose, mimempart);
5206 g_node_append(mimemsg->node, mimetext->node);
5210 /* sign message if sending */
5211 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5212 privacy_system_can_sign(compose->privacy_system))
5213 if (!privacy_sign(compose->privacy_system, mimemsg, compose->account))
5216 procmime_write_mimeinfo(mimemsg, fp);
5218 procmime_mimeinfo_free_all(mimemsg);
5223 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5225 GtkTextBuffer *buffer;
5226 GtkTextIter start, end;
5231 if ((fp = g_fopen(file, "wb")) == NULL) {
5232 FILE_OP_ERROR(file, "fopen");
5236 /* chmod for security */
5237 if (change_file_mode_rw(fp, file) < 0) {
5238 FILE_OP_ERROR(file, "chmod");
5239 g_warning("can't change file mode\n");
5242 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5243 gtk_text_buffer_get_start_iter(buffer, &start);
5244 gtk_text_buffer_get_end_iter(buffer, &end);
5245 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5247 chars = conv_codeset_strdup
5248 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5251 if (!chars) return -1;
5254 len = strlen(chars);
5255 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5256 FILE_OP_ERROR(file, "fwrite");
5265 if (fclose(fp) == EOF) {
5266 FILE_OP_ERROR(file, "fclose");
5273 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5276 MsgInfo *msginfo = compose->targetinfo;
5278 g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5279 if (!msginfo) return -1;
5281 if (!force && MSG_IS_LOCKED(msginfo->flags))
5284 item = msginfo->folder;
5285 g_return_val_if_fail(item != NULL, -1);
5287 if (procmsg_msg_exist(msginfo) &&
5288 (folder_has_parent_of_type(item, F_QUEUE) ||
5289 folder_has_parent_of_type(item, F_DRAFT)
5290 || msginfo == compose->autosaved_draft)) {
5291 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5292 g_warning("can't remove the old message\n");
5295 debug_print("removed reedit target %d\n", msginfo->msgnum);
5302 static void compose_remove_draft(Compose *compose)
5305 MsgInfo *msginfo = compose->targetinfo;
5306 drafts = account_get_special_folder(compose->account, F_DRAFT);
5308 if (procmsg_msg_exist(msginfo)) {
5309 folder_item_remove_msg(drafts, msginfo->msgnum);
5314 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5315 gboolean remove_reedit_target)
5317 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5320 static gboolean compose_warn_encryption(Compose *compose)
5322 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5323 AlertValue val = G_ALERTALTERNATE;
5325 if (warning == NULL)
5328 val = alertpanel_full(_("Encryption warning"), warning,
5329 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5330 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5331 if (val & G_ALERTDISABLE) {
5332 val &= ~G_ALERTDISABLE;
5333 if (val == G_ALERTALTERNATE)
5334 privacy_inhibit_encrypt_warning(compose->privacy_system,
5338 if (val == G_ALERTALTERNATE) {
5345 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5346 gchar **msgpath, gboolean check_subject,
5347 gboolean remove_reedit_target)
5354 static gboolean lock = FALSE;
5355 PrefsAccount *mailac = NULL, *newsac = NULL;
5356 gboolean err = FALSE;
5358 debug_print("queueing message...\n");
5359 g_return_val_if_fail(compose->account != NULL, -1);
5363 if (compose_check_entries(compose, check_subject) == FALSE) {
5365 if (compose->batch) {
5366 gtk_widget_show_all(compose->window);
5371 if (!compose->to_list && !compose->newsgroup_list) {
5372 g_warning("can't get recipient list.");
5377 if (compose->to_list) {
5378 if (compose->account->protocol != A_NNTP)
5379 mailac = compose->account;
5380 else if (cur_account && cur_account->protocol != A_NNTP)
5381 mailac = cur_account;
5382 else if (!(mailac = compose_current_mail_account())) {
5384 alertpanel_error(_("No account for sending mails available!"));
5389 if (compose->newsgroup_list) {
5390 if (compose->account->protocol == A_NNTP)
5391 newsac = compose->account;
5392 else if (!newsac->protocol != A_NNTP) {
5394 alertpanel_error(_("No account for posting news available!"));
5399 /* write queue header */
5400 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5401 G_DIR_SEPARATOR, compose, (guint) rand());
5402 debug_print("queuing to %s\n", tmp);
5403 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5404 FILE_OP_ERROR(tmp, "fopen");
5410 if (change_file_mode_rw(fp, tmp) < 0) {
5411 FILE_OP_ERROR(tmp, "chmod");
5412 g_warning("can't change file mode\n");
5415 /* queueing variables */
5416 err |= (fprintf(fp, "AF:\n") < 0);
5417 err |= (fprintf(fp, "NF:0\n") < 0);
5418 err |= (fprintf(fp, "PS:10\n") < 0);
5419 err |= (fprintf(fp, "SRH:1\n") < 0);
5420 err |= (fprintf(fp, "SFN:\n") < 0);
5421 err |= (fprintf(fp, "DSR:\n") < 0);
5423 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5425 err |= (fprintf(fp, "MID:\n") < 0);
5426 err |= (fprintf(fp, "CFG:\n") < 0);
5427 err |= (fprintf(fp, "PT:0\n") < 0);
5428 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5429 err |= (fprintf(fp, "RQ:\n") < 0);
5431 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5433 err |= (fprintf(fp, "SSV:\n") < 0);
5435 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5437 err |= (fprintf(fp, "NSV:\n") < 0);
5438 err |= (fprintf(fp, "SSH:\n") < 0);
5439 /* write recepient list */
5440 if (compose->to_list) {
5441 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5442 for (cur = compose->to_list->next; cur != NULL;
5444 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5445 err |= (fprintf(fp, "\n") < 0);
5447 /* write newsgroup list */
5448 if (compose->newsgroup_list) {
5449 err |= (fprintf(fp, "NG:") < 0);
5450 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5451 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5452 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5453 err |= (fprintf(fp, "\n") < 0);
5455 /* Sylpheed account IDs */
5457 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5459 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5462 if (compose->privacy_system != NULL) {
5463 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5464 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5465 if (compose->use_encryption) {
5467 if (!compose_warn_encryption(compose)) {
5474 if (mailac && mailac->encrypt_to_self) {
5475 GSList *tmp_list = g_slist_copy(compose->to_list);
5476 tmp_list = g_slist_append(tmp_list, compose->account->address);
5477 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5478 g_slist_free(tmp_list);
5480 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5482 if (encdata != NULL) {
5483 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5484 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5485 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5487 } /* else we finally dont want to encrypt */
5489 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5490 /* and if encdata was null, it means there's been a problem in
5502 /* Save copy folder */
5503 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5504 gchar *savefolderid;
5506 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
5507 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5508 g_free(savefolderid);
5510 /* Save copy folder */
5511 if (compose->return_receipt) {
5512 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5514 /* Message-ID of message replying to */
5515 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5518 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5519 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5522 /* Message-ID of message forwarding to */
5523 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5526 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5527 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5531 /* end of headers */
5532 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5534 if (compose->redirect_filename != NULL) {
5535 if (compose_redirect_write_to_file(compose, fp) < 0) {
5544 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5549 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5553 g_warning("failed to write queue message\n");
5560 if (fclose(fp) == EOF) {
5561 FILE_OP_ERROR(tmp, "fclose");
5568 if (item && *item) {
5571 queue = account_get_special_folder(compose->account, F_QUEUE);
5574 g_warning("can't find queue folder\n");
5580 folder_item_scan(queue);
5581 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5582 g_warning("can't queue the message\n");
5589 if (msgpath == NULL) {
5595 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5596 compose_remove_reedit_target(compose, FALSE);
5599 if ((msgnum != NULL) && (item != NULL)) {
5607 static void compose_add_attachments(Compose *compose, MimeInfo *parent)
5610 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5612 struct stat statbuf;
5613 gchar *type, *subtype;
5614 GtkTreeModel *model;
5617 model = gtk_tree_view_get_model(tree_view);
5619 if (!gtk_tree_model_get_iter_first(model, &iter))
5622 gtk_tree_model_get(model, &iter,
5626 mimepart = procmime_mimeinfo_new();
5627 mimepart->content = MIMECONTENT_FILE;
5628 mimepart->data.filename = g_strdup(ainfo->file);
5629 mimepart->tmp = FALSE; /* or we destroy our attachment */
5630 mimepart->offset = 0;
5632 stat(ainfo->file, &statbuf);
5633 mimepart->length = statbuf.st_size;
5635 type = g_strdup(ainfo->content_type);
5637 if (!strchr(type, '/')) {
5639 type = g_strdup("application/octet-stream");
5642 subtype = strchr(type, '/') + 1;
5643 *(subtype - 1) = '\0';
5644 mimepart->type = procmime_get_media_type(type);
5645 mimepart->subtype = g_strdup(subtype);
5648 if (mimepart->type == MIMETYPE_MESSAGE &&
5649 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5650 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5653 g_hash_table_insert(mimepart->typeparameters,
5654 g_strdup("name"), g_strdup(ainfo->name));
5655 g_hash_table_insert(mimepart->dispositionparameters,
5656 g_strdup("filename"), g_strdup(ainfo->name));
5657 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5661 if (compose->use_signing) {
5662 if (ainfo->encoding == ENC_7BIT)
5663 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5664 else if (ainfo->encoding == ENC_8BIT)
5665 ainfo->encoding = ENC_BASE64;
5668 procmime_encode_content(mimepart, ainfo->encoding);
5670 g_node_append(parent->node, mimepart->node);
5671 } while (gtk_tree_model_iter_next(model, &iter));
5674 #define IS_IN_CUSTOM_HEADER(header) \
5675 (compose->account->add_customhdr && \
5676 custom_header_find(compose->account->customhdr_list, header) != NULL)
5678 static void compose_add_headerfield_from_headerlist(Compose *compose,
5680 const gchar *fieldname,
5681 const gchar *seperator)
5683 gchar *str, *fieldname_w_colon;
5684 gboolean add_field = FALSE;
5686 ComposeHeaderEntry *headerentry;
5687 const gchar *headerentryname;
5688 const gchar *trans_fieldname;
5691 if (IS_IN_CUSTOM_HEADER(fieldname))
5694 debug_print("Adding %s-fields\n", fieldname);
5696 fieldstr = g_string_sized_new(64);
5698 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5699 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5701 for (list = compose->header_list; list; list = list->next) {
5702 headerentry = ((ComposeHeaderEntry *)list->data);
5703 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(headerentry->combo)->child));
5705 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5706 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5708 if (str[0] != '\0') {
5710 g_string_append(fieldstr, seperator);
5711 g_string_append(fieldstr, str);
5720 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5721 compose_convert_header
5722 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
5723 strlen(fieldname) + 2, TRUE);
5724 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5728 g_free(fieldname_w_colon);
5729 g_string_free(fieldstr, TRUE);
5734 static gchar *compose_get_header(Compose *compose)
5736 gchar buf[BUFFSIZE];
5737 const gchar *entry_str;
5741 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5743 gchar *from_name = NULL, *from_address = NULL;
5746 g_return_val_if_fail(compose->account != NULL, NULL);
5747 g_return_val_if_fail(compose->account->address != NULL, NULL);
5749 header = g_string_sized_new(64);
5752 get_rfc822_date(buf, sizeof(buf));
5753 g_string_append_printf(header, "Date: %s\n", buf);
5757 if (compose->account->name && *compose->account->name) {
5759 QUOTE_IF_REQUIRED(buf, compose->account->name);
5760 tmp = g_strdup_printf("%s <%s>",
5761 buf, compose->account->address);
5763 tmp = g_strdup_printf("%s",
5764 compose->account->address);
5766 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5767 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5769 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5770 from_address = g_strdup(compose->account->address);
5772 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5773 /* extract name and address */
5774 if (strstr(spec, " <") && strstr(spec, ">")) {
5775 from_address = g_strdup(strrchr(spec, '<')+1);
5776 *(strrchr(from_address, '>')) = '\0';
5777 from_name = g_strdup(spec);
5778 *(strrchr(from_name, '<')) = '\0';
5781 from_address = g_strdup(spec);
5788 if (from_name && *from_name) {
5789 compose_convert_header
5790 (compose, buf, sizeof(buf), from_name,
5791 strlen("From: "), TRUE);
5792 QUOTE_IF_REQUIRED(name, buf);
5794 g_string_append_printf(header, "From: %s <%s>\n",
5795 name, from_address);
5797 g_string_append_printf(header, "From: %s\n", from_address);
5800 g_free(from_address);
5803 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
5806 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
5809 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
5812 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
5815 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
5817 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
5820 compose_convert_header(compose, buf, sizeof(buf), str,
5821 strlen("Subject: "), FALSE);
5822 g_string_append_printf(header, "Subject: %s\n", buf);
5828 if (compose->account->set_domain && compose->account->domain) {
5829 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5830 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5831 g_snprintf(buf, sizeof(buf), "%s",
5832 strchr(compose->account->address, '@') ?
5833 strchr(compose->account->address, '@')+1 :
5834 compose->account->address);
5836 g_snprintf(buf, sizeof(buf), "%s", "");
5839 if (compose->account->gen_msgid) {
5840 generate_msgid(buf, sizeof(buf));
5841 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
5842 compose->msgid = g_strdup(buf);
5844 compose->msgid = NULL;
5847 if (compose->remove_references == FALSE) {
5849 if (compose->inreplyto && compose->to_list)
5850 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
5853 if (compose->references)
5854 g_string_append_printf(header, "References: %s\n", compose->references);
5858 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
5861 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
5864 if (compose->account->organization &&
5865 strlen(compose->account->organization) &&
5866 !IS_IN_CUSTOM_HEADER("Organization")) {
5867 compose_convert_header(compose, buf, sizeof(buf),
5868 compose->account->organization,
5869 strlen("Organization: "), FALSE);
5870 g_string_append_printf(header, "Organization: %s\n", buf);
5873 /* Program version and system info */
5874 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
5875 !compose->newsgroup_list) {
5876 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
5878 gtk_major_version, gtk_minor_version, gtk_micro_version,
5881 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
5882 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
5884 gtk_major_version, gtk_minor_version, gtk_micro_version,
5888 /* custom headers */
5889 if (compose->account->add_customhdr) {
5892 for (cur = compose->account->customhdr_list; cur != NULL;
5894 CustomHeader *chdr = (CustomHeader *)cur->data;
5896 if (custom_header_is_allowed(chdr->name)) {
5897 compose_convert_header
5898 (compose, buf, sizeof(buf),
5899 chdr->value ? chdr->value : "",
5900 strlen(chdr->name) + 2, FALSE);
5901 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
5906 /* Automatic Faces and X-Faces */
5907 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
5908 g_string_append_printf(header, "X-Face: %s\n", buf);
5910 else if (get_default_xface (buf, sizeof(buf)) == 0) {
5911 g_string_append_printf(header, "X-Face: %s\n", buf);
5913 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
5914 g_string_append_printf(header, "Face: %s\n", buf);
5916 else if (get_default_face (buf, sizeof(buf)) == 0) {
5917 g_string_append_printf(header, "Face: %s\n", buf);
5921 switch (compose->priority) {
5922 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
5923 "X-Priority: 1 (Highest)\n");
5925 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
5926 "X-Priority: 2 (High)\n");
5928 case PRIORITY_NORMAL: break;
5929 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
5930 "X-Priority: 4 (Low)\n");
5932 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
5933 "X-Priority: 5 (Lowest)\n");
5935 default: debug_print("compose: priority unknown : %d\n",
5939 /* Request Return Receipt */
5940 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
5941 if (compose->return_receipt) {
5942 if (compose->account->name
5943 && *compose->account->name) {
5944 compose_convert_header(compose, buf, sizeof(buf),
5945 compose->account->name,
5946 strlen("Disposition-Notification-To: "),
5948 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
5950 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
5954 /* get special headers */
5955 for (list = compose->header_list; list; list = list->next) {
5956 ComposeHeaderEntry *headerentry;
5959 gchar *headername_wcolon;
5960 const gchar *headername_trans;
5963 gboolean standard_header = FALSE;
5965 headerentry = ((ComposeHeaderEntry *)list->data);
5967 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(headerentry->combo)->child)));
5969 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
5974 if (!strstr(tmp, ":")) {
5975 headername_wcolon = g_strconcat(tmp, ":", NULL);
5976 headername = g_strdup(tmp);
5978 headername_wcolon = g_strdup(tmp);
5979 headername = g_strdup(strtok(tmp, ":"));
5983 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5984 Xstrdup_a(headervalue, entry_str, return NULL);
5985 subst_char(headervalue, '\r', ' ');
5986 subst_char(headervalue, '\n', ' ');
5987 string = std_headers;
5988 while (*string != NULL) {
5989 headername_trans = prefs_common_translated_header_name(*string);
5990 if (!strcmp(headername_trans, headername_wcolon))
5991 standard_header = TRUE;
5994 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
5995 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
5998 g_free(headername_wcolon);
6002 g_string_free(header, FALSE);
6007 #undef IS_IN_CUSTOM_HEADER
6009 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6010 gint header_len, gboolean addr_field)
6012 gchar *tmpstr = NULL;
6013 const gchar *out_codeset = NULL;
6015 g_return_if_fail(src != NULL);
6016 g_return_if_fail(dest != NULL);
6018 if (len < 1) return;
6020 tmpstr = g_strdup(src);
6022 subst_char(tmpstr, '\n', ' ');
6023 subst_char(tmpstr, '\r', ' ');
6026 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6027 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6028 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6033 codeconv_set_strict(TRUE);
6034 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6035 conv_get_charset_str(compose->out_encoding));
6036 codeconv_set_strict(FALSE);
6038 if (!dest || *dest == '\0') {
6039 gchar *test_conv_global_out = NULL;
6040 gchar *test_conv_reply = NULL;
6042 /* automatic mode. be automatic. */
6043 codeconv_set_strict(TRUE);
6045 out_codeset = conv_get_outgoing_charset_str();
6047 debug_print("trying to convert to %s\n", out_codeset);
6048 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6051 if (!test_conv_global_out && compose->orig_charset
6052 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6053 out_codeset = compose->orig_charset;
6054 debug_print("failure; trying to convert to %s\n", out_codeset);
6055 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6058 if (!test_conv_global_out && !test_conv_reply) {
6060 out_codeset = CS_INTERNAL;
6061 debug_print("finally using %s\n", out_codeset);
6063 g_free(test_conv_global_out);
6064 g_free(test_conv_reply);
6065 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6067 codeconv_set_strict(FALSE);
6072 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6076 g_return_if_fail(user_data != NULL);
6078 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6079 g_strstrip(address);
6080 if (*address != '\0') {
6081 gchar *name = procheader_get_fromname(address);
6082 extract_address(address);
6083 addressbook_add_contact(name, address, NULL, NULL);
6088 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6090 GtkWidget *menuitem;
6093 g_return_if_fail(menu != NULL);
6094 g_return_if_fail(GTK_IS_MENU_SHELL(menu));
6096 menuitem = gtk_separator_menu_item_new();
6097 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6098 gtk_widget_show(menuitem);
6100 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6101 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6103 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6104 g_strstrip(address);
6105 if (*address == '\0') {
6106 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6109 g_signal_connect(G_OBJECT(menuitem), "activate",
6110 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6111 gtk_widget_show(menuitem);
6114 static void compose_create_header_entry(Compose *compose)
6116 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6121 const gchar *header = NULL;
6122 ComposeHeaderEntry *headerentry;
6123 gboolean standard_header = FALSE;
6124 #if !(GTK_CHECK_VERSION(2,12,0))
6125 GtkTooltips *tips = compose->tooltips;
6128 headerentry = g_new0(ComposeHeaderEntry, 1);
6131 combo = gtk_combo_box_entry_new_text();
6133 while(*string != NULL) {
6134 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6135 (gchar*)prefs_common_translated_header_name(*string));
6138 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6139 g_signal_connect(G_OBJECT(GTK_BIN(combo)->child), "grab_focus",
6140 G_CALLBACK(compose_grab_focus_cb), compose);
6141 gtk_widget_show(combo);
6142 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6143 compose->header_nextrow, compose->header_nextrow+1,
6144 GTK_SHRINK, GTK_FILL, 0, 0);
6145 if (compose->header_last) {
6146 const gchar *last_header_entry = gtk_entry_get_text(
6147 GTK_ENTRY(GTK_BIN(compose->header_last->combo)->child));
6149 while (*string != NULL) {
6150 if (!strcmp(*string, last_header_entry))
6151 standard_header = TRUE;
6154 if (standard_header)
6155 header = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(compose->header_last->combo)->child));
6157 if (!compose->header_last || !standard_header) {
6158 switch(compose->account->protocol) {
6160 header = prefs_common_translated_header_name("Newsgroups:");
6163 header = prefs_common_translated_header_name("To:");
6168 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(combo)->child), header);
6170 g_signal_connect_after(G_OBJECT(GTK_BIN(combo)->child), "grab_focus",
6171 G_CALLBACK(compose_grab_focus_cb), compose);
6174 entry = gtk_entry_new();
6175 gtk_widget_show(entry);
6176 CLAWS_SET_TIP(entry,
6177 _("Use <tab> to autocomplete from addressbook"));
6178 gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2,
6179 compose->header_nextrow, compose->header_nextrow+1,
6180 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6182 g_signal_connect(G_OBJECT(entry), "key-press-event",
6183 G_CALLBACK(compose_headerentry_key_press_event_cb),
6185 g_signal_connect(G_OBJECT(entry), "changed",
6186 G_CALLBACK(compose_headerentry_changed_cb),
6188 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6189 G_CALLBACK(compose_grab_focus_cb), compose);
6192 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6193 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6194 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6195 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6196 G_CALLBACK(compose_header_drag_received_cb),
6198 g_signal_connect(G_OBJECT(entry), "drag-drop",
6199 G_CALLBACK(compose_drag_drop),
6201 g_signal_connect(G_OBJECT(entry), "populate-popup",
6202 G_CALLBACK(compose_entry_popup_extend),
6205 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6207 headerentry->compose = compose;
6208 headerentry->combo = combo;
6209 headerentry->entry = entry;
6210 headerentry->headernum = compose->header_nextrow;
6212 compose->header_nextrow++;
6213 compose->header_last = headerentry;
6214 compose->header_list =
6215 g_slist_append(compose->header_list,
6219 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text)
6221 ComposeHeaderEntry *last_header;
6223 last_header = compose->header_last;
6225 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(last_header->combo)->child), header);
6226 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6229 static void compose_remove_header_entries(Compose *compose)
6232 for (list = compose->header_list; list; list = list->next) {
6233 ComposeHeaderEntry *headerentry =
6234 (ComposeHeaderEntry *)list->data;
6235 gtk_widget_destroy(headerentry->combo);
6236 gtk_widget_destroy(headerentry->entry);
6237 g_free(headerentry);
6239 compose->header_last = NULL;
6240 g_slist_free(compose->header_list);
6241 compose->header_list = NULL;
6242 compose->header_nextrow = 1;
6243 compose_create_header_entry(compose);
6246 static GtkWidget *compose_create_header(Compose *compose)
6248 GtkWidget *from_optmenu_hbox;
6249 GtkWidget *header_scrolledwin;
6250 GtkWidget *header_table;
6254 /* header labels and entries */
6255 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6256 gtk_widget_show(header_scrolledwin);
6257 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6259 header_table = gtk_table_new(2, 2, FALSE);
6260 gtk_widget_show(header_table);
6261 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6262 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6263 gtk_viewport_set_shadow_type(GTK_VIEWPORT(GTK_BIN(header_scrolledwin)->child), GTK_SHADOW_NONE);
6266 /* option menu for selecting accounts */
6267 from_optmenu_hbox = compose_account_option_menu_create(compose);
6268 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6269 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6272 compose->header_table = header_table;
6273 compose->header_list = NULL;
6274 compose->header_nextrow = count;
6276 compose_create_header_entry(compose);
6278 compose->table = NULL;
6280 return header_scrolledwin ;
6283 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6285 Compose *compose = (Compose *)data;
6286 GdkEventButton event;
6289 event.time = gtk_get_current_event_time();
6291 return attach_button_pressed(compose->attach_clist, &event, compose);
6294 static GtkWidget *compose_create_attach(Compose *compose)
6296 GtkWidget *attach_scrwin;
6297 GtkWidget *attach_clist;
6299 GtkListStore *store;
6300 GtkCellRenderer *renderer;
6301 GtkTreeViewColumn *column;
6302 GtkTreeSelection *selection;
6304 /* attachment list */
6305 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6306 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6307 GTK_POLICY_AUTOMATIC,
6308 GTK_POLICY_AUTOMATIC);
6309 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6311 store = gtk_list_store_new(N_ATTACH_COLS,
6316 G_TYPE_AUTO_POINTER,
6318 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6319 (GTK_TREE_MODEL(store)));
6320 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6321 g_object_unref(store);
6323 renderer = gtk_cell_renderer_text_new();
6324 column = gtk_tree_view_column_new_with_attributes
6325 (_("Mime type"), renderer, "text",
6326 COL_MIMETYPE, NULL);
6327 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6329 renderer = gtk_cell_renderer_text_new();
6330 column = gtk_tree_view_column_new_with_attributes
6331 (_("Size"), renderer, "text",
6333 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6335 renderer = gtk_cell_renderer_text_new();
6336 column = gtk_tree_view_column_new_with_attributes
6337 (_("Name"), renderer, "text",
6339 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6341 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6342 prefs_common.use_stripes_everywhere);
6343 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6344 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6346 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6347 G_CALLBACK(attach_selected), compose);
6348 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6349 G_CALLBACK(attach_button_pressed), compose);
6351 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6352 G_CALLBACK(popup_attach_button_pressed), compose);
6354 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6355 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6356 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6357 G_CALLBACK(popup_attach_button_pressed), compose);
6359 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6360 G_CALLBACK(attach_key_pressed), compose);
6363 gtk_drag_dest_set(attach_clist,
6364 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6365 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6366 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6367 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6368 G_CALLBACK(compose_attach_drag_received_cb),
6370 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6371 G_CALLBACK(compose_drag_drop),
6374 compose->attach_scrwin = attach_scrwin;
6375 compose->attach_clist = attach_clist;
6377 return attach_scrwin;
6380 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6381 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6383 static GtkWidget *compose_create_others(Compose *compose)
6386 GtkWidget *savemsg_checkbtn;
6387 GtkWidget *savemsg_entry;
6388 GtkWidget *savemsg_select;
6391 gchar *folderidentifier;
6393 /* Table for settings */
6394 table = gtk_table_new(3, 1, FALSE);
6395 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6396 gtk_widget_show(table);
6397 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6400 /* Save Message to folder */
6401 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6402 gtk_widget_show(savemsg_checkbtn);
6403 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6404 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6405 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6407 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6408 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6410 savemsg_entry = gtk_entry_new();
6411 gtk_widget_show(savemsg_entry);
6412 gtk_table_attach_defaults(GTK_TABLE(table), savemsg_entry, 1, 2, rowcount, rowcount + 1);
6413 gtk_editable_set_editable(GTK_EDITABLE(savemsg_entry), prefs_common.savemsg);
6414 g_signal_connect_after(G_OBJECT(savemsg_entry), "grab_focus",
6415 G_CALLBACK(compose_grab_focus_cb), compose);
6416 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6417 folderidentifier = folder_item_get_identifier(account_get_special_folder
6418 (compose->account, F_OUTBOX));
6419 gtk_entry_set_text(GTK_ENTRY(savemsg_entry), folderidentifier);
6420 g_free(folderidentifier);
6423 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6424 gtk_widget_show(savemsg_select);
6425 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6426 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6427 G_CALLBACK(compose_savemsg_select_cb),
6432 compose->savemsg_checkbtn = savemsg_checkbtn;
6433 compose->savemsg_entry = savemsg_entry;
6438 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6440 gtk_editable_set_editable(GTK_EDITABLE(compose->savemsg_entry),
6441 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6444 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6449 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6452 path = folder_item_get_identifier(dest);
6454 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), path);
6458 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6459 GdkAtom clip, GtkTextIter *insert_place);
6462 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6466 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6468 if (event->button == 3) {
6470 GtkTextIter sel_start, sel_end;
6471 gboolean stuff_selected;
6473 /* move the cursor to allow GtkAspell to check the word
6474 * under the mouse */
6475 if (event->x && event->y) {
6476 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6477 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6479 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6482 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6483 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6486 stuff_selected = gtk_text_buffer_get_selection_bounds(
6488 &sel_start, &sel_end);
6490 gtk_text_buffer_place_cursor (buffer, &iter);
6491 /* reselect stuff */
6493 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6494 gtk_text_buffer_select_range(buffer,
6495 &sel_start, &sel_end);
6497 return FALSE; /* pass the event so that the right-click goes through */
6500 if (event->button == 2) {
6505 /* get the middle-click position to paste at the correct place */
6506 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6507 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6509 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6512 entry_paste_clipboard(compose, text,
6513 prefs_common.linewrap_pastes,
6514 GDK_SELECTION_PRIMARY, &iter);
6522 static void compose_spell_menu_changed(void *data)
6524 Compose *compose = (Compose *)data;
6526 GtkWidget *menuitem;
6527 GtkWidget *parent_item;
6528 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6531 if (compose->gtkaspell == NULL)
6534 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6535 "/Menu/Spelling/Options");
6537 /* setting the submenu removes /Spelling/Options from the factory
6538 * so we need to save it */
6540 if (parent_item == NULL) {
6541 parent_item = compose->aspell_options_menu;
6542 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6544 compose->aspell_options_menu = parent_item;
6546 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6548 spell_menu = g_slist_reverse(spell_menu);
6549 for (items = spell_menu;
6550 items; items = items->next) {
6551 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6552 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6553 gtk_widget_show(GTK_WIDGET(menuitem));
6555 g_slist_free(spell_menu);
6557 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6562 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6564 Compose *compose = (Compose *)data;
6565 GdkEventButton event;
6568 event.time = gtk_get_current_event_time();
6572 return text_clicked(compose->text, &event, compose);
6575 static gboolean compose_force_window_origin = TRUE;
6576 static Compose *compose_create(PrefsAccount *account,
6585 GtkWidget *handlebox;
6587 GtkWidget *notebook;
6589 GtkWidget *attach_hbox;
6590 GtkWidget *attach_lab1;
6591 GtkWidget *attach_lab2;
6596 GtkWidget *subject_hbox;
6597 GtkWidget *subject_frame;
6598 GtkWidget *subject_entry;
6602 GtkWidget *edit_vbox;
6603 GtkWidget *ruler_hbox;
6605 GtkWidget *scrolledwin;
6607 GtkTextBuffer *buffer;
6608 GtkClipboard *clipboard;
6611 UndoMain *undostruct;
6613 gchar *titles[N_ATTACH_COLS];
6614 GtkWidget *popupmenu;
6615 GtkWidget *tmpl_menu;
6616 GtkActionGroup *action_group = NULL;
6619 GtkAspell * gtkaspell = NULL;
6622 static GdkGeometry geometry;
6624 g_return_val_if_fail(account != NULL, NULL);
6626 debug_print("Creating compose window...\n");
6627 compose = g_new0(Compose, 1);
6629 titles[COL_MIMETYPE] = _("MIME type");
6630 titles[COL_SIZE] = _("Size");
6631 titles[COL_NAME] = _("Name");
6633 compose->batch = batch;
6634 compose->account = account;
6635 compose->folder = folder;
6637 compose->mutex = g_mutex_new();
6638 compose->set_cursor_pos = -1;
6640 #if !(GTK_CHECK_VERSION(2,12,0))
6641 compose->tooltips = tips;
6644 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6646 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6647 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6649 if (!geometry.max_width) {
6650 geometry.max_width = gdk_screen_width();
6651 geometry.max_height = gdk_screen_height();
6654 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6655 &geometry, GDK_HINT_MAX_SIZE);
6656 if (!geometry.min_width) {
6657 geometry.min_width = 600;
6658 geometry.min_height = 480;
6660 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6661 &geometry, GDK_HINT_MIN_SIZE);
6663 #ifndef GENERIC_UMPC
6664 if (compose_force_window_origin)
6665 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
6666 prefs_common.compose_y);
6668 g_signal_connect(G_OBJECT(window), "delete_event",
6669 G_CALLBACK(compose_delete_cb), compose);
6670 MANAGE_WINDOW_SIGNALS_CONNECT(window);
6671 gtk_widget_realize(window);
6673 gtkut_widget_set_composer_icon(window);
6675 vbox = gtk_vbox_new(FALSE, 0);
6676 gtk_container_add(GTK_CONTAINER(window), vbox);
6678 compose->ui_manager = gtk_ui_manager_new();
6679 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
6680 G_N_ELEMENTS(compose_entries), (gpointer)compose);
6681 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
6682 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
6683 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
6684 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
6685 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
6686 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
6687 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
6688 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
6691 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
6693 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENU)
6696 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
6697 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
6699 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
6701 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
6702 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
6703 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
6706 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
6707 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
6708 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6709 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
6710 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
6711 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
6712 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6713 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
6714 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6715 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
6718 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
6719 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
6720 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6722 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
6723 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
6724 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
6726 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
6727 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
6728 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
6729 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
6731 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
6733 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
6734 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
6735 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
6736 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
6737 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
6738 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
6739 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
6740 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
6741 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
6742 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
6743 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
6744 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
6745 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
6746 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
6747 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
6749 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6751 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
6752 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
6753 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
6754 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
6756 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6758 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
6762 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
6763 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
6764 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
6765 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
6766 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
6767 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
6771 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
6772 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
6773 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
6774 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
6775 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
6777 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6778 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
6779 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6780 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
6781 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
6784 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6785 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
6786 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
6787 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
6788 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
6789 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
6790 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
6792 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6793 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
6794 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6795 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
6796 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6798 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
6800 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
6801 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6802 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
6803 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
6804 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6806 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
6807 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)
6808 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)
6809 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
6811 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
6813 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
6814 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)
6815 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)
6817 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
6819 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
6820 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)
6821 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
6823 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
6824 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)
6825 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
6827 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
6829 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
6830 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)
6831 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
6832 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
6833 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
6835 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
6836 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)
6837 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)
6838 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
6839 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
6841 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
6842 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
6843 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
6844 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
6845 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
6847 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
6848 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
6849 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)
6851 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
6852 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
6853 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
6857 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
6858 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
6859 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
6860 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6861 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
6862 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6865 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
6867 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
6868 gtk_widget_show_all(menubar);
6870 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
6872 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
6874 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
6877 if (prefs_common.toolbar_detachable) {
6878 handlebox = gtk_handle_box_new();
6880 handlebox = gtk_hbox_new(FALSE, 0);
6882 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
6884 gtk_widget_realize(handlebox);
6886 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
6889 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
6893 vbox2 = gtk_vbox_new(FALSE, 2);
6894 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
6895 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
6898 notebook = gtk_notebook_new();
6899 gtk_widget_set_size_request(notebook, -1, 130);
6900 gtk_widget_show(notebook);
6902 /* header labels and entries */
6903 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6904 compose_create_header(compose),
6905 gtk_label_new_with_mnemonic(_("Hea_der")));
6906 /* attachment list */
6907 attach_hbox = gtk_hbox_new(FALSE, 0);
6908 gtk_widget_show(attach_hbox);
6910 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
6911 gtk_widget_show(attach_lab1);
6912 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
6914 attach_lab2 = gtk_label_new("");
6915 gtk_widget_show(attach_lab2);
6916 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
6918 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6919 compose_create_attach(compose),
6922 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6923 compose_create_others(compose),
6924 gtk_label_new_with_mnemonic(_("Othe_rs")));
6927 subject_hbox = gtk_hbox_new(FALSE, 0);
6928 gtk_widget_show(subject_hbox);
6930 subject_frame = gtk_frame_new(NULL);
6931 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
6932 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
6933 gtk_widget_show(subject_frame);
6935 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
6936 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
6937 gtk_widget_show(subject);
6939 label = gtk_label_new(_("Subject:"));
6940 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
6941 gtk_widget_show(label);
6943 subject_entry = gtk_entry_new();
6944 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
6945 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
6946 G_CALLBACK(compose_grab_focus_cb), compose);
6947 gtk_widget_show(subject_entry);
6948 compose->subject_entry = subject_entry;
6949 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
6951 edit_vbox = gtk_vbox_new(FALSE, 0);
6953 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
6956 ruler_hbox = gtk_hbox_new(FALSE, 0);
6957 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
6959 ruler = gtk_shruler_new();
6960 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
6961 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
6965 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6966 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
6967 GTK_POLICY_AUTOMATIC,
6968 GTK_POLICY_AUTOMATIC);
6969 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
6971 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
6972 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
6974 text = gtk_text_view_new();
6975 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
6976 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
6977 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
6978 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
6979 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
6981 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
6983 g_signal_connect_after(G_OBJECT(text), "size_allocate",
6984 G_CALLBACK(compose_edit_size_alloc),
6986 g_signal_connect(G_OBJECT(buffer), "changed",
6987 G_CALLBACK(compose_changed_cb), compose);
6988 g_signal_connect(G_OBJECT(text), "grab_focus",
6989 G_CALLBACK(compose_grab_focus_cb), compose);
6990 g_signal_connect(G_OBJECT(buffer), "insert_text",
6991 G_CALLBACK(text_inserted), compose);
6992 g_signal_connect(G_OBJECT(text), "button_press_event",
6993 G_CALLBACK(text_clicked), compose);
6995 g_signal_connect(G_OBJECT(text), "popup-menu",
6996 G_CALLBACK(compose_popup_menu), compose);
6998 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
6999 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7000 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7001 G_CALLBACK(compose_popup_menu), compose);
7003 g_signal_connect(G_OBJECT(subject_entry), "changed",
7004 G_CALLBACK(compose_changed_cb), compose);
7007 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7008 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7009 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7010 g_signal_connect(G_OBJECT(text), "drag_data_received",
7011 G_CALLBACK(compose_insert_drag_received_cb),
7013 g_signal_connect(G_OBJECT(text), "drag-drop",
7014 G_CALLBACK(compose_drag_drop),
7016 gtk_widget_show_all(vbox);
7018 /* pane between attach clist and text */
7019 paned = gtk_vpaned_new();
7020 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7022 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7023 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7025 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7027 gtk_paned_add1(GTK_PANED(paned), notebook);
7028 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7029 gtk_widget_show_all(paned);
7032 if (prefs_common.textfont) {
7033 PangoFontDescription *font_desc;
7035 font_desc = pango_font_description_from_string
7036 (prefs_common.textfont);
7038 gtk_widget_modify_font(text, font_desc);
7039 pango_font_description_free(font_desc);
7043 gtk_action_group_add_actions(action_group, compose_popup_entries,
7044 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7045 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7046 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7047 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7048 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7049 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7050 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7052 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7054 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7055 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7056 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7058 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7060 undostruct = undo_init(text);
7061 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7064 address_completion_start(window);
7066 compose->window = window;
7067 compose->vbox = vbox;
7068 compose->menubar = menubar;
7069 compose->handlebox = handlebox;
7071 compose->vbox2 = vbox2;
7073 compose->paned = paned;
7075 compose->attach_label = attach_lab2;
7077 compose->notebook = notebook;
7078 compose->edit_vbox = edit_vbox;
7079 compose->ruler_hbox = ruler_hbox;
7080 compose->ruler = ruler;
7081 compose->scrolledwin = scrolledwin;
7082 compose->text = text;
7084 compose->focused_editable = NULL;
7086 compose->popupmenu = popupmenu;
7088 compose->tmpl_menu = tmpl_menu;
7090 compose->mode = mode;
7091 compose->rmode = mode;
7093 compose->targetinfo = NULL;
7094 compose->replyinfo = NULL;
7095 compose->fwdinfo = NULL;
7097 compose->replyto = NULL;
7099 compose->bcc = NULL;
7100 compose->followup_to = NULL;
7102 compose->ml_post = NULL;
7104 compose->inreplyto = NULL;
7105 compose->references = NULL;
7106 compose->msgid = NULL;
7107 compose->boundary = NULL;
7109 compose->autowrap = prefs_common.autowrap;
7111 compose->use_signing = FALSE;
7112 compose->use_encryption = FALSE;
7113 compose->privacy_system = NULL;
7115 compose->modified = FALSE;
7117 compose->return_receipt = FALSE;
7119 compose->to_list = NULL;
7120 compose->newsgroup_list = NULL;
7122 compose->undostruct = undostruct;
7124 compose->sig_str = NULL;
7126 compose->exteditor_file = NULL;
7127 compose->exteditor_pid = -1;
7128 compose->exteditor_tag = -1;
7129 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7132 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7133 if (mode != COMPOSE_REDIRECT) {
7134 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7135 strcmp(prefs_common.dictionary, "")) {
7136 gtkaspell = gtkaspell_new(prefs_common.aspell_path,
7137 prefs_common.dictionary,
7138 prefs_common.alt_dictionary,
7139 conv_get_locale_charset_str(),
7140 prefs_common.misspelled_col,
7141 prefs_common.check_while_typing,
7142 prefs_common.recheck_when_changing_dict,
7143 prefs_common.use_alternate,
7144 prefs_common.use_both_dicts,
7145 GTK_TEXT_VIEW(text),
7146 GTK_WINDOW(compose->window),
7147 compose_spell_menu_changed,
7150 alertpanel_error(_("Spell checker could not "
7152 gtkaspell_checkers_strerror());
7153 gtkaspell_checkers_reset_error();
7155 if (!gtkaspell_set_sug_mode(gtkaspell,
7156 prefs_common.aspell_sugmode)) {
7157 debug_print("Aspell: could not set "
7158 "suggestion mode %s\n",
7159 gtkaspell_checkers_strerror());
7160 gtkaspell_checkers_reset_error();
7163 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7167 compose->gtkaspell = gtkaspell;
7168 compose_spell_menu_changed(compose);
7171 compose_select_account(compose, account, TRUE);
7173 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7175 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7176 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7178 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7179 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7181 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7182 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7184 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7185 if (account->protocol != A_NNTP)
7186 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(compose->header_last->combo)->child),
7187 prefs_common_translated_header_name("To:"));
7189 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(compose->header_last->combo)->child),
7190 prefs_common_translated_header_name("Newsgroups:"));
7192 addressbook_set_target_compose(compose);
7194 if (mode != COMPOSE_REDIRECT)
7195 compose_set_template_menu(compose);
7197 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7200 compose_list = g_list_append(compose_list, compose);
7202 if (!prefs_common.show_ruler)
7203 gtk_widget_hide(ruler_hbox);
7205 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7208 compose->priority = PRIORITY_NORMAL;
7209 compose_update_priority_menu_item(compose);
7211 compose_set_out_encoding(compose);
7214 compose_update_actions_menu(compose);
7216 /* Privacy Systems menu */
7217 compose_update_privacy_systems_menu(compose);
7219 activate_privacy_system(compose, account, TRUE);
7220 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7222 gtk_widget_realize(window);
7224 gtk_widget_show(window);
7226 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7227 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7234 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7239 GtkWidget *optmenubox;
7242 GtkWidget *from_name = NULL;
7243 #if !(GTK_CHECK_VERSION(2,12,0))
7244 GtkTooltips *tips = compose->tooltips;
7247 gint num = 0, def_menu = 0;
7249 accounts = account_get_list();
7250 g_return_val_if_fail(accounts != NULL, NULL);
7252 optmenubox = gtk_event_box_new();
7253 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7254 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7256 hbox = gtk_hbox_new(FALSE, 6);
7257 from_name = gtk_entry_new();
7259 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7260 G_CALLBACK(compose_grab_focus_cb), compose);
7262 for (; accounts != NULL; accounts = accounts->next, num++) {
7263 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7264 gchar *name, *from = NULL;
7266 if (ac == compose->account) def_menu = num;
7268 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7271 if (ac == compose->account) {
7272 if (ac->name && *ac->name) {
7274 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7275 from = g_strdup_printf("%s <%s>",
7277 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7279 from = g_strdup_printf("%s",
7281 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7284 COMBOBOX_ADD(menu, name, ac->account_id);
7289 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7291 g_signal_connect(G_OBJECT(optmenu), "changed",
7292 G_CALLBACK(account_activated),
7294 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7295 G_CALLBACK(compose_entry_popup_extend),
7298 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7299 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7301 CLAWS_SET_TIP(optmenubox,
7302 _("Account to use for this email"));
7303 CLAWS_SET_TIP(from_name,
7304 _("Sender address to be used"));
7306 compose->from_name = from_name;
7311 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7313 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7314 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7315 Compose *compose = (Compose *) data;
7317 printf("activated %d\n", value);
7318 compose->priority = value;
7322 static void compose_reply_change_mode(Compose *compose,
7325 gboolean was_modified = compose->modified;
7327 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7329 g_return_if_fail(compose->replyinfo != NULL);
7331 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7333 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7335 if (action == COMPOSE_REPLY_TO_ALL)
7337 if (action == COMPOSE_REPLY_TO_SENDER)
7339 if (action == COMPOSE_REPLY_TO_LIST)
7342 compose_remove_header_entries(compose);
7343 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7344 if (compose->account->set_autocc && compose->account->auto_cc)
7345 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7347 if (compose->account->set_autobcc && compose->account->auto_bcc)
7348 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7350 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7351 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7352 compose_show_first_last_header(compose, TRUE);
7353 compose->modified = was_modified;
7354 compose_set_title(compose);
7357 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7359 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7360 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7361 Compose *compose = (Compose *) data;
7364 compose_reply_change_mode(compose, value);
7367 static void compose_update_priority_menu_item(Compose * compose)
7369 GtkWidget *menuitem = NULL;
7370 switch (compose->priority) {
7371 case PRIORITY_HIGHEST:
7372 menuitem = gtk_ui_manager_get_widget
7373 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7376 menuitem = gtk_ui_manager_get_widget
7377 (compose->ui_manager, "/Menu/Options/Priority/High");
7379 case PRIORITY_NORMAL:
7380 menuitem = gtk_ui_manager_get_widget
7381 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7384 menuitem = gtk_ui_manager_get_widget
7385 (compose->ui_manager, "/Menu/Options/Priority/Low");
7387 case PRIORITY_LOWEST:
7388 menuitem = gtk_ui_manager_get_widget
7389 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7392 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7395 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7397 Compose *compose = (Compose *) data;
7399 gboolean can_sign = FALSE, can_encrypt = FALSE;
7401 g_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7403 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7406 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7407 g_free(compose->privacy_system);
7408 compose->privacy_system = NULL;
7409 if (systemid != NULL) {
7410 compose->privacy_system = g_strdup(systemid);
7412 can_sign = privacy_system_can_sign(systemid);
7413 can_encrypt = privacy_system_can_encrypt(systemid);
7416 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7418 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7419 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7422 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7424 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7425 GtkWidget *menuitem = NULL;
7427 gboolean can_sign = FALSE, can_encrypt = FALSE;
7428 gboolean found = FALSE;
7430 if (compose->privacy_system != NULL) {
7432 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7433 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7434 g_return_if_fail(menuitem != NULL);
7436 amenu = GTK_MENU_SHELL(menuitem)->children;
7438 while (amenu != NULL) {
7439 GList *alist = amenu->next;
7441 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7442 if (systemid != NULL) {
7443 if (strcmp(systemid, compose->privacy_system) == 0) {
7444 menuitem = GTK_WIDGET(amenu->data);
7446 can_sign = privacy_system_can_sign(systemid);
7447 can_encrypt = privacy_system_can_encrypt(systemid);
7451 } else if (strlen(compose->privacy_system) == 0) {
7452 menuitem = GTK_WIDGET(amenu->data);
7455 can_encrypt = FALSE;
7462 if (menuitem != NULL)
7463 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7465 if (warn && !found && strlen(compose->privacy_system)) {
7466 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7467 "will not be able to sign or encrypt this message."),
7468 compose->privacy_system);
7472 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7473 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7476 static void compose_set_out_encoding(Compose *compose)
7478 CharSet out_encoding;
7479 const gchar *branch = NULL;
7480 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7482 switch(out_encoding) {
7483 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7484 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7485 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7486 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7487 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7488 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7489 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7490 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7491 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7492 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7493 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7494 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7495 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7496 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7497 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7498 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7499 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7500 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7501 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7502 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7503 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7504 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7505 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7506 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7507 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7508 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7509 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7510 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7511 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7512 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7513 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7514 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7516 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7519 static void compose_set_template_menu(Compose *compose)
7521 GSList *tmpl_list, *cur;
7525 tmpl_list = template_get_config();
7527 menu = gtk_menu_new();
7529 gtk_menu_set_accel_group (GTK_MENU (menu),
7530 gtk_ui_manager_get_accel_group(compose->ui_manager));
7531 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7532 Template *tmpl = (Template *)cur->data;
7533 gchar *accel_path = NULL;
7534 item = gtk_menu_item_new_with_label(tmpl->name);
7535 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7536 g_signal_connect(G_OBJECT(item), "activate",
7537 G_CALLBACK(compose_template_activate_cb),
7539 g_object_set_data(G_OBJECT(item), "template", tmpl);
7540 gtk_widget_show(item);
7541 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7542 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7546 gtk_widget_show(menu);
7547 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7550 void compose_update_actions_menu(Compose *compose)
7552 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7555 static void compose_update_privacy_systems_menu(Compose *compose)
7557 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7558 GSList *systems, *cur;
7560 GtkWidget *system_none;
7562 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7563 GtkWidget *privacy_menu = gtk_menu_new();
7565 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7566 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7568 g_signal_connect(G_OBJECT(system_none), "activate",
7569 G_CALLBACK(compose_set_privacy_system_cb), compose);
7571 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7572 gtk_widget_show(system_none);
7574 systems = privacy_get_system_ids();
7575 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7576 gchar *systemid = cur->data;
7578 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7579 widget = gtk_radio_menu_item_new_with_label(group,
7580 privacy_system_get_name(systemid));
7581 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7582 g_strdup(systemid), g_free);
7583 g_signal_connect(G_OBJECT(widget), "activate",
7584 G_CALLBACK(compose_set_privacy_system_cb), compose);
7586 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7587 gtk_widget_show(widget);
7590 g_slist_free(systems);
7591 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7592 gtk_widget_show_all(privacy_menu);
7593 gtk_widget_show_all(privacy_menuitem);
7596 void compose_reflect_prefs_all(void)
7601 for (cur = compose_list; cur != NULL; cur = cur->next) {
7602 compose = (Compose *)cur->data;
7603 compose_set_template_menu(compose);
7607 void compose_reflect_prefs_pixmap_theme(void)
7612 for (cur = compose_list; cur != NULL; cur = cur->next) {
7613 compose = (Compose *)cur->data;
7614 toolbar_update(TOOLBAR_COMPOSE, compose);
7618 static const gchar *compose_quote_char_from_context(Compose *compose)
7620 const gchar *qmark = NULL;
7622 g_return_val_if_fail(compose != NULL, NULL);
7624 switch (compose->mode) {
7625 /* use forward-specific quote char */
7626 case COMPOSE_FORWARD:
7627 case COMPOSE_FORWARD_AS_ATTACH:
7628 case COMPOSE_FORWARD_INLINE:
7629 if (compose->folder && compose->folder->prefs &&
7630 compose->folder->prefs->forward_with_format)
7631 qmark = compose->folder->prefs->forward_quotemark;
7632 else if (compose->account->forward_with_format)
7633 qmark = compose->account->forward_quotemark;
7635 qmark = prefs_common.fw_quotemark;
7638 /* use reply-specific quote char in all other modes */
7640 if (compose->folder && compose->folder->prefs &&
7641 compose->folder->prefs->reply_with_format)
7642 qmark = compose->folder->prefs->reply_quotemark;
7643 else if (compose->account->reply_with_format)
7644 qmark = compose->account->reply_quotemark;
7646 qmark = prefs_common.quotemark;
7650 if (qmark == NULL || *qmark == '\0')
7656 static void compose_template_apply(Compose *compose, Template *tmpl,
7660 GtkTextBuffer *buffer;
7664 gchar *parsed_str = NULL;
7665 gint cursor_pos = 0;
7666 const gchar *err_msg = _("Template body format error at line %d.");
7669 /* process the body */
7671 text = GTK_TEXT_VIEW(compose->text);
7672 buffer = gtk_text_view_get_buffer(text);
7675 qmark = compose_quote_char_from_context(compose);
7677 if (compose->replyinfo != NULL) {
7680 gtk_text_buffer_set_text(buffer, "", -1);
7681 mark = gtk_text_buffer_get_insert(buffer);
7682 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7684 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7685 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7687 } else if (compose->fwdinfo != NULL) {
7690 gtk_text_buffer_set_text(buffer, "", -1);
7691 mark = gtk_text_buffer_get_insert(buffer);
7692 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7694 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7695 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7698 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7700 GtkTextIter start, end;
7703 gtk_text_buffer_get_start_iter(buffer, &start);
7704 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7705 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7707 /* clear the buffer now */
7709 gtk_text_buffer_set_text(buffer, "", -1);
7711 parsed_str = compose_quote_fmt(compose, dummyinfo,
7712 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7713 procmsg_msginfo_free( dummyinfo );
7719 gtk_text_buffer_set_text(buffer, "", -1);
7720 mark = gtk_text_buffer_get_insert(buffer);
7721 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7724 if (replace && parsed_str && compose->account->auto_sig)
7725 compose_insert_sig(compose, FALSE);
7727 if (replace && parsed_str) {
7728 gtk_text_buffer_get_start_iter(buffer, &iter);
7729 gtk_text_buffer_place_cursor(buffer, &iter);
7733 cursor_pos = quote_fmt_get_cursor_pos();
7734 compose->set_cursor_pos = cursor_pos;
7735 if (cursor_pos == -1)
7737 gtk_text_buffer_get_start_iter(buffer, &iter);
7738 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7739 gtk_text_buffer_place_cursor(buffer, &iter);
7742 /* process the other fields */
7744 compose_template_apply_fields(compose, tmpl);
7745 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7746 quote_fmt_reset_vartable();
7747 compose_changed_cb(NULL, compose);
7750 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7752 MsgInfo* dummyinfo = NULL;
7753 MsgInfo *msginfo = NULL;
7756 if (compose->replyinfo != NULL)
7757 msginfo = compose->replyinfo;
7758 else if (compose->fwdinfo != NULL)
7759 msginfo = compose->fwdinfo;
7761 dummyinfo = compose_msginfo_new_from_compose(compose);
7762 msginfo = dummyinfo;
7765 if (tmpl->from && *tmpl->from != '\0') {
7767 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7768 compose->gtkaspell);
7770 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7772 quote_fmt_scan_string(tmpl->from);
7775 buf = quote_fmt_get_buffer();
7777 alertpanel_error(_("Template From format error."));
7779 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
7783 if (tmpl->to && *tmpl->to != '\0') {
7785 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7786 compose->gtkaspell);
7788 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7790 quote_fmt_scan_string(tmpl->to);
7793 buf = quote_fmt_get_buffer();
7795 alertpanel_error(_("Template To format error."));
7797 compose_entry_append(compose, buf, COMPOSE_TO);
7801 if (tmpl->cc && *tmpl->cc != '\0') {
7803 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7804 compose->gtkaspell);
7806 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7808 quote_fmt_scan_string(tmpl->cc);
7811 buf = quote_fmt_get_buffer();
7813 alertpanel_error(_("Template Cc format error."));
7815 compose_entry_append(compose, buf, COMPOSE_CC);
7819 if (tmpl->bcc && *tmpl->bcc != '\0') {
7821 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7822 compose->gtkaspell);
7824 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7826 quote_fmt_scan_string(tmpl->bcc);
7829 buf = quote_fmt_get_buffer();
7831 alertpanel_error(_("Template Bcc format error."));
7833 compose_entry_append(compose, buf, COMPOSE_BCC);
7837 /* process the subject */
7838 if (tmpl->subject && *tmpl->subject != '\0') {
7840 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7841 compose->gtkaspell);
7843 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7845 quote_fmt_scan_string(tmpl->subject);
7848 buf = quote_fmt_get_buffer();
7850 alertpanel_error(_("Template subject format error."));
7852 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
7856 procmsg_msginfo_free( dummyinfo );
7859 static void compose_destroy(Compose *compose)
7861 GtkTextBuffer *buffer;
7862 GtkClipboard *clipboard;
7864 compose_list = g_list_remove(compose_list, compose);
7866 if (compose->updating) {
7867 debug_print("danger, not destroying anything now\n");
7868 compose->deferred_destroy = TRUE;
7871 /* NOTE: address_completion_end() does nothing with the window
7872 * however this may change. */
7873 address_completion_end(compose->window);
7875 slist_free_strings(compose->to_list);
7876 g_slist_free(compose->to_list);
7877 slist_free_strings(compose->newsgroup_list);
7878 g_slist_free(compose->newsgroup_list);
7879 slist_free_strings(compose->header_list);
7880 g_slist_free(compose->header_list);
7882 procmsg_msginfo_free(compose->targetinfo);
7883 procmsg_msginfo_free(compose->replyinfo);
7884 procmsg_msginfo_free(compose->fwdinfo);
7886 g_free(compose->replyto);
7887 g_free(compose->cc);
7888 g_free(compose->bcc);
7889 g_free(compose->newsgroups);
7890 g_free(compose->followup_to);
7892 g_free(compose->ml_post);
7894 g_free(compose->inreplyto);
7895 g_free(compose->references);
7896 g_free(compose->msgid);
7897 g_free(compose->boundary);
7899 g_free(compose->redirect_filename);
7900 if (compose->undostruct)
7901 undo_destroy(compose->undostruct);
7903 g_free(compose->sig_str);
7905 g_free(compose->exteditor_file);
7907 g_free(compose->orig_charset);
7909 g_free(compose->privacy_system);
7911 if (addressbook_get_target_compose() == compose)
7912 addressbook_set_target_compose(NULL);
7915 if (compose->gtkaspell) {
7916 gtkaspell_delete(compose->gtkaspell);
7917 compose->gtkaspell = NULL;
7921 prefs_common.compose_width = compose->scrolledwin->allocation.width;
7922 prefs_common.compose_height = compose->window->allocation.height;
7924 if (!gtk_widget_get_parent(compose->paned))
7925 gtk_widget_destroy(compose->paned);
7926 gtk_widget_destroy(compose->popupmenu);
7928 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
7929 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7930 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
7932 gtk_widget_destroy(compose->window);
7933 toolbar_destroy(compose->toolbar);
7934 g_free(compose->toolbar);
7935 g_mutex_free(compose->mutex);
7939 static void compose_attach_info_free(AttachInfo *ainfo)
7941 g_free(ainfo->file);
7942 g_free(ainfo->content_type);
7943 g_free(ainfo->name);
7947 static void compose_attach_update_label(Compose *compose)
7952 GtkTreeModel *model;
7957 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
7958 if(!gtk_tree_model_get_iter_first(model, &iter)) {
7959 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
7963 while(gtk_tree_model_iter_next(model, &iter))
7966 text = g_strdup_printf("(%d)", i);
7967 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
7971 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
7973 Compose *compose = (Compose *)data;
7974 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
7975 GtkTreeSelection *selection;
7977 GtkTreeModel *model;
7979 selection = gtk_tree_view_get_selection(tree_view);
7980 sel = gtk_tree_selection_get_selected_rows(selection, &model);
7985 for (cur = sel; cur != NULL; cur = cur->next) {
7986 GtkTreePath *path = cur->data;
7987 GtkTreeRowReference *ref = gtk_tree_row_reference_new
7990 gtk_tree_path_free(path);
7993 for (cur = sel; cur != NULL; cur = cur->next) {
7994 GtkTreeRowReference *ref = cur->data;
7995 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
7998 if (gtk_tree_model_get_iter(model, &iter, path))
7999 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8001 gtk_tree_path_free(path);
8002 gtk_tree_row_reference_free(ref);
8006 compose_attach_update_label(compose);
8009 static struct _AttachProperty
8012 GtkWidget *mimetype_entry;
8013 GtkWidget *encoding_optmenu;
8014 GtkWidget *path_entry;
8015 GtkWidget *filename_entry;
8017 GtkWidget *cancel_btn;
8020 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8022 gtk_tree_path_free((GtkTreePath *)ptr);
8025 static void compose_attach_property(GtkAction *action, gpointer data)
8027 Compose *compose = (Compose *)data;
8028 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8030 GtkComboBox *optmenu;
8031 GtkTreeSelection *selection;
8033 GtkTreeModel *model;
8036 static gboolean cancelled;
8038 /* only if one selected */
8039 selection = gtk_tree_view_get_selection(tree_view);
8040 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8043 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8047 path = (GtkTreePath *) sel->data;
8048 gtk_tree_model_get_iter(model, &iter, path);
8049 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8052 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8058 if (!attach_prop.window)
8059 compose_attach_property_create(&cancelled);
8060 gtk_widget_grab_focus(attach_prop.ok_btn);
8061 gtk_widget_show(attach_prop.window);
8062 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8064 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8065 if (ainfo->encoding == ENC_UNKNOWN)
8066 combobox_select_by_data(optmenu, ENC_BASE64);
8068 combobox_select_by_data(optmenu, ainfo->encoding);
8070 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8071 ainfo->content_type ? ainfo->content_type : "");
8072 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8073 ainfo->file ? ainfo->file : "");
8074 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8075 ainfo->name ? ainfo->name : "");
8078 const gchar *entry_text;
8080 gchar *cnttype = NULL;
8087 gtk_widget_hide(attach_prop.window);
8092 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8093 if (*entry_text != '\0') {
8096 text = g_strstrip(g_strdup(entry_text));
8097 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8098 cnttype = g_strdup(text);
8101 alertpanel_error(_("Invalid MIME type."));
8107 ainfo->encoding = combobox_get_active_data(optmenu);
8109 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8110 if (*entry_text != '\0') {
8111 if (is_file_exist(entry_text) &&
8112 (size = get_file_size(entry_text)) > 0)
8113 file = g_strdup(entry_text);
8116 (_("File doesn't exist or is empty."));
8122 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8123 if (*entry_text != '\0') {
8124 g_free(ainfo->name);
8125 ainfo->name = g_strdup(entry_text);
8129 g_free(ainfo->content_type);
8130 ainfo->content_type = cnttype;
8133 g_free(ainfo->file);
8137 ainfo->size = (goffset)size;
8139 /* update tree store */
8140 text = to_human_readable(ainfo->size);
8141 gtk_tree_model_get_iter(model, &iter, path);
8142 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8143 COL_MIMETYPE, ainfo->content_type,
8145 COL_NAME, ainfo->name,
8151 gtk_tree_path_free(path);
8154 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8156 label = gtk_label_new(str); \
8157 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8158 GTK_FILL, 0, 0, 0); \
8159 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8161 entry = gtk_entry_new(); \
8162 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8163 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8166 static void compose_attach_property_create(gboolean *cancelled)
8172 GtkWidget *mimetype_entry;
8175 GtkListStore *optmenu_menu;
8176 GtkWidget *path_entry;
8177 GtkWidget *filename_entry;
8180 GtkWidget *cancel_btn;
8181 GList *mime_type_list, *strlist;
8184 debug_print("Creating attach_property window...\n");
8186 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8187 gtk_widget_set_size_request(window, 480, -1);
8188 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8189 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8190 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8191 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8192 g_signal_connect(G_OBJECT(window), "delete_event",
8193 G_CALLBACK(attach_property_delete_event),
8195 g_signal_connect(G_OBJECT(window), "key_press_event",
8196 G_CALLBACK(attach_property_key_pressed),
8199 vbox = gtk_vbox_new(FALSE, 8);
8200 gtk_container_add(GTK_CONTAINER(window), vbox);
8202 table = gtk_table_new(4, 2, FALSE);
8203 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8204 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8205 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8207 label = gtk_label_new(_("MIME type"));
8208 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8210 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8211 mimetype_entry = gtk_combo_box_entry_new_text();
8212 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8213 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8215 /* stuff with list */
8216 mime_type_list = procmime_get_mime_type_list();
8218 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8219 MimeType *type = (MimeType *) mime_type_list->data;
8222 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8224 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8227 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8228 (GCompareFunc)strcmp2);
8231 for (mime_type_list = strlist; mime_type_list != NULL;
8232 mime_type_list = mime_type_list->next) {
8233 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8234 g_free(mime_type_list->data);
8236 g_list_free(strlist);
8237 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8238 mimetype_entry = GTK_BIN(mimetype_entry)->child;
8240 label = gtk_label_new(_("Encoding"));
8241 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8243 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8245 hbox = gtk_hbox_new(FALSE, 0);
8246 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8247 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8249 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8250 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8252 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8253 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8254 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8255 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8256 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8258 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8260 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8261 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8263 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8264 &ok_btn, GTK_STOCK_OK,
8266 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8267 gtk_widget_grab_default(ok_btn);
8269 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8270 G_CALLBACK(attach_property_ok),
8272 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8273 G_CALLBACK(attach_property_cancel),
8276 gtk_widget_show_all(vbox);
8278 attach_prop.window = window;
8279 attach_prop.mimetype_entry = mimetype_entry;
8280 attach_prop.encoding_optmenu = optmenu;
8281 attach_prop.path_entry = path_entry;
8282 attach_prop.filename_entry = filename_entry;
8283 attach_prop.ok_btn = ok_btn;
8284 attach_prop.cancel_btn = cancel_btn;
8287 #undef SET_LABEL_AND_ENTRY
8289 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8295 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8301 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8302 gboolean *cancelled)
8310 static gboolean attach_property_key_pressed(GtkWidget *widget,
8312 gboolean *cancelled)
8314 if (event && event->keyval == GDK_Escape) {
8321 static void compose_exec_ext_editor(Compose *compose)
8328 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8329 G_DIR_SEPARATOR, compose);
8331 if (pipe(pipe_fds) < 0) {
8337 if ((pid = fork()) < 0) {
8344 /* close the write side of the pipe */
8347 compose->exteditor_file = g_strdup(tmp);
8348 compose->exteditor_pid = pid;
8350 compose_set_ext_editor_sensitive(compose, FALSE);
8352 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8353 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8357 } else { /* process-monitoring process */
8363 /* close the read side of the pipe */
8366 if (compose_write_body_to_file(compose, tmp) < 0) {
8367 fd_write_all(pipe_fds[1], "2\n", 2);
8371 pid_ed = compose_exec_ext_editor_real(tmp);
8373 fd_write_all(pipe_fds[1], "1\n", 2);
8377 /* wait until editor is terminated */
8378 waitpid(pid_ed, NULL, 0);
8380 fd_write_all(pipe_fds[1], "0\n", 2);
8387 #endif /* G_OS_UNIX */
8391 static gint compose_exec_ext_editor_real(const gchar *file)
8398 g_return_val_if_fail(file != NULL, -1);
8400 if ((pid = fork()) < 0) {
8405 if (pid != 0) return pid;
8407 /* grandchild process */
8409 if (setpgid(0, getppid()))
8412 if (prefs_common_get_ext_editor_cmd() &&
8413 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8414 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8415 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8417 if (prefs_common_get_ext_editor_cmd())
8418 g_warning("External editor command line is invalid: '%s'\n",
8419 prefs_common_get_ext_editor_cmd());
8420 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8423 cmdline = strsplit_with_quote(buf, " ", 1024);
8424 execvp(cmdline[0], cmdline);
8427 g_strfreev(cmdline);
8432 static gboolean compose_ext_editor_kill(Compose *compose)
8434 pid_t pgid = compose->exteditor_pid * -1;
8437 ret = kill(pgid, 0);
8439 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8443 msg = g_strdup_printf
8444 (_("The external editor is still working.\n"
8445 "Force terminating the process?\n"
8446 "process group id: %d"), -pgid);
8447 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8448 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8452 if (val == G_ALERTALTERNATE) {
8453 g_source_remove(compose->exteditor_tag);
8454 g_io_channel_shutdown(compose->exteditor_ch,
8456 g_io_channel_unref(compose->exteditor_ch);
8458 if (kill(pgid, SIGTERM) < 0) perror("kill");
8459 waitpid(compose->exteditor_pid, NULL, 0);
8461 g_warning("Terminated process group id: %d", -pgid);
8462 g_warning("Temporary file: %s",
8463 compose->exteditor_file);
8465 compose_set_ext_editor_sensitive(compose, TRUE);
8467 g_free(compose->exteditor_file);
8468 compose->exteditor_file = NULL;
8469 compose->exteditor_pid = -1;
8470 compose->exteditor_ch = NULL;
8471 compose->exteditor_tag = -1;
8479 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8483 Compose *compose = (Compose *)data;
8486 debug_print(_("Compose: input from monitoring process\n"));
8488 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8490 g_io_channel_shutdown(source, FALSE, NULL);
8491 g_io_channel_unref(source);
8493 waitpid(compose->exteditor_pid, NULL, 0);
8495 if (buf[0] == '0') { /* success */
8496 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8497 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8499 gtk_text_buffer_set_text(buffer, "", -1);
8500 compose_insert_file(compose, compose->exteditor_file);
8501 compose_changed_cb(NULL, compose);
8503 if (claws_unlink(compose->exteditor_file) < 0)
8504 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8505 } else if (buf[0] == '1') { /* failed */
8506 g_warning("Couldn't exec external editor\n");
8507 if (claws_unlink(compose->exteditor_file) < 0)
8508 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8509 } else if (buf[0] == '2') {
8510 g_warning("Couldn't write to file\n");
8511 } else if (buf[0] == '3') {
8512 g_warning("Pipe read failed\n");
8515 compose_set_ext_editor_sensitive(compose, TRUE);
8517 g_free(compose->exteditor_file);
8518 compose->exteditor_file = NULL;
8519 compose->exteditor_pid = -1;
8520 compose->exteditor_ch = NULL;
8521 compose->exteditor_tag = -1;
8526 static void compose_set_ext_editor_sensitive(Compose *compose,
8529 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8530 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8531 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8532 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8533 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8534 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8535 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8537 gtk_widget_set_sensitive(compose->text, sensitive);
8538 if (compose->toolbar->send_btn)
8539 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8540 if (compose->toolbar->sendl_btn)
8541 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8542 if (compose->toolbar->draft_btn)
8543 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8544 if (compose->toolbar->insert_btn)
8545 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8546 if (compose->toolbar->sig_btn)
8547 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8548 if (compose->toolbar->exteditor_btn)
8549 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8550 if (compose->toolbar->linewrap_current_btn)
8551 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8552 if (compose->toolbar->linewrap_all_btn)
8553 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8555 #endif /* G_OS_UNIX */
8558 * compose_undo_state_changed:
8560 * Change the sensivity of the menuentries undo and redo
8562 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8563 gint redo_state, gpointer data)
8565 Compose *compose = (Compose *)data;
8567 switch (undo_state) {
8568 case UNDO_STATE_TRUE:
8569 if (!undostruct->undo_state) {
8570 undostruct->undo_state = TRUE;
8571 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8574 case UNDO_STATE_FALSE:
8575 if (undostruct->undo_state) {
8576 undostruct->undo_state = FALSE;
8577 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8580 case UNDO_STATE_UNCHANGED:
8582 case UNDO_STATE_REFRESH:
8583 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8586 g_warning("Undo state not recognized");
8590 switch (redo_state) {
8591 case UNDO_STATE_TRUE:
8592 if (!undostruct->redo_state) {
8593 undostruct->redo_state = TRUE;
8594 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8597 case UNDO_STATE_FALSE:
8598 if (undostruct->redo_state) {
8599 undostruct->redo_state = FALSE;
8600 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8603 case UNDO_STATE_UNCHANGED:
8605 case UNDO_STATE_REFRESH:
8606 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8609 g_warning("Redo state not recognized");
8614 /* callback functions */
8616 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8617 * includes "non-client" (windows-izm) in calculation, so this calculation
8618 * may not be accurate.
8620 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8621 GtkAllocation *allocation,
8622 GtkSHRuler *shruler)
8624 if (prefs_common.show_ruler) {
8625 gint char_width = 0, char_height = 0;
8626 gint line_width_in_chars;
8628 gtkut_get_font_size(GTK_WIDGET(widget),
8629 &char_width, &char_height);
8630 line_width_in_chars =
8631 (allocation->width - allocation->x) / char_width;
8633 /* got the maximum */
8634 gtk_ruler_set_range(GTK_RULER(shruler),
8635 0.0, line_width_in_chars, 0,
8636 /*line_width_in_chars*/ char_width);
8642 static void account_activated(GtkComboBox *optmenu, gpointer data)
8644 Compose *compose = (Compose *)data;
8647 gchar *folderidentifier;
8648 gint account_id = 0;
8652 /* Get ID of active account in the combo box */
8653 menu = gtk_combo_box_get_model(optmenu);
8654 gtk_combo_box_get_active_iter(optmenu, &iter);
8655 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8657 ac = account_find_from_id(account_id);
8658 g_return_if_fail(ac != NULL);
8660 if (ac != compose->account)
8661 compose_select_account(compose, ac, FALSE);
8663 /* Set message save folder */
8664 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8665 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8667 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8668 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8670 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8671 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8672 folderidentifier = folder_item_get_identifier(account_get_special_folder
8673 (compose->account, F_OUTBOX));
8674 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
8675 g_free(folderidentifier);
8679 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8680 GtkTreeViewColumn *column, Compose *compose)
8682 compose_attach_property(NULL, compose);
8685 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8688 Compose *compose = (Compose *)data;
8689 GtkTreeSelection *attach_selection;
8690 gint attach_nr_selected;
8692 if (!event) return FALSE;
8694 if (event->button == 3) {
8695 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8696 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8698 if (attach_nr_selected > 0)
8700 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8701 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8703 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8704 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8707 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8708 NULL, NULL, event->button, event->time);
8715 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8718 Compose *compose = (Compose *)data;
8720 if (!event) return FALSE;
8722 switch (event->keyval) {
8724 compose_attach_remove_selected(NULL, compose);
8730 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8732 toolbar_comp_set_sensitive(compose, allow);
8733 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8734 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8736 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8738 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8739 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8740 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8742 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8746 static void compose_send_cb(GtkAction *action, gpointer data)
8748 Compose *compose = (Compose *)data;
8750 if (prefs_common.work_offline &&
8751 !inc_offline_should_override(TRUE,
8752 _("Claws Mail needs network access in order "
8753 "to send this email.")))
8756 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8757 g_source_remove(compose->draft_timeout_tag);
8758 compose->draft_timeout_tag = -1;
8761 compose_send(compose);
8764 static void compose_send_later_cb(GtkAction *action, gpointer data)
8766 Compose *compose = (Compose *)data;
8770 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8774 compose_close(compose);
8775 } else if (val == -1) {
8776 alertpanel_error(_("Could not queue message."));
8777 } else if (val == -2) {
8778 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8779 } else if (val == -3) {
8780 if (privacy_peek_error())
8781 alertpanel_error(_("Could not queue message for sending:\n\n"
8782 "Signature failed: %s"), privacy_get_error());
8783 } else if (val == -4) {
8784 alertpanel_error(_("Could not queue message for sending:\n\n"
8785 "Charset conversion failed."));
8786 } else if (val == -5) {
8787 alertpanel_error(_("Could not queue message for sending:\n\n"
8788 "Couldn't get recipient encryption key."));
8789 } else if (val == -6) {
8792 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
8795 #define DRAFTED_AT_EXIT "drafted_at_exit"
8796 static void compose_register_draft(MsgInfo *info)
8798 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8799 DRAFTED_AT_EXIT, NULL);
8800 FILE *fp = fopen(filepath, "ab");
8803 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
8811 gboolean compose_draft (gpointer data, guint action)
8813 Compose *compose = (Compose *)data;
8817 MsgFlags flag = {0, 0};
8818 static gboolean lock = FALSE;
8819 MsgInfo *newmsginfo;
8821 gboolean target_locked = FALSE;
8822 gboolean err = FALSE;
8824 if (lock) return FALSE;
8826 if (compose->sending)
8829 draft = account_get_special_folder(compose->account, F_DRAFT);
8830 g_return_val_if_fail(draft != NULL, FALSE);
8832 if (!g_mutex_trylock(compose->mutex)) {
8833 /* we don't want to lock the mutex once it's available,
8834 * because as the only other part of compose.c locking
8835 * it is compose_close - which means once unlocked,
8836 * the compose struct will be freed */
8837 debug_print("couldn't lock mutex, probably sending\n");
8843 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
8844 G_DIR_SEPARATOR, compose);
8845 if ((fp = g_fopen(tmp, "wb")) == NULL) {
8846 FILE_OP_ERROR(tmp, "fopen");
8850 /* chmod for security */
8851 if (change_file_mode_rw(fp, tmp) < 0) {
8852 FILE_OP_ERROR(tmp, "chmod");
8853 g_warning("can't change file mode\n");
8856 /* Save draft infos */
8857 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
8858 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
8860 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
8861 gchar *savefolderid;
8863 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8864 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
8865 g_free(savefolderid);
8867 if (compose->return_receipt) {
8868 err |= (fprintf(fp, "RRCPT:1\n") < 0);
8870 if (compose->privacy_system) {
8871 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
8872 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
8873 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
8876 /* Message-ID of message replying to */
8877 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
8880 folderid = folder_item_get_identifier(compose->replyinfo->folder);
8881 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
8884 /* Message-ID of message forwarding to */
8885 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
8888 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
8889 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
8893 /* end of headers */
8894 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
8901 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
8905 if (fclose(fp) == EOF) {
8909 if (compose->targetinfo) {
8910 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
8911 flag.perm_flags = target_locked?MSG_LOCKED:0;
8913 flag.tmp_flags = MSG_DRAFT;
8915 folder_item_scan(draft);
8916 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
8917 MsgInfo *tmpinfo = NULL;
8918 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
8919 if (compose->msgid) {
8920 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
8923 msgnum = tmpinfo->msgnum;
8924 procmsg_msginfo_free(tmpinfo);
8925 debug_print("got draft msgnum %d from scanning\n", msgnum);
8927 debug_print("didn't get draft msgnum after scanning\n");
8930 debug_print("got draft msgnum %d from adding\n", msgnum);
8936 if (action != COMPOSE_AUTO_SAVE) {
8937 if (action != COMPOSE_DRAFT_FOR_EXIT)
8938 alertpanel_error(_("Could not save draft."));
8941 gtkut_window_popup(compose->window);
8942 val = alertpanel_full(_("Could not save draft"),
8943 _("Could not save draft.\n"
8944 "Do you want to cancel exit or discard this email?"),
8945 _("_Cancel exit"), _("_Discard email"), NULL,
8946 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
8947 if (val == G_ALERTALTERNATE) {
8949 g_mutex_unlock(compose->mutex); /* must be done before closing */
8950 compose_close(compose);
8954 g_mutex_unlock(compose->mutex); /* must be done before closing */
8963 if (compose->mode == COMPOSE_REEDIT) {
8964 compose_remove_reedit_target(compose, TRUE);
8967 newmsginfo = folder_item_get_msginfo(draft, msgnum);
8970 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
8972 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
8974 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
8975 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
8976 procmsg_msginfo_set_flags(newmsginfo, 0,
8977 MSG_HAS_ATTACHMENT);
8979 if (action == COMPOSE_DRAFT_FOR_EXIT) {
8980 compose_register_draft(newmsginfo);
8982 procmsg_msginfo_free(newmsginfo);
8985 folder_item_scan(draft);
8987 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
8989 g_mutex_unlock(compose->mutex); /* must be done before closing */
8990 compose_close(compose);
8996 path = folder_item_fetch_msg(draft, msgnum);
8998 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9001 if (g_stat(path, &s) < 0) {
9002 FILE_OP_ERROR(path, "stat");
9008 procmsg_msginfo_free(compose->targetinfo);
9009 compose->targetinfo = procmsg_msginfo_new();
9010 compose->targetinfo->msgnum = msgnum;
9011 compose->targetinfo->size = (goffset)s.st_size;
9012 compose->targetinfo->mtime = s.st_mtime;
9013 compose->targetinfo->folder = draft;
9015 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9016 compose->mode = COMPOSE_REEDIT;
9018 if (action == COMPOSE_AUTO_SAVE) {
9019 compose->autosaved_draft = compose->targetinfo;
9021 compose->modified = FALSE;
9022 compose_set_title(compose);
9026 g_mutex_unlock(compose->mutex);
9030 void compose_clear_exit_drafts(void)
9032 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9033 DRAFTED_AT_EXIT, NULL);
9034 if (is_file_exist(filepath))
9035 claws_unlink(filepath);
9040 void compose_reopen_exit_drafts(void)
9042 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9043 DRAFTED_AT_EXIT, NULL);
9044 FILE *fp = fopen(filepath, "rb");
9048 while (fgets(buf, sizeof(buf), fp)) {
9049 gchar **parts = g_strsplit(buf, "\t", 2);
9050 const gchar *folder = parts[0];
9051 int msgnum = parts[1] ? atoi(parts[1]):-1;
9053 if (folder && *folder && msgnum > -1) {
9054 FolderItem *item = folder_find_item_from_identifier(folder);
9055 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9057 compose_reedit(info, FALSE);
9064 compose_clear_exit_drafts();
9067 static void compose_save_cb(GtkAction *action, gpointer data)
9069 compose_draft(data, COMPOSE_KEEP_EDITING);
9072 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9074 if (compose && file_list) {
9077 for ( tmp = file_list; tmp; tmp = tmp->next) {
9078 gchar *file = (gchar *) tmp->data;
9079 gchar *utf8_filename = conv_filename_to_utf8(file);
9080 compose_attach_append(compose, file, utf8_filename, NULL);
9081 compose_changed_cb(NULL, compose);
9086 g_free(utf8_filename);
9091 static void compose_attach_cb(GtkAction *action, gpointer data)
9093 Compose *compose = (Compose *)data;
9096 if (compose->redirect_filename != NULL)
9099 file_list = filesel_select_multiple_files_open(_("Select file"));
9102 compose_attach_from_list(compose, file_list, TRUE);
9103 g_list_free(file_list);
9107 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9109 Compose *compose = (Compose *)data;
9112 file_list = filesel_select_multiple_files_open(_("Select file"));
9117 for ( tmp = file_list; tmp; tmp = tmp->next) {
9118 gchar *file = (gchar *) tmp->data;
9119 gchar *filedup = g_strdup(file);
9120 gchar *shortfile = g_path_get_basename(filedup);
9121 ComposeInsertResult res;
9123 res = compose_insert_file(compose, file);
9124 if (res == COMPOSE_INSERT_READ_ERROR) {
9125 alertpanel_error(_("File '%s' could not be read."), shortfile);
9126 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9127 alertpanel_error(_("File '%s' contained invalid characters\n"
9128 "for the current encoding, insertion may be incorrect."), shortfile);
9134 g_list_free(file_list);
9138 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9140 Compose *compose = (Compose *)data;
9142 compose_insert_sig(compose, FALSE);
9145 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9149 Compose *compose = (Compose *)data;
9151 gtkut_widget_get_uposition(widget, &x, &y);
9152 prefs_common.compose_x = x;
9153 prefs_common.compose_y = y;
9155 if (compose->sending || compose->updating)
9157 compose_close_cb(NULL, compose);
9161 void compose_close_toolbar(Compose *compose)
9163 compose_close_cb(NULL, compose);
9166 static void compose_close_cb(GtkAction *action, gpointer data)
9168 Compose *compose = (Compose *)data;
9172 if (compose->exteditor_tag != -1) {
9173 if (!compose_ext_editor_kill(compose))
9178 if (compose->modified) {
9179 if (!g_mutex_trylock(compose->mutex)) {
9180 /* we don't want to lock the mutex once it's available,
9181 * because as the only other part of compose.c locking
9182 * it is compose_close - which means once unlocked,
9183 * the compose struct will be freed */
9184 debug_print("couldn't lock mutex, probably sending\n");
9187 val = alertpanel(_("Discard message"),
9188 _("This message has been modified. Discard it?"),
9189 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9190 g_mutex_unlock(compose->mutex);
9192 case G_ALERTDEFAULT:
9193 if (prefs_common.autosave)
9194 compose_remove_draft(compose);
9196 case G_ALERTALTERNATE:
9197 compose_draft(data, COMPOSE_QUIT_EDITING);
9204 compose_close(compose);
9207 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9209 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9210 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9211 Compose *compose = (Compose *) data;
9214 compose->out_encoding = (CharSet)value;
9217 static void compose_address_cb(GtkAction *action, gpointer data)
9219 Compose *compose = (Compose *)data;
9221 addressbook_open(compose);
9224 static void about_show_cb(GtkAction *action, gpointer data)
9229 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9231 Compose *compose = (Compose *)data;
9236 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9237 g_return_if_fail(tmpl != NULL);
9239 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9241 val = alertpanel(_("Apply template"), msg,
9242 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9245 if (val == G_ALERTDEFAULT)
9246 compose_template_apply(compose, tmpl, TRUE);
9247 else if (val == G_ALERTALTERNATE)
9248 compose_template_apply(compose, tmpl, FALSE);
9251 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9253 Compose *compose = (Compose *)data;
9255 compose_exec_ext_editor(compose);
9258 static void compose_undo_cb(GtkAction *action, gpointer data)
9260 Compose *compose = (Compose *)data;
9261 gboolean prev_autowrap = compose->autowrap;
9263 compose->autowrap = FALSE;
9264 undo_undo(compose->undostruct);
9265 compose->autowrap = prev_autowrap;
9268 static void compose_redo_cb(GtkAction *action, gpointer data)
9270 Compose *compose = (Compose *)data;
9271 gboolean prev_autowrap = compose->autowrap;
9273 compose->autowrap = FALSE;
9274 undo_redo(compose->undostruct);
9275 compose->autowrap = prev_autowrap;
9278 static void entry_cut_clipboard(GtkWidget *entry)
9280 if (GTK_IS_EDITABLE(entry))
9281 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9282 else if (GTK_IS_TEXT_VIEW(entry))
9283 gtk_text_buffer_cut_clipboard(
9284 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9285 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9289 static void entry_copy_clipboard(GtkWidget *entry)
9291 if (GTK_IS_EDITABLE(entry))
9292 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9293 else if (GTK_IS_TEXT_VIEW(entry))
9294 gtk_text_buffer_copy_clipboard(
9295 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9296 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9299 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9300 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9302 if (GTK_IS_TEXT_VIEW(entry)) {
9303 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9304 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9305 GtkTextIter start_iter, end_iter;
9307 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9309 if (contents == NULL)
9312 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9314 /* we shouldn't delete the selection when middle-click-pasting, or we
9315 * can't mid-click-paste our own selection */
9316 if (clip != GDK_SELECTION_PRIMARY) {
9317 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9320 if (insert_place == NULL) {
9321 /* if insert_place isn't specified, insert at the cursor.
9322 * used for Ctrl-V pasting */
9323 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9324 start = gtk_text_iter_get_offset(&start_iter);
9325 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9327 /* if insert_place is specified, paste here.
9328 * used for mid-click-pasting */
9329 start = gtk_text_iter_get_offset(insert_place);
9330 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9334 /* paste unwrapped: mark the paste so it's not wrapped later */
9335 end = start + strlen(contents);
9336 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9337 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9338 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9339 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9340 /* rewrap paragraph now (after a mid-click-paste) */
9341 mark_start = gtk_text_buffer_get_insert(buffer);
9342 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9343 gtk_text_iter_backward_char(&start_iter);
9344 compose_beautify_paragraph(compose, &start_iter, TRUE);
9346 } else if (GTK_IS_EDITABLE(entry))
9347 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9349 compose->modified = TRUE;
9352 static void entry_allsel(GtkWidget *entry)
9354 if (GTK_IS_EDITABLE(entry))
9355 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9356 else if (GTK_IS_TEXT_VIEW(entry)) {
9357 GtkTextIter startiter, enditer;
9358 GtkTextBuffer *textbuf;
9360 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9361 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9362 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9364 gtk_text_buffer_move_mark_by_name(textbuf,
9365 "selection_bound", &startiter);
9366 gtk_text_buffer_move_mark_by_name(textbuf,
9367 "insert", &enditer);
9371 static void compose_cut_cb(GtkAction *action, gpointer data)
9373 Compose *compose = (Compose *)data;
9374 if (compose->focused_editable
9375 #ifndef GENERIC_UMPC
9376 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9379 entry_cut_clipboard(compose->focused_editable);
9382 static void compose_copy_cb(GtkAction *action, gpointer data)
9384 Compose *compose = (Compose *)data;
9385 if (compose->focused_editable
9386 #ifndef GENERIC_UMPC
9387 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9390 entry_copy_clipboard(compose->focused_editable);
9393 static void compose_paste_cb(GtkAction *action, gpointer data)
9395 Compose *compose = (Compose *)data;
9397 GtkTextBuffer *buffer;
9399 if (compose->focused_editable &&
9400 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9401 entry_paste_clipboard(compose, compose->focused_editable,
9402 prefs_common.linewrap_pastes,
9403 GDK_SELECTION_CLIPBOARD, NULL);
9407 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9409 Compose *compose = (Compose *)data;
9410 gint wrap_quote = prefs_common.linewrap_quote;
9411 if (compose->focused_editable
9412 #ifndef GENERIC_UMPC
9413 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9416 /* let text_insert() (called directly or at a later time
9417 * after the gtk_editable_paste_clipboard) know that
9418 * text is to be inserted as a quotation. implemented
9419 * by using a simple refcount... */
9420 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9421 G_OBJECT(compose->focused_editable),
9422 "paste_as_quotation"));
9423 g_object_set_data(G_OBJECT(compose->focused_editable),
9424 "paste_as_quotation",
9425 GINT_TO_POINTER(paste_as_quotation + 1));
9426 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9427 entry_paste_clipboard(compose, compose->focused_editable,
9428 prefs_common.linewrap_pastes,
9429 GDK_SELECTION_CLIPBOARD, NULL);
9430 prefs_common.linewrap_quote = wrap_quote;
9434 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9436 Compose *compose = (Compose *)data;
9438 GtkTextBuffer *buffer;
9440 if (compose->focused_editable
9441 #ifndef GENERIC_UMPC
9442 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9445 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9446 GDK_SELECTION_CLIPBOARD, NULL);
9450 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9452 Compose *compose = (Compose *)data;
9454 GtkTextBuffer *buffer;
9456 if (compose->focused_editable
9457 #ifndef GENERIC_UMPC
9458 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9461 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9462 GDK_SELECTION_CLIPBOARD, NULL);
9466 static void compose_allsel_cb(GtkAction *action, gpointer data)
9468 Compose *compose = (Compose *)data;
9469 if (compose->focused_editable
9470 #ifndef GENERIC_UMPC
9471 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9474 entry_allsel(compose->focused_editable);
9477 static void textview_move_beginning_of_line (GtkTextView *text)
9479 GtkTextBuffer *buffer;
9483 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9485 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9486 mark = gtk_text_buffer_get_insert(buffer);
9487 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9488 gtk_text_iter_set_line_offset(&ins, 0);
9489 gtk_text_buffer_place_cursor(buffer, &ins);
9492 static void textview_move_forward_character (GtkTextView *text)
9494 GtkTextBuffer *buffer;
9498 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9500 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9501 mark = gtk_text_buffer_get_insert(buffer);
9502 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9503 if (gtk_text_iter_forward_cursor_position(&ins))
9504 gtk_text_buffer_place_cursor(buffer, &ins);
9507 static void textview_move_backward_character (GtkTextView *text)
9509 GtkTextBuffer *buffer;
9513 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9515 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9516 mark = gtk_text_buffer_get_insert(buffer);
9517 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9518 if (gtk_text_iter_backward_cursor_position(&ins))
9519 gtk_text_buffer_place_cursor(buffer, &ins);
9522 static void textview_move_forward_word (GtkTextView *text)
9524 GtkTextBuffer *buffer;
9529 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9531 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9532 mark = gtk_text_buffer_get_insert(buffer);
9533 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9534 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9535 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9536 gtk_text_iter_backward_word_start(&ins);
9537 gtk_text_buffer_place_cursor(buffer, &ins);
9541 static void textview_move_backward_word (GtkTextView *text)
9543 GtkTextBuffer *buffer;
9548 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9550 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9551 mark = gtk_text_buffer_get_insert(buffer);
9552 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9553 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9554 if (gtk_text_iter_backward_word_starts(&ins, 1))
9555 gtk_text_buffer_place_cursor(buffer, &ins);
9558 static void textview_move_end_of_line (GtkTextView *text)
9560 GtkTextBuffer *buffer;
9564 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9566 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9567 mark = gtk_text_buffer_get_insert(buffer);
9568 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9569 if (gtk_text_iter_forward_to_line_end(&ins))
9570 gtk_text_buffer_place_cursor(buffer, &ins);
9573 static void textview_move_next_line (GtkTextView *text)
9575 GtkTextBuffer *buffer;
9580 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9582 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9583 mark = gtk_text_buffer_get_insert(buffer);
9584 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9585 offset = gtk_text_iter_get_line_offset(&ins);
9586 if (gtk_text_iter_forward_line(&ins)) {
9587 gtk_text_iter_set_line_offset(&ins, offset);
9588 gtk_text_buffer_place_cursor(buffer, &ins);
9592 static void textview_move_previous_line (GtkTextView *text)
9594 GtkTextBuffer *buffer;
9599 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9601 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9602 mark = gtk_text_buffer_get_insert(buffer);
9603 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9604 offset = gtk_text_iter_get_line_offset(&ins);
9605 if (gtk_text_iter_backward_line(&ins)) {
9606 gtk_text_iter_set_line_offset(&ins, offset);
9607 gtk_text_buffer_place_cursor(buffer, &ins);
9611 static void textview_delete_forward_character (GtkTextView *text)
9613 GtkTextBuffer *buffer;
9615 GtkTextIter ins, end_iter;
9617 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9619 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9620 mark = gtk_text_buffer_get_insert(buffer);
9621 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9623 if (gtk_text_iter_forward_char(&end_iter)) {
9624 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9628 static void textview_delete_backward_character (GtkTextView *text)
9630 GtkTextBuffer *buffer;
9632 GtkTextIter ins, end_iter;
9634 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9636 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9637 mark = gtk_text_buffer_get_insert(buffer);
9638 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9640 if (gtk_text_iter_backward_char(&end_iter)) {
9641 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9645 static void textview_delete_forward_word (GtkTextView *text)
9647 GtkTextBuffer *buffer;
9649 GtkTextIter ins, end_iter;
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);
9657 if (gtk_text_iter_forward_word_end(&end_iter)) {
9658 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9662 static void textview_delete_backward_word (GtkTextView *text)
9664 GtkTextBuffer *buffer;
9666 GtkTextIter ins, end_iter;
9668 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9670 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9671 mark = gtk_text_buffer_get_insert(buffer);
9672 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9674 if (gtk_text_iter_backward_word_start(&end_iter)) {
9675 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9679 static void textview_delete_line (GtkTextView *text)
9681 GtkTextBuffer *buffer;
9683 GtkTextIter ins, start_iter, end_iter;
9685 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9687 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9688 mark = gtk_text_buffer_get_insert(buffer);
9689 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9692 gtk_text_iter_set_line_offset(&start_iter, 0);
9695 if (gtk_text_iter_ends_line(&end_iter)){
9696 if (!gtk_text_iter_forward_char(&end_iter))
9697 gtk_text_iter_backward_char(&start_iter);
9700 gtk_text_iter_forward_to_line_end(&end_iter);
9701 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9704 static void textview_delete_to_line_end (GtkTextView *text)
9706 GtkTextBuffer *buffer;
9708 GtkTextIter ins, end_iter;
9710 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9712 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9713 mark = gtk_text_buffer_get_insert(buffer);
9714 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9716 if (gtk_text_iter_ends_line(&end_iter))
9717 gtk_text_iter_forward_char(&end_iter);
9719 gtk_text_iter_forward_to_line_end(&end_iter);
9720 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9723 #define DO_ACTION(name, act) { \
9724 if(!strcmp(name, a_name)) { \
9728 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
9730 const gchar *a_name = gtk_action_get_name(action);
9731 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
9732 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
9733 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
9734 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
9735 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
9736 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
9737 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
9738 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
9739 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
9740 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
9741 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
9742 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
9743 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
9744 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
9748 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
9750 Compose *compose = (Compose *)data;
9751 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9752 ComposeCallAdvancedAction action = -1;
9754 action = compose_call_advanced_action_from_path(gaction);
9757 void (*do_action) (GtkTextView *text);
9758 } action_table[] = {
9759 {textview_move_beginning_of_line},
9760 {textview_move_forward_character},
9761 {textview_move_backward_character},
9762 {textview_move_forward_word},
9763 {textview_move_backward_word},
9764 {textview_move_end_of_line},
9765 {textview_move_next_line},
9766 {textview_move_previous_line},
9767 {textview_delete_forward_character},
9768 {textview_delete_backward_character},
9769 {textview_delete_forward_word},
9770 {textview_delete_backward_word},
9771 {textview_delete_line},
9772 {textview_delete_to_line_end}
9775 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
9777 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
9778 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
9779 if (action_table[action].do_action)
9780 action_table[action].do_action(text);
9782 g_warning("Not implemented yet.");
9786 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
9790 if (GTK_IS_EDITABLE(widget)) {
9791 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
9792 gtk_editable_set_position(GTK_EDITABLE(widget),
9795 if (widget->parent && widget->parent->parent
9796 && widget->parent->parent->parent) {
9797 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
9798 gint y = widget->allocation.y;
9799 gint height = widget->allocation.height;
9800 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
9801 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
9803 if (y < (int)shown->value) {
9804 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
9806 if (y + height > (int)shown->value + (int)shown->page_size) {
9807 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
9808 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9809 y + height - (int)shown->page_size - 1);
9811 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9812 (int)shown->upper - (int)shown->page_size - 1);
9819 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
9820 compose->focused_editable = widget;
9823 if (GTK_IS_TEXT_VIEW(widget)
9824 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
9825 g_object_ref(compose->notebook);
9826 g_object_ref(compose->edit_vbox);
9827 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9828 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9829 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
9830 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
9831 g_object_unref(compose->notebook);
9832 g_object_unref(compose->edit_vbox);
9833 g_signal_handlers_block_by_func(G_OBJECT(widget),
9834 G_CALLBACK(compose_grab_focus_cb),
9836 gtk_widget_grab_focus(widget);
9837 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9838 G_CALLBACK(compose_grab_focus_cb),
9840 } else if (!GTK_IS_TEXT_VIEW(widget)
9841 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
9842 g_object_ref(compose->notebook);
9843 g_object_ref(compose->edit_vbox);
9844 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9845 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9846 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
9847 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
9848 g_object_unref(compose->notebook);
9849 g_object_unref(compose->edit_vbox);
9850 g_signal_handlers_block_by_func(G_OBJECT(widget),
9851 G_CALLBACK(compose_grab_focus_cb),
9853 gtk_widget_grab_focus(widget);
9854 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9855 G_CALLBACK(compose_grab_focus_cb),
9861 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
9863 compose->modified = TRUE;
9864 #ifndef GENERIC_UMPC
9865 compose_set_title(compose);
9869 static void compose_wrap_cb(GtkAction *action, gpointer data)
9871 Compose *compose = (Compose *)data;
9872 compose_beautify_paragraph(compose, NULL, TRUE);
9875 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
9877 Compose *compose = (Compose *)data;
9878 compose_wrap_all_full(compose, TRUE);
9881 static void compose_find_cb(GtkAction *action, gpointer data)
9883 Compose *compose = (Compose *)data;
9885 message_search_compose(compose);
9888 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
9891 Compose *compose = (Compose *)data;
9892 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9893 if (compose->autowrap)
9894 compose_wrap_all_full(compose, TRUE);
9895 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9898 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
9900 Compose *compose = (Compose *)data;
9902 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9905 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
9907 Compose *compose = (Compose *)data;
9909 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9912 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
9914 g_free(compose->privacy_system);
9916 compose->privacy_system = g_strdup(account->default_privacy_system);
9917 compose_update_privacy_system_menu_item(compose, warn);
9920 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
9922 Compose *compose = (Compose *)data;
9924 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
9925 gtk_widget_show(compose->ruler_hbox);
9926 prefs_common.show_ruler = TRUE;
9928 gtk_widget_hide(compose->ruler_hbox);
9929 gtk_widget_queue_resize(compose->edit_vbox);
9930 prefs_common.show_ruler = FALSE;
9934 static void compose_attach_drag_received_cb (GtkWidget *widget,
9935 GdkDragContext *context,
9938 GtkSelectionData *data,
9943 Compose *compose = (Compose *)user_data;
9946 if (gdk_atom_name(data->type) &&
9947 !strcmp(gdk_atom_name(data->type), "text/uri-list")
9948 && gtk_drag_get_source_widget(context) !=
9949 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
9950 list = uri_list_extract_filenames((const gchar *)data->data);
9951 for (tmp = list; tmp != NULL; tmp = tmp->next) {
9952 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
9953 compose_attach_append
9954 (compose, (const gchar *)tmp->data,
9955 utf8_filename, NULL);
9956 g_free(utf8_filename);
9958 if (list) compose_changed_cb(NULL, compose);
9959 list_free_strings(list);
9961 } else if (gtk_drag_get_source_widget(context)
9962 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
9963 /* comes from our summaryview */
9964 SummaryView * summaryview = NULL;
9965 GSList * list = NULL, *cur = NULL;
9967 if (mainwindow_get_mainwindow())
9968 summaryview = mainwindow_get_mainwindow()->summaryview;
9971 list = summary_get_selected_msg_list(summaryview);
9973 for (cur = list; cur; cur = cur->next) {
9974 MsgInfo *msginfo = (MsgInfo *)cur->data;
9977 file = procmsg_get_message_file_full(msginfo,
9980 compose_attach_append(compose, (const gchar *)file,
9981 (const gchar *)file, "message/rfc822");
9989 static gboolean compose_drag_drop(GtkWidget *widget,
9990 GdkDragContext *drag_context,
9992 guint time, gpointer user_data)
9994 /* not handling this signal makes compose_insert_drag_received_cb
9999 static void compose_insert_drag_received_cb (GtkWidget *widget,
10000 GdkDragContext *drag_context,
10003 GtkSelectionData *data,
10006 gpointer user_data)
10008 Compose *compose = (Compose *)user_data;
10011 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10013 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10014 AlertValue val = G_ALERTDEFAULT;
10016 list = uri_list_extract_filenames((const gchar *)data->data);
10018 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10019 /* Assume a list of no files, and data has ://, is a remote link */
10020 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10021 gchar *tmpfile = get_tmp_file();
10022 str_write_to_file(tmpdata, tmpfile);
10024 compose_insert_file(compose, tmpfile);
10025 claws_unlink(tmpfile);
10027 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10028 compose_beautify_paragraph(compose, NULL, TRUE);
10031 switch (prefs_common.compose_dnd_mode) {
10032 case COMPOSE_DND_ASK:
10033 val = alertpanel_full(_("Insert or attach?"),
10034 _("Do you want to insert the contents of the file(s) "
10035 "into the message body, or attach it to the email?"),
10036 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10037 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10039 case COMPOSE_DND_INSERT:
10040 val = G_ALERTALTERNATE;
10042 case COMPOSE_DND_ATTACH:
10043 val = G_ALERTOTHER;
10046 /* unexpected case */
10047 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10050 if (val & G_ALERTDISABLE) {
10051 val &= ~G_ALERTDISABLE;
10052 /* remember what action to perform by default, only if we don't click Cancel */
10053 if (val == G_ALERTALTERNATE)
10054 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10055 else if (val == G_ALERTOTHER)
10056 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10059 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10060 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10061 list_free_strings(list);
10064 } else if (val == G_ALERTOTHER) {
10065 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10066 list_free_strings(list);
10071 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10072 compose_insert_file(compose, (const gchar *)tmp->data);
10074 list_free_strings(list);
10076 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10079 #if GTK_CHECK_VERSION(2, 8, 0)
10080 /* do nothing, handled by GTK */
10082 gchar *tmpfile = get_tmp_file();
10083 str_write_to_file((const gchar *)data->data, tmpfile);
10084 compose_insert_file(compose, tmpfile);
10085 claws_unlink(tmpfile);
10087 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10091 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10094 static void compose_header_drag_received_cb (GtkWidget *widget,
10095 GdkDragContext *drag_context,
10098 GtkSelectionData *data,
10101 gpointer user_data)
10103 GtkEditable *entry = (GtkEditable *)user_data;
10104 gchar *email = (gchar *)data->data;
10106 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10109 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10110 gchar *decoded=g_new(gchar, strlen(email));
10113 email += strlen("mailto:");
10114 decode_uri(decoded, email); /* will fit */
10115 gtk_editable_delete_text(entry, 0, -1);
10116 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10117 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10121 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10124 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10126 Compose *compose = (Compose *)data;
10128 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10129 compose->return_receipt = TRUE;
10131 compose->return_receipt = FALSE;
10134 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10136 Compose *compose = (Compose *)data;
10138 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10139 compose->remove_references = TRUE;
10141 compose->remove_references = FALSE;
10144 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10145 GdkEventKey *event,
10146 ComposeHeaderEntry *headerentry)
10148 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10149 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10150 !(event->state & GDK_MODIFIER_MASK) &&
10151 (event->keyval == GDK_BackSpace) &&
10152 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10153 gtk_container_remove
10154 (GTK_CONTAINER(headerentry->compose->header_table),
10155 headerentry->combo);
10156 gtk_container_remove
10157 (GTK_CONTAINER(headerentry->compose->header_table),
10158 headerentry->entry);
10159 headerentry->compose->header_list =
10160 g_slist_remove(headerentry->compose->header_list,
10162 g_free(headerentry);
10163 } else if (event->keyval == GDK_Tab) {
10164 if (headerentry->compose->header_last == headerentry) {
10165 /* Override default next focus, and give it to subject_entry
10166 * instead of notebook tabs
10168 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10169 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10176 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10177 ComposeHeaderEntry *headerentry)
10179 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10180 compose_create_header_entry(headerentry->compose);
10181 g_signal_handlers_disconnect_matched
10182 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10183 0, 0, NULL, NULL, headerentry);
10185 /* Automatically scroll down */
10186 compose_show_first_last_header(headerentry->compose, FALSE);
10192 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10194 GtkAdjustment *vadj;
10196 g_return_if_fail(compose);
10197 g_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10198 g_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10200 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10201 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10202 gtk_adjustment_changed(vadj);
10205 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10206 const gchar *text, gint len, Compose *compose)
10208 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10209 (G_OBJECT(compose->text), "paste_as_quotation"));
10212 g_return_if_fail(text != NULL);
10214 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10215 G_CALLBACK(text_inserted),
10217 if (paste_as_quotation) {
10219 const gchar *qmark;
10221 GtkTextIter start_iter;
10224 len = strlen(text);
10226 new_text = g_strndup(text, len);
10228 qmark = compose_quote_char_from_context(compose);
10230 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10231 gtk_text_buffer_place_cursor(buffer, iter);
10233 pos = gtk_text_iter_get_offset(iter);
10235 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10236 _("Quote format error at line %d."));
10237 quote_fmt_reset_vartable();
10239 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10240 GINT_TO_POINTER(paste_as_quotation - 1));
10242 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10243 gtk_text_buffer_place_cursor(buffer, iter);
10244 gtk_text_buffer_delete_mark(buffer, mark);
10246 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10247 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10248 compose_beautify_paragraph(compose, &start_iter, FALSE);
10249 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10250 gtk_text_buffer_delete_mark(buffer, mark);
10252 if (strcmp(text, "\n") || compose->automatic_break
10253 || gtk_text_iter_starts_line(iter))
10254 gtk_text_buffer_insert(buffer, iter, text, len);
10256 /* check if the preceding is just whitespace or quote */
10257 GtkTextIter start_line;
10258 gchar *tmp = NULL, *quote = NULL;
10259 gint quote_len = 0, is_normal = 0;
10260 start_line = *iter;
10261 gtk_text_iter_set_line_offset(&start_line, 0);
10262 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10264 if (*tmp == '\0') {
10267 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10275 gtk_text_buffer_insert(buffer, iter, text, len);
10277 gtk_text_buffer_insert_with_tags_by_name(buffer,
10278 iter, text, len, "no_join", NULL);
10283 if (!paste_as_quotation) {
10284 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10285 compose_beautify_paragraph(compose, iter, FALSE);
10286 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10287 gtk_text_buffer_delete_mark(buffer, mark);
10290 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10291 G_CALLBACK(text_inserted),
10293 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10295 if (prefs_common.autosave &&
10296 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10297 compose->draft_timeout_tag != -2 /* disabled while loading */)
10298 compose->draft_timeout_tag = g_timeout_add
10299 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10301 static gint compose_defer_auto_save_draft(Compose *compose)
10303 compose->draft_timeout_tag = -1;
10304 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10309 static void compose_check_all(GtkAction *action, gpointer data)
10311 Compose *compose = (Compose *)data;
10312 if (compose->gtkaspell)
10313 gtkaspell_check_all(compose->gtkaspell);
10316 static void compose_highlight_all(GtkAction *action, gpointer data)
10318 Compose *compose = (Compose *)data;
10319 if (compose->gtkaspell)
10320 gtkaspell_highlight_all(compose->gtkaspell);
10323 static void compose_check_backwards(GtkAction *action, gpointer data)
10325 Compose *compose = (Compose *)data;
10326 if (compose->gtkaspell)
10327 gtkaspell_check_backwards(compose->gtkaspell);
10329 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10333 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10335 Compose *compose = (Compose *)data;
10336 if (compose->gtkaspell)
10337 gtkaspell_check_forwards_go(compose->gtkaspell);
10339 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10345 *\brief Guess originating forward account from MsgInfo and several
10346 * "common preference" settings. Return NULL if no guess.
10348 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10350 PrefsAccount *account = NULL;
10352 g_return_val_if_fail(msginfo, NULL);
10353 g_return_val_if_fail(msginfo->folder, NULL);
10354 g_return_val_if_fail(msginfo->folder->prefs, NULL);
10356 if (msginfo->folder->prefs->enable_default_account)
10357 account = account_find_from_id(msginfo->folder->prefs->default_account);
10360 account = msginfo->folder->folder->account;
10362 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10364 Xstrdup_a(to, msginfo->to, return NULL);
10365 extract_address(to);
10366 account = account_find_from_address(to, FALSE);
10369 if (!account && prefs_common.forward_account_autosel) {
10370 gchar cc[BUFFSIZE];
10371 if (!procheader_get_header_from_msginfo
10372 (msginfo, cc,sizeof cc , "Cc:")) {
10373 gchar *buf = cc + strlen("Cc:");
10374 extract_address(buf);
10375 account = account_find_from_address(buf, FALSE);
10379 if (!account && prefs_common.forward_account_autosel) {
10380 gchar deliveredto[BUFFSIZE];
10381 if (!procheader_get_header_from_msginfo
10382 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10383 gchar *buf = deliveredto + strlen("Delivered-To:");
10384 extract_address(buf);
10385 account = account_find_from_address(buf, FALSE);
10392 gboolean compose_close(Compose *compose)
10396 if (!g_mutex_trylock(compose->mutex)) {
10397 /* we have to wait for the (possibly deferred by auto-save)
10398 * drafting to be done, before destroying the compose under
10400 debug_print("waiting for drafting to finish...\n");
10401 compose_allow_user_actions(compose, FALSE);
10402 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10405 g_return_val_if_fail(compose, FALSE);
10406 gtkut_widget_get_uposition(compose->window, &x, &y);
10407 prefs_common.compose_x = x;
10408 prefs_common.compose_y = y;
10409 g_mutex_unlock(compose->mutex);
10410 compose_destroy(compose);
10415 * Add entry field for each address in list.
10416 * \param compose E-Mail composition object.
10417 * \param listAddress List of (formatted) E-Mail addresses.
10419 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10422 node = listAddress;
10424 addr = ( gchar * ) node->data;
10425 compose_entry_append( compose, addr, COMPOSE_TO );
10426 node = g_list_next( node );
10430 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10431 guint action, gboolean opening_multiple)
10433 gchar *body = NULL;
10434 GSList *new_msglist = NULL;
10435 MsgInfo *tmp_msginfo = NULL;
10436 gboolean originally_enc = FALSE;
10437 Compose *compose = NULL;
10439 g_return_if_fail(msgview != NULL);
10441 g_return_if_fail(msginfo_list != NULL);
10443 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10444 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10445 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10447 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10448 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10449 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10450 orig_msginfo, mimeinfo);
10451 if (tmp_msginfo != NULL) {
10452 new_msglist = g_slist_append(NULL, tmp_msginfo);
10454 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10455 tmp_msginfo->folder = orig_msginfo->folder;
10456 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10457 if (orig_msginfo->tags)
10458 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10463 if (!opening_multiple)
10464 body = messageview_get_selection(msgview);
10467 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10468 procmsg_msginfo_free(tmp_msginfo);
10469 g_slist_free(new_msglist);
10471 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10473 if (compose && originally_enc) {
10474 compose_force_encryption(compose, compose->account, FALSE);
10480 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10483 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
10484 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10485 GSList *cur = msginfo_list;
10486 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10487 "messages. Opening the windows "
10488 "could take some time. Do you "
10489 "want to continue?"),
10490 g_slist_length(msginfo_list));
10491 if (g_slist_length(msginfo_list) > 9
10492 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10493 != G_ALERTALTERNATE) {
10498 /* We'll open multiple compose windows */
10499 /* let the WM place the next windows */
10500 compose_force_window_origin = FALSE;
10501 for (; cur; cur = cur->next) {
10503 tmplist.data = cur->data;
10504 tmplist.next = NULL;
10505 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10507 compose_force_window_origin = TRUE;
10509 /* forwarding multiple mails as attachments is done via a
10510 * single compose window */
10511 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10515 void compose_set_position(Compose *compose, gint pos)
10517 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10519 gtkut_text_view_set_position(text, pos);
10522 gboolean compose_search_string(Compose *compose,
10523 const gchar *str, gboolean case_sens)
10525 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10527 return gtkut_text_view_search_string(text, str, case_sens);
10530 gboolean compose_search_string_backward(Compose *compose,
10531 const gchar *str, gboolean case_sens)
10533 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10535 return gtkut_text_view_search_string_backward(text, str, case_sens);
10538 /* allocate a msginfo structure and populate its data from a compose data structure */
10539 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10541 MsgInfo *newmsginfo;
10543 gchar buf[BUFFSIZE];
10545 g_return_val_if_fail( compose != NULL, NULL );
10547 newmsginfo = procmsg_msginfo_new();
10550 get_rfc822_date(buf, sizeof(buf));
10551 newmsginfo->date = g_strdup(buf);
10554 if (compose->from_name) {
10555 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10556 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10560 if (compose->subject_entry)
10561 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10563 /* to, cc, reply-to, newsgroups */
10564 for (list = compose->header_list; list; list = list->next) {
10565 gchar *header = gtk_editable_get_chars(
10567 GTK_BIN(((ComposeHeaderEntry *)list->data)->combo)->child), 0, -1);
10568 gchar *entry = gtk_editable_get_chars(
10569 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10571 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10572 if ( newmsginfo->to == NULL ) {
10573 newmsginfo->to = g_strdup(entry);
10574 } else if (entry && *entry) {
10575 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10576 g_free(newmsginfo->to);
10577 newmsginfo->to = tmp;
10580 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10581 if ( newmsginfo->cc == NULL ) {
10582 newmsginfo->cc = g_strdup(entry);
10583 } else if (entry && *entry) {
10584 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10585 g_free(newmsginfo->cc);
10586 newmsginfo->cc = tmp;
10589 if ( strcasecmp(header,
10590 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10591 if ( newmsginfo->newsgroups == NULL ) {
10592 newmsginfo->newsgroups = g_strdup(entry);
10593 } else if (entry && *entry) {
10594 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10595 g_free(newmsginfo->newsgroups);
10596 newmsginfo->newsgroups = tmp;
10604 /* other data is unset */
10610 /* update compose's dictionaries from folder dict settings */
10611 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10612 FolderItem *folder_item)
10614 g_return_if_fail(compose != NULL);
10616 if (compose->gtkaspell && folder_item && folder_item->prefs) {
10617 FolderItemPrefs *prefs = folder_item->prefs;
10619 if (prefs->enable_default_dictionary)
10620 gtkaspell_change_dict(compose->gtkaspell,
10621 prefs->default_dictionary, FALSE);
10622 if (folder_item->prefs->enable_default_alt_dictionary)
10623 gtkaspell_change_alt_dict(compose->gtkaspell,
10624 prefs->default_alt_dictionary);
10625 if (prefs->enable_default_dictionary
10626 || prefs->enable_default_alt_dictionary)
10627 compose_spell_menu_changed(compose);