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 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7445 menuitem = GTK_WIDGET(amenu->data);
7447 can_sign = privacy_system_can_sign(systemid);
7448 can_encrypt = privacy_system_can_encrypt(systemid);
7452 } else if (strlen(compose->privacy_system) == 0 &&
7453 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7454 menuitem = GTK_WIDGET(amenu->data);
7457 can_encrypt = FALSE;
7464 if (menuitem != NULL)
7465 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7467 if (warn && !found && strlen(compose->privacy_system)) {
7468 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7469 "will not be able to sign or encrypt this message."),
7470 compose->privacy_system);
7474 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7475 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7478 static void compose_set_out_encoding(Compose *compose)
7480 CharSet out_encoding;
7481 const gchar *branch = NULL;
7482 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7484 switch(out_encoding) {
7485 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7486 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7487 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7488 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7489 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7490 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7491 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7492 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7493 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7494 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7495 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7496 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7497 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7498 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7499 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7500 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7501 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7502 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7503 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7504 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7505 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7506 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7507 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7508 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7509 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7510 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7511 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7512 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7513 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7514 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7515 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7516 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7518 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7521 static void compose_set_template_menu(Compose *compose)
7523 GSList *tmpl_list, *cur;
7527 tmpl_list = template_get_config();
7529 menu = gtk_menu_new();
7531 gtk_menu_set_accel_group (GTK_MENU (menu),
7532 gtk_ui_manager_get_accel_group(compose->ui_manager));
7533 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7534 Template *tmpl = (Template *)cur->data;
7535 gchar *accel_path = NULL;
7536 item = gtk_menu_item_new_with_label(tmpl->name);
7537 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7538 g_signal_connect(G_OBJECT(item), "activate",
7539 G_CALLBACK(compose_template_activate_cb),
7541 g_object_set_data(G_OBJECT(item), "template", tmpl);
7542 gtk_widget_show(item);
7543 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7544 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7548 gtk_widget_show(menu);
7549 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7552 void compose_update_actions_menu(Compose *compose)
7554 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7557 static void compose_update_privacy_systems_menu(Compose *compose)
7559 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7560 GSList *systems, *cur;
7562 GtkWidget *system_none;
7564 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7565 GtkWidget *privacy_menu = gtk_menu_new();
7567 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7568 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7570 g_signal_connect(G_OBJECT(system_none), "activate",
7571 G_CALLBACK(compose_set_privacy_system_cb), compose);
7573 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7574 gtk_widget_show(system_none);
7576 systems = privacy_get_system_ids();
7577 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7578 gchar *systemid = cur->data;
7580 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7581 widget = gtk_radio_menu_item_new_with_label(group,
7582 privacy_system_get_name(systemid));
7583 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7584 g_strdup(systemid), g_free);
7585 g_signal_connect(G_OBJECT(widget), "activate",
7586 G_CALLBACK(compose_set_privacy_system_cb), compose);
7588 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7589 gtk_widget_show(widget);
7592 g_slist_free(systems);
7593 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7594 gtk_widget_show_all(privacy_menu);
7595 gtk_widget_show_all(privacy_menuitem);
7598 void compose_reflect_prefs_all(void)
7603 for (cur = compose_list; cur != NULL; cur = cur->next) {
7604 compose = (Compose *)cur->data;
7605 compose_set_template_menu(compose);
7609 void compose_reflect_prefs_pixmap_theme(void)
7614 for (cur = compose_list; cur != NULL; cur = cur->next) {
7615 compose = (Compose *)cur->data;
7616 toolbar_update(TOOLBAR_COMPOSE, compose);
7620 static const gchar *compose_quote_char_from_context(Compose *compose)
7622 const gchar *qmark = NULL;
7624 g_return_val_if_fail(compose != NULL, NULL);
7626 switch (compose->mode) {
7627 /* use forward-specific quote char */
7628 case COMPOSE_FORWARD:
7629 case COMPOSE_FORWARD_AS_ATTACH:
7630 case COMPOSE_FORWARD_INLINE:
7631 if (compose->folder && compose->folder->prefs &&
7632 compose->folder->prefs->forward_with_format)
7633 qmark = compose->folder->prefs->forward_quotemark;
7634 else if (compose->account->forward_with_format)
7635 qmark = compose->account->forward_quotemark;
7637 qmark = prefs_common.fw_quotemark;
7640 /* use reply-specific quote char in all other modes */
7642 if (compose->folder && compose->folder->prefs &&
7643 compose->folder->prefs->reply_with_format)
7644 qmark = compose->folder->prefs->reply_quotemark;
7645 else if (compose->account->reply_with_format)
7646 qmark = compose->account->reply_quotemark;
7648 qmark = prefs_common.quotemark;
7652 if (qmark == NULL || *qmark == '\0')
7658 static void compose_template_apply(Compose *compose, Template *tmpl,
7662 GtkTextBuffer *buffer;
7666 gchar *parsed_str = NULL;
7667 gint cursor_pos = 0;
7668 const gchar *err_msg = _("Template body format error at line %d.");
7671 /* process the body */
7673 text = GTK_TEXT_VIEW(compose->text);
7674 buffer = gtk_text_view_get_buffer(text);
7677 qmark = compose_quote_char_from_context(compose);
7679 if (compose->replyinfo != NULL) {
7682 gtk_text_buffer_set_text(buffer, "", -1);
7683 mark = gtk_text_buffer_get_insert(buffer);
7684 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7686 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7687 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7689 } else if (compose->fwdinfo != NULL) {
7692 gtk_text_buffer_set_text(buffer, "", -1);
7693 mark = gtk_text_buffer_get_insert(buffer);
7694 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7696 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7697 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7700 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7702 GtkTextIter start, end;
7705 gtk_text_buffer_get_start_iter(buffer, &start);
7706 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7707 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7709 /* clear the buffer now */
7711 gtk_text_buffer_set_text(buffer, "", -1);
7713 parsed_str = compose_quote_fmt(compose, dummyinfo,
7714 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7715 procmsg_msginfo_free( dummyinfo );
7721 gtk_text_buffer_set_text(buffer, "", -1);
7722 mark = gtk_text_buffer_get_insert(buffer);
7723 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7726 if (replace && parsed_str && compose->account->auto_sig)
7727 compose_insert_sig(compose, FALSE);
7729 if (replace && parsed_str) {
7730 gtk_text_buffer_get_start_iter(buffer, &iter);
7731 gtk_text_buffer_place_cursor(buffer, &iter);
7735 cursor_pos = quote_fmt_get_cursor_pos();
7736 compose->set_cursor_pos = cursor_pos;
7737 if (cursor_pos == -1)
7739 gtk_text_buffer_get_start_iter(buffer, &iter);
7740 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7741 gtk_text_buffer_place_cursor(buffer, &iter);
7744 /* process the other fields */
7746 compose_template_apply_fields(compose, tmpl);
7747 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7748 quote_fmt_reset_vartable();
7749 compose_changed_cb(NULL, compose);
7752 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7754 MsgInfo* dummyinfo = NULL;
7755 MsgInfo *msginfo = NULL;
7758 if (compose->replyinfo != NULL)
7759 msginfo = compose->replyinfo;
7760 else if (compose->fwdinfo != NULL)
7761 msginfo = compose->fwdinfo;
7763 dummyinfo = compose_msginfo_new_from_compose(compose);
7764 msginfo = dummyinfo;
7767 if (tmpl->from && *tmpl->from != '\0') {
7769 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7770 compose->gtkaspell);
7772 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7774 quote_fmt_scan_string(tmpl->from);
7777 buf = quote_fmt_get_buffer();
7779 alertpanel_error(_("Template From format error."));
7781 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
7785 if (tmpl->to && *tmpl->to != '\0') {
7787 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7788 compose->gtkaspell);
7790 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7792 quote_fmt_scan_string(tmpl->to);
7795 buf = quote_fmt_get_buffer();
7797 alertpanel_error(_("Template To format error."));
7799 compose_entry_append(compose, buf, COMPOSE_TO);
7803 if (tmpl->cc && *tmpl->cc != '\0') {
7805 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7806 compose->gtkaspell);
7808 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7810 quote_fmt_scan_string(tmpl->cc);
7813 buf = quote_fmt_get_buffer();
7815 alertpanel_error(_("Template Cc format error."));
7817 compose_entry_append(compose, buf, COMPOSE_CC);
7821 if (tmpl->bcc && *tmpl->bcc != '\0') {
7823 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7824 compose->gtkaspell);
7826 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7828 quote_fmt_scan_string(tmpl->bcc);
7831 buf = quote_fmt_get_buffer();
7833 alertpanel_error(_("Template Bcc format error."));
7835 compose_entry_append(compose, buf, COMPOSE_BCC);
7839 /* process the subject */
7840 if (tmpl->subject && *tmpl->subject != '\0') {
7842 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7843 compose->gtkaspell);
7845 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7847 quote_fmt_scan_string(tmpl->subject);
7850 buf = quote_fmt_get_buffer();
7852 alertpanel_error(_("Template subject format error."));
7854 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
7858 procmsg_msginfo_free( dummyinfo );
7861 static void compose_destroy(Compose *compose)
7863 GtkTextBuffer *buffer;
7864 GtkClipboard *clipboard;
7866 compose_list = g_list_remove(compose_list, compose);
7868 if (compose->updating) {
7869 debug_print("danger, not destroying anything now\n");
7870 compose->deferred_destroy = TRUE;
7873 /* NOTE: address_completion_end() does nothing with the window
7874 * however this may change. */
7875 address_completion_end(compose->window);
7877 slist_free_strings(compose->to_list);
7878 g_slist_free(compose->to_list);
7879 slist_free_strings(compose->newsgroup_list);
7880 g_slist_free(compose->newsgroup_list);
7881 slist_free_strings(compose->header_list);
7882 g_slist_free(compose->header_list);
7884 procmsg_msginfo_free(compose->targetinfo);
7885 procmsg_msginfo_free(compose->replyinfo);
7886 procmsg_msginfo_free(compose->fwdinfo);
7888 g_free(compose->replyto);
7889 g_free(compose->cc);
7890 g_free(compose->bcc);
7891 g_free(compose->newsgroups);
7892 g_free(compose->followup_to);
7894 g_free(compose->ml_post);
7896 g_free(compose->inreplyto);
7897 g_free(compose->references);
7898 g_free(compose->msgid);
7899 g_free(compose->boundary);
7901 g_free(compose->redirect_filename);
7902 if (compose->undostruct)
7903 undo_destroy(compose->undostruct);
7905 g_free(compose->sig_str);
7907 g_free(compose->exteditor_file);
7909 g_free(compose->orig_charset);
7911 g_free(compose->privacy_system);
7913 if (addressbook_get_target_compose() == compose)
7914 addressbook_set_target_compose(NULL);
7917 if (compose->gtkaspell) {
7918 gtkaspell_delete(compose->gtkaspell);
7919 compose->gtkaspell = NULL;
7923 prefs_common.compose_width = compose->scrolledwin->allocation.width;
7924 prefs_common.compose_height = compose->window->allocation.height;
7926 if (!gtk_widget_get_parent(compose->paned))
7927 gtk_widget_destroy(compose->paned);
7928 gtk_widget_destroy(compose->popupmenu);
7930 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
7931 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7932 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
7934 gtk_widget_destroy(compose->window);
7935 toolbar_destroy(compose->toolbar);
7936 g_free(compose->toolbar);
7937 g_mutex_free(compose->mutex);
7941 static void compose_attach_info_free(AttachInfo *ainfo)
7943 g_free(ainfo->file);
7944 g_free(ainfo->content_type);
7945 g_free(ainfo->name);
7949 static void compose_attach_update_label(Compose *compose)
7954 GtkTreeModel *model;
7959 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
7960 if(!gtk_tree_model_get_iter_first(model, &iter)) {
7961 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
7965 while(gtk_tree_model_iter_next(model, &iter))
7968 text = g_strdup_printf("(%d)", i);
7969 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
7973 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
7975 Compose *compose = (Compose *)data;
7976 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
7977 GtkTreeSelection *selection;
7979 GtkTreeModel *model;
7981 selection = gtk_tree_view_get_selection(tree_view);
7982 sel = gtk_tree_selection_get_selected_rows(selection, &model);
7987 for (cur = sel; cur != NULL; cur = cur->next) {
7988 GtkTreePath *path = cur->data;
7989 GtkTreeRowReference *ref = gtk_tree_row_reference_new
7992 gtk_tree_path_free(path);
7995 for (cur = sel; cur != NULL; cur = cur->next) {
7996 GtkTreeRowReference *ref = cur->data;
7997 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8000 if (gtk_tree_model_get_iter(model, &iter, path))
8001 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8003 gtk_tree_path_free(path);
8004 gtk_tree_row_reference_free(ref);
8008 compose_attach_update_label(compose);
8011 static struct _AttachProperty
8014 GtkWidget *mimetype_entry;
8015 GtkWidget *encoding_optmenu;
8016 GtkWidget *path_entry;
8017 GtkWidget *filename_entry;
8019 GtkWidget *cancel_btn;
8022 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8024 gtk_tree_path_free((GtkTreePath *)ptr);
8027 static void compose_attach_property(GtkAction *action, gpointer data)
8029 Compose *compose = (Compose *)data;
8030 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8032 GtkComboBox *optmenu;
8033 GtkTreeSelection *selection;
8035 GtkTreeModel *model;
8038 static gboolean cancelled;
8040 /* only if one selected */
8041 selection = gtk_tree_view_get_selection(tree_view);
8042 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8045 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8049 path = (GtkTreePath *) sel->data;
8050 gtk_tree_model_get_iter(model, &iter, path);
8051 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8054 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8060 if (!attach_prop.window)
8061 compose_attach_property_create(&cancelled);
8062 gtk_widget_grab_focus(attach_prop.ok_btn);
8063 gtk_widget_show(attach_prop.window);
8064 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8066 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8067 if (ainfo->encoding == ENC_UNKNOWN)
8068 combobox_select_by_data(optmenu, ENC_BASE64);
8070 combobox_select_by_data(optmenu, ainfo->encoding);
8072 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8073 ainfo->content_type ? ainfo->content_type : "");
8074 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8075 ainfo->file ? ainfo->file : "");
8076 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8077 ainfo->name ? ainfo->name : "");
8080 const gchar *entry_text;
8082 gchar *cnttype = NULL;
8089 gtk_widget_hide(attach_prop.window);
8094 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8095 if (*entry_text != '\0') {
8098 text = g_strstrip(g_strdup(entry_text));
8099 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8100 cnttype = g_strdup(text);
8103 alertpanel_error(_("Invalid MIME type."));
8109 ainfo->encoding = combobox_get_active_data(optmenu);
8111 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8112 if (*entry_text != '\0') {
8113 if (is_file_exist(entry_text) &&
8114 (size = get_file_size(entry_text)) > 0)
8115 file = g_strdup(entry_text);
8118 (_("File doesn't exist or is empty."));
8124 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8125 if (*entry_text != '\0') {
8126 g_free(ainfo->name);
8127 ainfo->name = g_strdup(entry_text);
8131 g_free(ainfo->content_type);
8132 ainfo->content_type = cnttype;
8135 g_free(ainfo->file);
8139 ainfo->size = (goffset)size;
8141 /* update tree store */
8142 text = to_human_readable(ainfo->size);
8143 gtk_tree_model_get_iter(model, &iter, path);
8144 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8145 COL_MIMETYPE, ainfo->content_type,
8147 COL_NAME, ainfo->name,
8153 gtk_tree_path_free(path);
8156 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8158 label = gtk_label_new(str); \
8159 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8160 GTK_FILL, 0, 0, 0); \
8161 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8163 entry = gtk_entry_new(); \
8164 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8165 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8168 static void compose_attach_property_create(gboolean *cancelled)
8174 GtkWidget *mimetype_entry;
8177 GtkListStore *optmenu_menu;
8178 GtkWidget *path_entry;
8179 GtkWidget *filename_entry;
8182 GtkWidget *cancel_btn;
8183 GList *mime_type_list, *strlist;
8186 debug_print("Creating attach_property window...\n");
8188 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8189 gtk_widget_set_size_request(window, 480, -1);
8190 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8191 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8192 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8193 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8194 g_signal_connect(G_OBJECT(window), "delete_event",
8195 G_CALLBACK(attach_property_delete_event),
8197 g_signal_connect(G_OBJECT(window), "key_press_event",
8198 G_CALLBACK(attach_property_key_pressed),
8201 vbox = gtk_vbox_new(FALSE, 8);
8202 gtk_container_add(GTK_CONTAINER(window), vbox);
8204 table = gtk_table_new(4, 2, FALSE);
8205 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8206 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8207 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8209 label = gtk_label_new(_("MIME type"));
8210 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8212 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8213 mimetype_entry = gtk_combo_box_entry_new_text();
8214 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8215 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8217 /* stuff with list */
8218 mime_type_list = procmime_get_mime_type_list();
8220 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8221 MimeType *type = (MimeType *) mime_type_list->data;
8224 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8226 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8229 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8230 (GCompareFunc)strcmp2);
8233 for (mime_type_list = strlist; mime_type_list != NULL;
8234 mime_type_list = mime_type_list->next) {
8235 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8236 g_free(mime_type_list->data);
8238 g_list_free(strlist);
8239 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8240 mimetype_entry = GTK_BIN(mimetype_entry)->child;
8242 label = gtk_label_new(_("Encoding"));
8243 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8245 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8247 hbox = gtk_hbox_new(FALSE, 0);
8248 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8249 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8251 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8252 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8254 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8255 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8256 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8257 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8258 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8260 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8262 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8263 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8265 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8266 &ok_btn, GTK_STOCK_OK,
8268 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8269 gtk_widget_grab_default(ok_btn);
8271 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8272 G_CALLBACK(attach_property_ok),
8274 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8275 G_CALLBACK(attach_property_cancel),
8278 gtk_widget_show_all(vbox);
8280 attach_prop.window = window;
8281 attach_prop.mimetype_entry = mimetype_entry;
8282 attach_prop.encoding_optmenu = optmenu;
8283 attach_prop.path_entry = path_entry;
8284 attach_prop.filename_entry = filename_entry;
8285 attach_prop.ok_btn = ok_btn;
8286 attach_prop.cancel_btn = cancel_btn;
8289 #undef SET_LABEL_AND_ENTRY
8291 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8297 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8303 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8304 gboolean *cancelled)
8312 static gboolean attach_property_key_pressed(GtkWidget *widget,
8314 gboolean *cancelled)
8316 if (event && event->keyval == GDK_Escape) {
8323 static void compose_exec_ext_editor(Compose *compose)
8330 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8331 G_DIR_SEPARATOR, compose);
8333 if (pipe(pipe_fds) < 0) {
8339 if ((pid = fork()) < 0) {
8346 /* close the write side of the pipe */
8349 compose->exteditor_file = g_strdup(tmp);
8350 compose->exteditor_pid = pid;
8352 compose_set_ext_editor_sensitive(compose, FALSE);
8354 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8355 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8359 } else { /* process-monitoring process */
8365 /* close the read side of the pipe */
8368 if (compose_write_body_to_file(compose, tmp) < 0) {
8369 fd_write_all(pipe_fds[1], "2\n", 2);
8373 pid_ed = compose_exec_ext_editor_real(tmp);
8375 fd_write_all(pipe_fds[1], "1\n", 2);
8379 /* wait until editor is terminated */
8380 waitpid(pid_ed, NULL, 0);
8382 fd_write_all(pipe_fds[1], "0\n", 2);
8389 #endif /* G_OS_UNIX */
8393 static gint compose_exec_ext_editor_real(const gchar *file)
8400 g_return_val_if_fail(file != NULL, -1);
8402 if ((pid = fork()) < 0) {
8407 if (pid != 0) return pid;
8409 /* grandchild process */
8411 if (setpgid(0, getppid()))
8414 if (prefs_common_get_ext_editor_cmd() &&
8415 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8416 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8417 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8419 if (prefs_common_get_ext_editor_cmd())
8420 g_warning("External editor command line is invalid: '%s'\n",
8421 prefs_common_get_ext_editor_cmd());
8422 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8425 cmdline = strsplit_with_quote(buf, " ", 1024);
8426 execvp(cmdline[0], cmdline);
8429 g_strfreev(cmdline);
8434 static gboolean compose_ext_editor_kill(Compose *compose)
8436 pid_t pgid = compose->exteditor_pid * -1;
8439 ret = kill(pgid, 0);
8441 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8445 msg = g_strdup_printf
8446 (_("The external editor is still working.\n"
8447 "Force terminating the process?\n"
8448 "process group id: %d"), -pgid);
8449 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8450 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8454 if (val == G_ALERTALTERNATE) {
8455 g_source_remove(compose->exteditor_tag);
8456 g_io_channel_shutdown(compose->exteditor_ch,
8458 g_io_channel_unref(compose->exteditor_ch);
8460 if (kill(pgid, SIGTERM) < 0) perror("kill");
8461 waitpid(compose->exteditor_pid, NULL, 0);
8463 g_warning("Terminated process group id: %d", -pgid);
8464 g_warning("Temporary file: %s",
8465 compose->exteditor_file);
8467 compose_set_ext_editor_sensitive(compose, TRUE);
8469 g_free(compose->exteditor_file);
8470 compose->exteditor_file = NULL;
8471 compose->exteditor_pid = -1;
8472 compose->exteditor_ch = NULL;
8473 compose->exteditor_tag = -1;
8481 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8485 Compose *compose = (Compose *)data;
8488 debug_print(_("Compose: input from monitoring process\n"));
8490 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8492 g_io_channel_shutdown(source, FALSE, NULL);
8493 g_io_channel_unref(source);
8495 waitpid(compose->exteditor_pid, NULL, 0);
8497 if (buf[0] == '0') { /* success */
8498 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8499 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8501 gtk_text_buffer_set_text(buffer, "", -1);
8502 compose_insert_file(compose, compose->exteditor_file);
8503 compose_changed_cb(NULL, compose);
8505 if (claws_unlink(compose->exteditor_file) < 0)
8506 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8507 } else if (buf[0] == '1') { /* failed */
8508 g_warning("Couldn't exec external editor\n");
8509 if (claws_unlink(compose->exteditor_file) < 0)
8510 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8511 } else if (buf[0] == '2') {
8512 g_warning("Couldn't write to file\n");
8513 } else if (buf[0] == '3') {
8514 g_warning("Pipe read failed\n");
8517 compose_set_ext_editor_sensitive(compose, TRUE);
8519 g_free(compose->exteditor_file);
8520 compose->exteditor_file = NULL;
8521 compose->exteditor_pid = -1;
8522 compose->exteditor_ch = NULL;
8523 compose->exteditor_tag = -1;
8528 static void compose_set_ext_editor_sensitive(Compose *compose,
8531 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8532 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8533 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8534 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8535 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8536 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8537 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8539 gtk_widget_set_sensitive(compose->text, sensitive);
8540 if (compose->toolbar->send_btn)
8541 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8542 if (compose->toolbar->sendl_btn)
8543 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8544 if (compose->toolbar->draft_btn)
8545 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8546 if (compose->toolbar->insert_btn)
8547 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8548 if (compose->toolbar->sig_btn)
8549 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8550 if (compose->toolbar->exteditor_btn)
8551 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8552 if (compose->toolbar->linewrap_current_btn)
8553 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8554 if (compose->toolbar->linewrap_all_btn)
8555 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8557 #endif /* G_OS_UNIX */
8560 * compose_undo_state_changed:
8562 * Change the sensivity of the menuentries undo and redo
8564 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8565 gint redo_state, gpointer data)
8567 Compose *compose = (Compose *)data;
8569 switch (undo_state) {
8570 case UNDO_STATE_TRUE:
8571 if (!undostruct->undo_state) {
8572 undostruct->undo_state = TRUE;
8573 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8576 case UNDO_STATE_FALSE:
8577 if (undostruct->undo_state) {
8578 undostruct->undo_state = FALSE;
8579 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8582 case UNDO_STATE_UNCHANGED:
8584 case UNDO_STATE_REFRESH:
8585 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8588 g_warning("Undo state not recognized");
8592 switch (redo_state) {
8593 case UNDO_STATE_TRUE:
8594 if (!undostruct->redo_state) {
8595 undostruct->redo_state = TRUE;
8596 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8599 case UNDO_STATE_FALSE:
8600 if (undostruct->redo_state) {
8601 undostruct->redo_state = FALSE;
8602 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8605 case UNDO_STATE_UNCHANGED:
8607 case UNDO_STATE_REFRESH:
8608 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8611 g_warning("Redo state not recognized");
8616 /* callback functions */
8618 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8619 * includes "non-client" (windows-izm) in calculation, so this calculation
8620 * may not be accurate.
8622 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8623 GtkAllocation *allocation,
8624 GtkSHRuler *shruler)
8626 if (prefs_common.show_ruler) {
8627 gint char_width = 0, char_height = 0;
8628 gint line_width_in_chars;
8630 gtkut_get_font_size(GTK_WIDGET(widget),
8631 &char_width, &char_height);
8632 line_width_in_chars =
8633 (allocation->width - allocation->x) / char_width;
8635 /* got the maximum */
8636 gtk_ruler_set_range(GTK_RULER(shruler),
8637 0.0, line_width_in_chars, 0,
8638 /*line_width_in_chars*/ char_width);
8644 static void account_activated(GtkComboBox *optmenu, gpointer data)
8646 Compose *compose = (Compose *)data;
8649 gchar *folderidentifier;
8650 gint account_id = 0;
8654 /* Get ID of active account in the combo box */
8655 menu = gtk_combo_box_get_model(optmenu);
8656 gtk_combo_box_get_active_iter(optmenu, &iter);
8657 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8659 ac = account_find_from_id(account_id);
8660 g_return_if_fail(ac != NULL);
8662 if (ac != compose->account)
8663 compose_select_account(compose, ac, FALSE);
8665 /* Set message save folder */
8666 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8667 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8669 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8670 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8672 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8673 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8674 folderidentifier = folder_item_get_identifier(account_get_special_folder
8675 (compose->account, F_OUTBOX));
8676 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
8677 g_free(folderidentifier);
8681 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8682 GtkTreeViewColumn *column, Compose *compose)
8684 compose_attach_property(NULL, compose);
8687 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8690 Compose *compose = (Compose *)data;
8691 GtkTreeSelection *attach_selection;
8692 gint attach_nr_selected;
8694 if (!event) return FALSE;
8696 if (event->button == 3) {
8697 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8698 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8700 if (attach_nr_selected > 0)
8702 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8703 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8705 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8706 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8709 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8710 NULL, NULL, event->button, event->time);
8717 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8720 Compose *compose = (Compose *)data;
8722 if (!event) return FALSE;
8724 switch (event->keyval) {
8726 compose_attach_remove_selected(NULL, compose);
8732 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8734 toolbar_comp_set_sensitive(compose, allow);
8735 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8736 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8738 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8740 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8741 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8742 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8744 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8748 static void compose_send_cb(GtkAction *action, gpointer data)
8750 Compose *compose = (Compose *)data;
8752 if (prefs_common.work_offline &&
8753 !inc_offline_should_override(TRUE,
8754 _("Claws Mail needs network access in order "
8755 "to send this email.")))
8758 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8759 g_source_remove(compose->draft_timeout_tag);
8760 compose->draft_timeout_tag = -1;
8763 compose_send(compose);
8766 static void compose_send_later_cb(GtkAction *action, gpointer data)
8768 Compose *compose = (Compose *)data;
8772 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8776 compose_close(compose);
8777 } else if (val == -1) {
8778 alertpanel_error(_("Could not queue message."));
8779 } else if (val == -2) {
8780 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8781 } else if (val == -3) {
8782 if (privacy_peek_error())
8783 alertpanel_error(_("Could not queue message for sending:\n\n"
8784 "Signature failed: %s"), privacy_get_error());
8785 } else if (val == -4) {
8786 alertpanel_error(_("Could not queue message for sending:\n\n"
8787 "Charset conversion failed."));
8788 } else if (val == -5) {
8789 alertpanel_error(_("Could not queue message for sending:\n\n"
8790 "Couldn't get recipient encryption key."));
8791 } else if (val == -6) {
8794 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
8797 #define DRAFTED_AT_EXIT "drafted_at_exit"
8798 static void compose_register_draft(MsgInfo *info)
8800 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8801 DRAFTED_AT_EXIT, NULL);
8802 FILE *fp = fopen(filepath, "ab");
8805 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
8813 gboolean compose_draft (gpointer data, guint action)
8815 Compose *compose = (Compose *)data;
8819 MsgFlags flag = {0, 0};
8820 static gboolean lock = FALSE;
8821 MsgInfo *newmsginfo;
8823 gboolean target_locked = FALSE;
8824 gboolean err = FALSE;
8826 if (lock) return FALSE;
8828 if (compose->sending)
8831 draft = account_get_special_folder(compose->account, F_DRAFT);
8832 g_return_val_if_fail(draft != NULL, FALSE);
8834 if (!g_mutex_trylock(compose->mutex)) {
8835 /* we don't want to lock the mutex once it's available,
8836 * because as the only other part of compose.c locking
8837 * it is compose_close - which means once unlocked,
8838 * the compose struct will be freed */
8839 debug_print("couldn't lock mutex, probably sending\n");
8845 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
8846 G_DIR_SEPARATOR, compose);
8847 if ((fp = g_fopen(tmp, "wb")) == NULL) {
8848 FILE_OP_ERROR(tmp, "fopen");
8852 /* chmod for security */
8853 if (change_file_mode_rw(fp, tmp) < 0) {
8854 FILE_OP_ERROR(tmp, "chmod");
8855 g_warning("can't change file mode\n");
8858 /* Save draft infos */
8859 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
8860 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
8862 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
8863 gchar *savefolderid;
8865 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8866 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
8867 g_free(savefolderid);
8869 if (compose->return_receipt) {
8870 err |= (fprintf(fp, "RRCPT:1\n") < 0);
8872 if (compose->privacy_system) {
8873 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
8874 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
8875 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
8878 /* Message-ID of message replying to */
8879 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
8882 folderid = folder_item_get_identifier(compose->replyinfo->folder);
8883 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
8886 /* Message-ID of message forwarding to */
8887 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
8890 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
8891 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
8895 /* end of headers */
8896 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
8903 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
8907 if (fclose(fp) == EOF) {
8911 if (compose->targetinfo) {
8912 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
8913 flag.perm_flags = target_locked?MSG_LOCKED:0;
8915 flag.tmp_flags = MSG_DRAFT;
8917 folder_item_scan(draft);
8918 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
8919 MsgInfo *tmpinfo = NULL;
8920 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
8921 if (compose->msgid) {
8922 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
8925 msgnum = tmpinfo->msgnum;
8926 procmsg_msginfo_free(tmpinfo);
8927 debug_print("got draft msgnum %d from scanning\n", msgnum);
8929 debug_print("didn't get draft msgnum after scanning\n");
8932 debug_print("got draft msgnum %d from adding\n", msgnum);
8938 if (action != COMPOSE_AUTO_SAVE) {
8939 if (action != COMPOSE_DRAFT_FOR_EXIT)
8940 alertpanel_error(_("Could not save draft."));
8943 gtkut_window_popup(compose->window);
8944 val = alertpanel_full(_("Could not save draft"),
8945 _("Could not save draft.\n"
8946 "Do you want to cancel exit or discard this email?"),
8947 _("_Cancel exit"), _("_Discard email"), NULL,
8948 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
8949 if (val == G_ALERTALTERNATE) {
8951 g_mutex_unlock(compose->mutex); /* must be done before closing */
8952 compose_close(compose);
8956 g_mutex_unlock(compose->mutex); /* must be done before closing */
8965 if (compose->mode == COMPOSE_REEDIT) {
8966 compose_remove_reedit_target(compose, TRUE);
8969 newmsginfo = folder_item_get_msginfo(draft, msgnum);
8972 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
8974 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
8976 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
8977 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
8978 procmsg_msginfo_set_flags(newmsginfo, 0,
8979 MSG_HAS_ATTACHMENT);
8981 if (action == COMPOSE_DRAFT_FOR_EXIT) {
8982 compose_register_draft(newmsginfo);
8984 procmsg_msginfo_free(newmsginfo);
8987 folder_item_scan(draft);
8989 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
8991 g_mutex_unlock(compose->mutex); /* must be done before closing */
8992 compose_close(compose);
8998 path = folder_item_fetch_msg(draft, msgnum);
9000 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9003 if (g_stat(path, &s) < 0) {
9004 FILE_OP_ERROR(path, "stat");
9010 procmsg_msginfo_free(compose->targetinfo);
9011 compose->targetinfo = procmsg_msginfo_new();
9012 compose->targetinfo->msgnum = msgnum;
9013 compose->targetinfo->size = (goffset)s.st_size;
9014 compose->targetinfo->mtime = s.st_mtime;
9015 compose->targetinfo->folder = draft;
9017 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9018 compose->mode = COMPOSE_REEDIT;
9020 if (action == COMPOSE_AUTO_SAVE) {
9021 compose->autosaved_draft = compose->targetinfo;
9023 compose->modified = FALSE;
9024 compose_set_title(compose);
9028 g_mutex_unlock(compose->mutex);
9032 void compose_clear_exit_drafts(void)
9034 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9035 DRAFTED_AT_EXIT, NULL);
9036 if (is_file_exist(filepath))
9037 claws_unlink(filepath);
9042 void compose_reopen_exit_drafts(void)
9044 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9045 DRAFTED_AT_EXIT, NULL);
9046 FILE *fp = fopen(filepath, "rb");
9050 while (fgets(buf, sizeof(buf), fp)) {
9051 gchar **parts = g_strsplit(buf, "\t", 2);
9052 const gchar *folder = parts[0];
9053 int msgnum = parts[1] ? atoi(parts[1]):-1;
9055 if (folder && *folder && msgnum > -1) {
9056 FolderItem *item = folder_find_item_from_identifier(folder);
9057 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9059 compose_reedit(info, FALSE);
9066 compose_clear_exit_drafts();
9069 static void compose_save_cb(GtkAction *action, gpointer data)
9071 compose_draft(data, COMPOSE_KEEP_EDITING);
9074 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9076 if (compose && file_list) {
9079 for ( tmp = file_list; tmp; tmp = tmp->next) {
9080 gchar *file = (gchar *) tmp->data;
9081 gchar *utf8_filename = conv_filename_to_utf8(file);
9082 compose_attach_append(compose, file, utf8_filename, NULL);
9083 compose_changed_cb(NULL, compose);
9088 g_free(utf8_filename);
9093 static void compose_attach_cb(GtkAction *action, gpointer data)
9095 Compose *compose = (Compose *)data;
9098 if (compose->redirect_filename != NULL)
9101 file_list = filesel_select_multiple_files_open(_("Select file"));
9104 compose_attach_from_list(compose, file_list, TRUE);
9105 g_list_free(file_list);
9109 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9111 Compose *compose = (Compose *)data;
9114 file_list = filesel_select_multiple_files_open(_("Select file"));
9119 for ( tmp = file_list; tmp; tmp = tmp->next) {
9120 gchar *file = (gchar *) tmp->data;
9121 gchar *filedup = g_strdup(file);
9122 gchar *shortfile = g_path_get_basename(filedup);
9123 ComposeInsertResult res;
9125 res = compose_insert_file(compose, file);
9126 if (res == COMPOSE_INSERT_READ_ERROR) {
9127 alertpanel_error(_("File '%s' could not be read."), shortfile);
9128 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9129 alertpanel_error(_("File '%s' contained invalid characters\n"
9130 "for the current encoding, insertion may be incorrect."), shortfile);
9136 g_list_free(file_list);
9140 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9142 Compose *compose = (Compose *)data;
9144 compose_insert_sig(compose, FALSE);
9147 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9151 Compose *compose = (Compose *)data;
9153 gtkut_widget_get_uposition(widget, &x, &y);
9154 prefs_common.compose_x = x;
9155 prefs_common.compose_y = y;
9157 if (compose->sending || compose->updating)
9159 compose_close_cb(NULL, compose);
9163 void compose_close_toolbar(Compose *compose)
9165 compose_close_cb(NULL, compose);
9168 static void compose_close_cb(GtkAction *action, gpointer data)
9170 Compose *compose = (Compose *)data;
9174 if (compose->exteditor_tag != -1) {
9175 if (!compose_ext_editor_kill(compose))
9180 if (compose->modified) {
9181 if (!g_mutex_trylock(compose->mutex)) {
9182 /* we don't want to lock the mutex once it's available,
9183 * because as the only other part of compose.c locking
9184 * it is compose_close - which means once unlocked,
9185 * the compose struct will be freed */
9186 debug_print("couldn't lock mutex, probably sending\n");
9189 val = alertpanel(_("Discard message"),
9190 _("This message has been modified. Discard it?"),
9191 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9192 g_mutex_unlock(compose->mutex);
9194 case G_ALERTDEFAULT:
9195 if (prefs_common.autosave)
9196 compose_remove_draft(compose);
9198 case G_ALERTALTERNATE:
9199 compose_draft(data, COMPOSE_QUIT_EDITING);
9206 compose_close(compose);
9209 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9211 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9212 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9213 Compose *compose = (Compose *) data;
9216 compose->out_encoding = (CharSet)value;
9219 static void compose_address_cb(GtkAction *action, gpointer data)
9221 Compose *compose = (Compose *)data;
9223 addressbook_open(compose);
9226 static void about_show_cb(GtkAction *action, gpointer data)
9231 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9233 Compose *compose = (Compose *)data;
9238 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9239 g_return_if_fail(tmpl != NULL);
9241 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9243 val = alertpanel(_("Apply template"), msg,
9244 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9247 if (val == G_ALERTDEFAULT)
9248 compose_template_apply(compose, tmpl, TRUE);
9249 else if (val == G_ALERTALTERNATE)
9250 compose_template_apply(compose, tmpl, FALSE);
9253 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9255 Compose *compose = (Compose *)data;
9257 compose_exec_ext_editor(compose);
9260 static void compose_undo_cb(GtkAction *action, gpointer data)
9262 Compose *compose = (Compose *)data;
9263 gboolean prev_autowrap = compose->autowrap;
9265 compose->autowrap = FALSE;
9266 undo_undo(compose->undostruct);
9267 compose->autowrap = prev_autowrap;
9270 static void compose_redo_cb(GtkAction *action, gpointer data)
9272 Compose *compose = (Compose *)data;
9273 gboolean prev_autowrap = compose->autowrap;
9275 compose->autowrap = FALSE;
9276 undo_redo(compose->undostruct);
9277 compose->autowrap = prev_autowrap;
9280 static void entry_cut_clipboard(GtkWidget *entry)
9282 if (GTK_IS_EDITABLE(entry))
9283 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9284 else if (GTK_IS_TEXT_VIEW(entry))
9285 gtk_text_buffer_cut_clipboard(
9286 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9287 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9291 static void entry_copy_clipboard(GtkWidget *entry)
9293 if (GTK_IS_EDITABLE(entry))
9294 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9295 else if (GTK_IS_TEXT_VIEW(entry))
9296 gtk_text_buffer_copy_clipboard(
9297 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9298 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9301 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9302 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9304 if (GTK_IS_TEXT_VIEW(entry)) {
9305 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9306 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9307 GtkTextIter start_iter, end_iter;
9309 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9311 if (contents == NULL)
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 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9318 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9321 if (insert_place == NULL) {
9322 /* if insert_place isn't specified, insert at the cursor.
9323 * used for Ctrl-V pasting */
9324 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9325 start = gtk_text_iter_get_offset(&start_iter);
9326 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9328 /* if insert_place is specified, paste here.
9329 * used for mid-click-pasting */
9330 start = gtk_text_iter_get_offset(insert_place);
9331 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9335 /* paste unwrapped: mark the paste so it's not wrapped later */
9336 end = start + strlen(contents);
9337 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9338 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9339 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9340 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9341 /* rewrap paragraph now (after a mid-click-paste) */
9342 mark_start = gtk_text_buffer_get_insert(buffer);
9343 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9344 gtk_text_iter_backward_char(&start_iter);
9345 compose_beautify_paragraph(compose, &start_iter, TRUE);
9347 } else if (GTK_IS_EDITABLE(entry))
9348 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9350 compose->modified = TRUE;
9353 static void entry_allsel(GtkWidget *entry)
9355 if (GTK_IS_EDITABLE(entry))
9356 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9357 else if (GTK_IS_TEXT_VIEW(entry)) {
9358 GtkTextIter startiter, enditer;
9359 GtkTextBuffer *textbuf;
9361 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9362 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9363 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9365 gtk_text_buffer_move_mark_by_name(textbuf,
9366 "selection_bound", &startiter);
9367 gtk_text_buffer_move_mark_by_name(textbuf,
9368 "insert", &enditer);
9372 static void compose_cut_cb(GtkAction *action, gpointer data)
9374 Compose *compose = (Compose *)data;
9375 if (compose->focused_editable
9376 #ifndef GENERIC_UMPC
9377 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9380 entry_cut_clipboard(compose->focused_editable);
9383 static void compose_copy_cb(GtkAction *action, gpointer data)
9385 Compose *compose = (Compose *)data;
9386 if (compose->focused_editable
9387 #ifndef GENERIC_UMPC
9388 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9391 entry_copy_clipboard(compose->focused_editable);
9394 static void compose_paste_cb(GtkAction *action, gpointer data)
9396 Compose *compose = (Compose *)data;
9398 GtkTextBuffer *buffer;
9400 if (compose->focused_editable &&
9401 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9402 entry_paste_clipboard(compose, compose->focused_editable,
9403 prefs_common.linewrap_pastes,
9404 GDK_SELECTION_CLIPBOARD, NULL);
9408 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9410 Compose *compose = (Compose *)data;
9411 gint wrap_quote = prefs_common.linewrap_quote;
9412 if (compose->focused_editable
9413 #ifndef GENERIC_UMPC
9414 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9417 /* let text_insert() (called directly or at a later time
9418 * after the gtk_editable_paste_clipboard) know that
9419 * text is to be inserted as a quotation. implemented
9420 * by using a simple refcount... */
9421 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9422 G_OBJECT(compose->focused_editable),
9423 "paste_as_quotation"));
9424 g_object_set_data(G_OBJECT(compose->focused_editable),
9425 "paste_as_quotation",
9426 GINT_TO_POINTER(paste_as_quotation + 1));
9427 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9428 entry_paste_clipboard(compose, compose->focused_editable,
9429 prefs_common.linewrap_pastes,
9430 GDK_SELECTION_CLIPBOARD, NULL);
9431 prefs_common.linewrap_quote = wrap_quote;
9435 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9437 Compose *compose = (Compose *)data;
9439 GtkTextBuffer *buffer;
9441 if (compose->focused_editable
9442 #ifndef GENERIC_UMPC
9443 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9446 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9447 GDK_SELECTION_CLIPBOARD, NULL);
9451 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9453 Compose *compose = (Compose *)data;
9455 GtkTextBuffer *buffer;
9457 if (compose->focused_editable
9458 #ifndef GENERIC_UMPC
9459 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9462 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9463 GDK_SELECTION_CLIPBOARD, NULL);
9467 static void compose_allsel_cb(GtkAction *action, gpointer data)
9469 Compose *compose = (Compose *)data;
9470 if (compose->focused_editable
9471 #ifndef GENERIC_UMPC
9472 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9475 entry_allsel(compose->focused_editable);
9478 static void textview_move_beginning_of_line (GtkTextView *text)
9480 GtkTextBuffer *buffer;
9484 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9486 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9487 mark = gtk_text_buffer_get_insert(buffer);
9488 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9489 gtk_text_iter_set_line_offset(&ins, 0);
9490 gtk_text_buffer_place_cursor(buffer, &ins);
9493 static void textview_move_forward_character (GtkTextView *text)
9495 GtkTextBuffer *buffer;
9499 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9501 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9502 mark = gtk_text_buffer_get_insert(buffer);
9503 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9504 if (gtk_text_iter_forward_cursor_position(&ins))
9505 gtk_text_buffer_place_cursor(buffer, &ins);
9508 static void textview_move_backward_character (GtkTextView *text)
9510 GtkTextBuffer *buffer;
9514 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9516 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9517 mark = gtk_text_buffer_get_insert(buffer);
9518 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9519 if (gtk_text_iter_backward_cursor_position(&ins))
9520 gtk_text_buffer_place_cursor(buffer, &ins);
9523 static void textview_move_forward_word (GtkTextView *text)
9525 GtkTextBuffer *buffer;
9530 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9532 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9533 mark = gtk_text_buffer_get_insert(buffer);
9534 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9535 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9536 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9537 gtk_text_iter_backward_word_start(&ins);
9538 gtk_text_buffer_place_cursor(buffer, &ins);
9542 static void textview_move_backward_word (GtkTextView *text)
9544 GtkTextBuffer *buffer;
9549 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9551 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9552 mark = gtk_text_buffer_get_insert(buffer);
9553 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9554 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9555 if (gtk_text_iter_backward_word_starts(&ins, 1))
9556 gtk_text_buffer_place_cursor(buffer, &ins);
9559 static void textview_move_end_of_line (GtkTextView *text)
9561 GtkTextBuffer *buffer;
9565 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9567 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9568 mark = gtk_text_buffer_get_insert(buffer);
9569 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9570 if (gtk_text_iter_forward_to_line_end(&ins))
9571 gtk_text_buffer_place_cursor(buffer, &ins);
9574 static void textview_move_next_line (GtkTextView *text)
9576 GtkTextBuffer *buffer;
9581 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9583 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9584 mark = gtk_text_buffer_get_insert(buffer);
9585 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9586 offset = gtk_text_iter_get_line_offset(&ins);
9587 if (gtk_text_iter_forward_line(&ins)) {
9588 gtk_text_iter_set_line_offset(&ins, offset);
9589 gtk_text_buffer_place_cursor(buffer, &ins);
9593 static void textview_move_previous_line (GtkTextView *text)
9595 GtkTextBuffer *buffer;
9600 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9602 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9603 mark = gtk_text_buffer_get_insert(buffer);
9604 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9605 offset = gtk_text_iter_get_line_offset(&ins);
9606 if (gtk_text_iter_backward_line(&ins)) {
9607 gtk_text_iter_set_line_offset(&ins, offset);
9608 gtk_text_buffer_place_cursor(buffer, &ins);
9612 static void textview_delete_forward_character (GtkTextView *text)
9614 GtkTextBuffer *buffer;
9616 GtkTextIter ins, end_iter;
9618 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9620 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9621 mark = gtk_text_buffer_get_insert(buffer);
9622 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9624 if (gtk_text_iter_forward_char(&end_iter)) {
9625 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9629 static void textview_delete_backward_character (GtkTextView *text)
9631 GtkTextBuffer *buffer;
9633 GtkTextIter ins, end_iter;
9635 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9637 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9638 mark = gtk_text_buffer_get_insert(buffer);
9639 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9641 if (gtk_text_iter_backward_char(&end_iter)) {
9642 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9646 static void textview_delete_forward_word (GtkTextView *text)
9648 GtkTextBuffer *buffer;
9650 GtkTextIter ins, end_iter;
9652 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9654 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9655 mark = gtk_text_buffer_get_insert(buffer);
9656 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9658 if (gtk_text_iter_forward_word_end(&end_iter)) {
9659 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9663 static void textview_delete_backward_word (GtkTextView *text)
9665 GtkTextBuffer *buffer;
9667 GtkTextIter ins, end_iter;
9669 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9671 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9672 mark = gtk_text_buffer_get_insert(buffer);
9673 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9675 if (gtk_text_iter_backward_word_start(&end_iter)) {
9676 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9680 static void textview_delete_line (GtkTextView *text)
9682 GtkTextBuffer *buffer;
9684 GtkTextIter ins, start_iter, end_iter;
9686 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9688 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9689 mark = gtk_text_buffer_get_insert(buffer);
9690 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9693 gtk_text_iter_set_line_offset(&start_iter, 0);
9696 if (gtk_text_iter_ends_line(&end_iter)){
9697 if (!gtk_text_iter_forward_char(&end_iter))
9698 gtk_text_iter_backward_char(&start_iter);
9701 gtk_text_iter_forward_to_line_end(&end_iter);
9702 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9705 static void textview_delete_to_line_end (GtkTextView *text)
9707 GtkTextBuffer *buffer;
9709 GtkTextIter ins, end_iter;
9711 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9713 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9714 mark = gtk_text_buffer_get_insert(buffer);
9715 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9717 if (gtk_text_iter_ends_line(&end_iter))
9718 gtk_text_iter_forward_char(&end_iter);
9720 gtk_text_iter_forward_to_line_end(&end_iter);
9721 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9724 #define DO_ACTION(name, act) { \
9725 if(!strcmp(name, a_name)) { \
9729 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
9731 const gchar *a_name = gtk_action_get_name(action);
9732 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
9733 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
9734 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
9735 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
9736 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
9737 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
9738 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
9739 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
9740 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
9741 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
9742 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
9743 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
9744 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
9745 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
9749 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
9751 Compose *compose = (Compose *)data;
9752 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9753 ComposeCallAdvancedAction action = -1;
9755 action = compose_call_advanced_action_from_path(gaction);
9758 void (*do_action) (GtkTextView *text);
9759 } action_table[] = {
9760 {textview_move_beginning_of_line},
9761 {textview_move_forward_character},
9762 {textview_move_backward_character},
9763 {textview_move_forward_word},
9764 {textview_move_backward_word},
9765 {textview_move_end_of_line},
9766 {textview_move_next_line},
9767 {textview_move_previous_line},
9768 {textview_delete_forward_character},
9769 {textview_delete_backward_character},
9770 {textview_delete_forward_word},
9771 {textview_delete_backward_word},
9772 {textview_delete_line},
9773 {textview_delete_to_line_end}
9776 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
9778 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
9779 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
9780 if (action_table[action].do_action)
9781 action_table[action].do_action(text);
9783 g_warning("Not implemented yet.");
9787 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
9791 if (GTK_IS_EDITABLE(widget)) {
9792 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
9793 gtk_editable_set_position(GTK_EDITABLE(widget),
9796 if (widget->parent && widget->parent->parent
9797 && widget->parent->parent->parent) {
9798 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
9799 gint y = widget->allocation.y;
9800 gint height = widget->allocation.height;
9801 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
9802 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
9804 if (y < (int)shown->value) {
9805 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
9807 if (y + height > (int)shown->value + (int)shown->page_size) {
9808 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
9809 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9810 y + height - (int)shown->page_size - 1);
9812 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9813 (int)shown->upper - (int)shown->page_size - 1);
9820 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
9821 compose->focused_editable = widget;
9824 if (GTK_IS_TEXT_VIEW(widget)
9825 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
9826 g_object_ref(compose->notebook);
9827 g_object_ref(compose->edit_vbox);
9828 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9829 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9830 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
9831 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
9832 g_object_unref(compose->notebook);
9833 g_object_unref(compose->edit_vbox);
9834 g_signal_handlers_block_by_func(G_OBJECT(widget),
9835 G_CALLBACK(compose_grab_focus_cb),
9837 gtk_widget_grab_focus(widget);
9838 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9839 G_CALLBACK(compose_grab_focus_cb),
9841 } else if (!GTK_IS_TEXT_VIEW(widget)
9842 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
9843 g_object_ref(compose->notebook);
9844 g_object_ref(compose->edit_vbox);
9845 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9846 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9847 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
9848 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
9849 g_object_unref(compose->notebook);
9850 g_object_unref(compose->edit_vbox);
9851 g_signal_handlers_block_by_func(G_OBJECT(widget),
9852 G_CALLBACK(compose_grab_focus_cb),
9854 gtk_widget_grab_focus(widget);
9855 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9856 G_CALLBACK(compose_grab_focus_cb),
9862 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
9864 compose->modified = TRUE;
9865 #ifndef GENERIC_UMPC
9866 compose_set_title(compose);
9870 static void compose_wrap_cb(GtkAction *action, gpointer data)
9872 Compose *compose = (Compose *)data;
9873 compose_beautify_paragraph(compose, NULL, TRUE);
9876 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
9878 Compose *compose = (Compose *)data;
9879 compose_wrap_all_full(compose, TRUE);
9882 static void compose_find_cb(GtkAction *action, gpointer data)
9884 Compose *compose = (Compose *)data;
9886 message_search_compose(compose);
9889 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
9892 Compose *compose = (Compose *)data;
9893 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9894 if (compose->autowrap)
9895 compose_wrap_all_full(compose, TRUE);
9896 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9899 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
9901 Compose *compose = (Compose *)data;
9903 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9906 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
9908 Compose *compose = (Compose *)data;
9910 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9913 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
9915 g_free(compose->privacy_system);
9917 compose->privacy_system = g_strdup(account->default_privacy_system);
9918 compose_update_privacy_system_menu_item(compose, warn);
9921 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
9923 Compose *compose = (Compose *)data;
9925 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
9926 gtk_widget_show(compose->ruler_hbox);
9927 prefs_common.show_ruler = TRUE;
9929 gtk_widget_hide(compose->ruler_hbox);
9930 gtk_widget_queue_resize(compose->edit_vbox);
9931 prefs_common.show_ruler = FALSE;
9935 static void compose_attach_drag_received_cb (GtkWidget *widget,
9936 GdkDragContext *context,
9939 GtkSelectionData *data,
9944 Compose *compose = (Compose *)user_data;
9947 if (gdk_atom_name(data->type) &&
9948 !strcmp(gdk_atom_name(data->type), "text/uri-list")
9949 && gtk_drag_get_source_widget(context) !=
9950 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
9951 list = uri_list_extract_filenames((const gchar *)data->data);
9952 for (tmp = list; tmp != NULL; tmp = tmp->next) {
9953 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
9954 compose_attach_append
9955 (compose, (const gchar *)tmp->data,
9956 utf8_filename, NULL);
9957 g_free(utf8_filename);
9959 if (list) compose_changed_cb(NULL, compose);
9960 list_free_strings(list);
9962 } else if (gtk_drag_get_source_widget(context)
9963 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
9964 /* comes from our summaryview */
9965 SummaryView * summaryview = NULL;
9966 GSList * list = NULL, *cur = NULL;
9968 if (mainwindow_get_mainwindow())
9969 summaryview = mainwindow_get_mainwindow()->summaryview;
9972 list = summary_get_selected_msg_list(summaryview);
9974 for (cur = list; cur; cur = cur->next) {
9975 MsgInfo *msginfo = (MsgInfo *)cur->data;
9978 file = procmsg_get_message_file_full(msginfo,
9981 compose_attach_append(compose, (const gchar *)file,
9982 (const gchar *)file, "message/rfc822");
9990 static gboolean compose_drag_drop(GtkWidget *widget,
9991 GdkDragContext *drag_context,
9993 guint time, gpointer user_data)
9995 /* not handling this signal makes compose_insert_drag_received_cb
10000 static void compose_insert_drag_received_cb (GtkWidget *widget,
10001 GdkDragContext *drag_context,
10004 GtkSelectionData *data,
10007 gpointer user_data)
10009 Compose *compose = (Compose *)user_data;
10012 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10014 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10015 AlertValue val = G_ALERTDEFAULT;
10017 list = uri_list_extract_filenames((const gchar *)data->data);
10019 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10020 /* Assume a list of no files, and data has ://, is a remote link */
10021 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10022 gchar *tmpfile = get_tmp_file();
10023 str_write_to_file(tmpdata, tmpfile);
10025 compose_insert_file(compose, tmpfile);
10026 claws_unlink(tmpfile);
10028 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10029 compose_beautify_paragraph(compose, NULL, TRUE);
10032 switch (prefs_common.compose_dnd_mode) {
10033 case COMPOSE_DND_ASK:
10034 val = alertpanel_full(_("Insert or attach?"),
10035 _("Do you want to insert the contents of the file(s) "
10036 "into the message body, or attach it to the email?"),
10037 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10038 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10040 case COMPOSE_DND_INSERT:
10041 val = G_ALERTALTERNATE;
10043 case COMPOSE_DND_ATTACH:
10044 val = G_ALERTOTHER;
10047 /* unexpected case */
10048 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10051 if (val & G_ALERTDISABLE) {
10052 val &= ~G_ALERTDISABLE;
10053 /* remember what action to perform by default, only if we don't click Cancel */
10054 if (val == G_ALERTALTERNATE)
10055 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10056 else if (val == G_ALERTOTHER)
10057 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10060 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10061 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10062 list_free_strings(list);
10065 } else if (val == G_ALERTOTHER) {
10066 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10067 list_free_strings(list);
10072 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10073 compose_insert_file(compose, (const gchar *)tmp->data);
10075 list_free_strings(list);
10077 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10080 #if GTK_CHECK_VERSION(2, 8, 0)
10081 /* do nothing, handled by GTK */
10083 gchar *tmpfile = get_tmp_file();
10084 str_write_to_file((const gchar *)data->data, tmpfile);
10085 compose_insert_file(compose, tmpfile);
10086 claws_unlink(tmpfile);
10088 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10092 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10095 static void compose_header_drag_received_cb (GtkWidget *widget,
10096 GdkDragContext *drag_context,
10099 GtkSelectionData *data,
10102 gpointer user_data)
10104 GtkEditable *entry = (GtkEditable *)user_data;
10105 gchar *email = (gchar *)data->data;
10107 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10110 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10111 gchar *decoded=g_new(gchar, strlen(email));
10114 email += strlen("mailto:");
10115 decode_uri(decoded, email); /* will fit */
10116 gtk_editable_delete_text(entry, 0, -1);
10117 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10118 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10122 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10125 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10127 Compose *compose = (Compose *)data;
10129 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10130 compose->return_receipt = TRUE;
10132 compose->return_receipt = FALSE;
10135 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10137 Compose *compose = (Compose *)data;
10139 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10140 compose->remove_references = TRUE;
10142 compose->remove_references = FALSE;
10145 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10146 GdkEventKey *event,
10147 ComposeHeaderEntry *headerentry)
10149 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10150 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10151 !(event->state & GDK_MODIFIER_MASK) &&
10152 (event->keyval == GDK_BackSpace) &&
10153 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10154 gtk_container_remove
10155 (GTK_CONTAINER(headerentry->compose->header_table),
10156 headerentry->combo);
10157 gtk_container_remove
10158 (GTK_CONTAINER(headerentry->compose->header_table),
10159 headerentry->entry);
10160 headerentry->compose->header_list =
10161 g_slist_remove(headerentry->compose->header_list,
10163 g_free(headerentry);
10164 } else if (event->keyval == GDK_Tab) {
10165 if (headerentry->compose->header_last == headerentry) {
10166 /* Override default next focus, and give it to subject_entry
10167 * instead of notebook tabs
10169 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10170 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10177 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10178 ComposeHeaderEntry *headerentry)
10180 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10181 compose_create_header_entry(headerentry->compose);
10182 g_signal_handlers_disconnect_matched
10183 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10184 0, 0, NULL, NULL, headerentry);
10186 /* Automatically scroll down */
10187 compose_show_first_last_header(headerentry->compose, FALSE);
10193 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10195 GtkAdjustment *vadj;
10197 g_return_if_fail(compose);
10198 g_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10199 g_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10201 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10202 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10203 gtk_adjustment_changed(vadj);
10206 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10207 const gchar *text, gint len, Compose *compose)
10209 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10210 (G_OBJECT(compose->text), "paste_as_quotation"));
10213 g_return_if_fail(text != NULL);
10215 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10216 G_CALLBACK(text_inserted),
10218 if (paste_as_quotation) {
10220 const gchar *qmark;
10222 GtkTextIter start_iter;
10225 len = strlen(text);
10227 new_text = g_strndup(text, len);
10229 qmark = compose_quote_char_from_context(compose);
10231 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10232 gtk_text_buffer_place_cursor(buffer, iter);
10234 pos = gtk_text_iter_get_offset(iter);
10236 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10237 _("Quote format error at line %d."));
10238 quote_fmt_reset_vartable();
10240 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10241 GINT_TO_POINTER(paste_as_quotation - 1));
10243 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10244 gtk_text_buffer_place_cursor(buffer, iter);
10245 gtk_text_buffer_delete_mark(buffer, mark);
10247 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10248 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10249 compose_beautify_paragraph(compose, &start_iter, FALSE);
10250 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10251 gtk_text_buffer_delete_mark(buffer, mark);
10253 if (strcmp(text, "\n") || compose->automatic_break
10254 || gtk_text_iter_starts_line(iter))
10255 gtk_text_buffer_insert(buffer, iter, text, len);
10257 /* check if the preceding is just whitespace or quote */
10258 GtkTextIter start_line;
10259 gchar *tmp = NULL, *quote = NULL;
10260 gint quote_len = 0, is_normal = 0;
10261 start_line = *iter;
10262 gtk_text_iter_set_line_offset(&start_line, 0);
10263 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10265 if (*tmp == '\0') {
10268 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10276 gtk_text_buffer_insert(buffer, iter, text, len);
10278 gtk_text_buffer_insert_with_tags_by_name(buffer,
10279 iter, text, len, "no_join", NULL);
10284 if (!paste_as_quotation) {
10285 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10286 compose_beautify_paragraph(compose, iter, FALSE);
10287 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10288 gtk_text_buffer_delete_mark(buffer, mark);
10291 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10292 G_CALLBACK(text_inserted),
10294 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10296 if (prefs_common.autosave &&
10297 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10298 compose->draft_timeout_tag != -2 /* disabled while loading */)
10299 compose->draft_timeout_tag = g_timeout_add
10300 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10302 static gint compose_defer_auto_save_draft(Compose *compose)
10304 compose->draft_timeout_tag = -1;
10305 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10310 static void compose_check_all(GtkAction *action, gpointer data)
10312 Compose *compose = (Compose *)data;
10313 if (compose->gtkaspell)
10314 gtkaspell_check_all(compose->gtkaspell);
10317 static void compose_highlight_all(GtkAction *action, gpointer data)
10319 Compose *compose = (Compose *)data;
10320 if (compose->gtkaspell)
10321 gtkaspell_highlight_all(compose->gtkaspell);
10324 static void compose_check_backwards(GtkAction *action, gpointer data)
10326 Compose *compose = (Compose *)data;
10327 if (compose->gtkaspell)
10328 gtkaspell_check_backwards(compose->gtkaspell);
10330 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10334 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10336 Compose *compose = (Compose *)data;
10337 if (compose->gtkaspell)
10338 gtkaspell_check_forwards_go(compose->gtkaspell);
10340 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10346 *\brief Guess originating forward account from MsgInfo and several
10347 * "common preference" settings. Return NULL if no guess.
10349 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10351 PrefsAccount *account = NULL;
10353 g_return_val_if_fail(msginfo, NULL);
10354 g_return_val_if_fail(msginfo->folder, NULL);
10355 g_return_val_if_fail(msginfo->folder->prefs, NULL);
10357 if (msginfo->folder->prefs->enable_default_account)
10358 account = account_find_from_id(msginfo->folder->prefs->default_account);
10361 account = msginfo->folder->folder->account;
10363 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10365 Xstrdup_a(to, msginfo->to, return NULL);
10366 extract_address(to);
10367 account = account_find_from_address(to, FALSE);
10370 if (!account && prefs_common.forward_account_autosel) {
10371 gchar cc[BUFFSIZE];
10372 if (!procheader_get_header_from_msginfo
10373 (msginfo, cc,sizeof cc , "Cc:")) {
10374 gchar *buf = cc + strlen("Cc:");
10375 extract_address(buf);
10376 account = account_find_from_address(buf, FALSE);
10380 if (!account && prefs_common.forward_account_autosel) {
10381 gchar deliveredto[BUFFSIZE];
10382 if (!procheader_get_header_from_msginfo
10383 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10384 gchar *buf = deliveredto + strlen("Delivered-To:");
10385 extract_address(buf);
10386 account = account_find_from_address(buf, FALSE);
10393 gboolean compose_close(Compose *compose)
10397 if (!g_mutex_trylock(compose->mutex)) {
10398 /* we have to wait for the (possibly deferred by auto-save)
10399 * drafting to be done, before destroying the compose under
10401 debug_print("waiting for drafting to finish...\n");
10402 compose_allow_user_actions(compose, FALSE);
10403 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10406 g_return_val_if_fail(compose, FALSE);
10407 gtkut_widget_get_uposition(compose->window, &x, &y);
10408 prefs_common.compose_x = x;
10409 prefs_common.compose_y = y;
10410 g_mutex_unlock(compose->mutex);
10411 compose_destroy(compose);
10416 * Add entry field for each address in list.
10417 * \param compose E-Mail composition object.
10418 * \param listAddress List of (formatted) E-Mail addresses.
10420 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10423 node = listAddress;
10425 addr = ( gchar * ) node->data;
10426 compose_entry_append( compose, addr, COMPOSE_TO );
10427 node = g_list_next( node );
10431 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10432 guint action, gboolean opening_multiple)
10434 gchar *body = NULL;
10435 GSList *new_msglist = NULL;
10436 MsgInfo *tmp_msginfo = NULL;
10437 gboolean originally_enc = FALSE;
10438 Compose *compose = NULL;
10440 g_return_if_fail(msgview != NULL);
10442 g_return_if_fail(msginfo_list != NULL);
10444 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10445 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10446 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10448 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10449 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10450 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10451 orig_msginfo, mimeinfo);
10452 if (tmp_msginfo != NULL) {
10453 new_msglist = g_slist_append(NULL, tmp_msginfo);
10455 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10456 tmp_msginfo->folder = orig_msginfo->folder;
10457 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10458 if (orig_msginfo->tags)
10459 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10464 if (!opening_multiple)
10465 body = messageview_get_selection(msgview);
10468 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10469 procmsg_msginfo_free(tmp_msginfo);
10470 g_slist_free(new_msglist);
10472 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10474 if (compose && originally_enc) {
10475 compose_force_encryption(compose, compose->account, FALSE);
10481 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10484 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
10485 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10486 GSList *cur = msginfo_list;
10487 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10488 "messages. Opening the windows "
10489 "could take some time. Do you "
10490 "want to continue?"),
10491 g_slist_length(msginfo_list));
10492 if (g_slist_length(msginfo_list) > 9
10493 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10494 != G_ALERTALTERNATE) {
10499 /* We'll open multiple compose windows */
10500 /* let the WM place the next windows */
10501 compose_force_window_origin = FALSE;
10502 for (; cur; cur = cur->next) {
10504 tmplist.data = cur->data;
10505 tmplist.next = NULL;
10506 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10508 compose_force_window_origin = TRUE;
10510 /* forwarding multiple mails as attachments is done via a
10511 * single compose window */
10512 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10516 void compose_set_position(Compose *compose, gint pos)
10518 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10520 gtkut_text_view_set_position(text, pos);
10523 gboolean compose_search_string(Compose *compose,
10524 const gchar *str, gboolean case_sens)
10526 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10528 return gtkut_text_view_search_string(text, str, case_sens);
10531 gboolean compose_search_string_backward(Compose *compose,
10532 const gchar *str, gboolean case_sens)
10534 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10536 return gtkut_text_view_search_string_backward(text, str, case_sens);
10539 /* allocate a msginfo structure and populate its data from a compose data structure */
10540 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10542 MsgInfo *newmsginfo;
10544 gchar buf[BUFFSIZE];
10546 g_return_val_if_fail( compose != NULL, NULL );
10548 newmsginfo = procmsg_msginfo_new();
10551 get_rfc822_date(buf, sizeof(buf));
10552 newmsginfo->date = g_strdup(buf);
10555 if (compose->from_name) {
10556 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10557 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10561 if (compose->subject_entry)
10562 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10564 /* to, cc, reply-to, newsgroups */
10565 for (list = compose->header_list; list; list = list->next) {
10566 gchar *header = gtk_editable_get_chars(
10568 GTK_BIN(((ComposeHeaderEntry *)list->data)->combo)->child), 0, -1);
10569 gchar *entry = gtk_editable_get_chars(
10570 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10572 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10573 if ( newmsginfo->to == NULL ) {
10574 newmsginfo->to = g_strdup(entry);
10575 } else if (entry && *entry) {
10576 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10577 g_free(newmsginfo->to);
10578 newmsginfo->to = tmp;
10581 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10582 if ( newmsginfo->cc == NULL ) {
10583 newmsginfo->cc = g_strdup(entry);
10584 } else if (entry && *entry) {
10585 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10586 g_free(newmsginfo->cc);
10587 newmsginfo->cc = tmp;
10590 if ( strcasecmp(header,
10591 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10592 if ( newmsginfo->newsgroups == NULL ) {
10593 newmsginfo->newsgroups = g_strdup(entry);
10594 } else if (entry && *entry) {
10595 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10596 g_free(newmsginfo->newsgroups);
10597 newmsginfo->newsgroups = tmp;
10605 /* other data is unset */
10611 /* update compose's dictionaries from folder dict settings */
10612 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10613 FolderItem *folder_item)
10615 g_return_if_fail(compose != NULL);
10617 if (compose->gtkaspell && folder_item && folder_item->prefs) {
10618 FolderItemPrefs *prefs = folder_item->prefs;
10620 if (prefs->enable_default_dictionary)
10621 gtkaspell_change_dict(compose->gtkaspell,
10622 prefs->default_dictionary, FALSE);
10623 if (folder_item->prefs->enable_default_alt_dictionary)
10624 gtkaspell_change_alt_dict(compose->gtkaspell,
10625 prefs->default_alt_dictionary);
10626 if (prefs->enable_default_dictionary
10627 || prefs->enable_default_alt_dictionary)
10628 compose_spell_menu_changed(compose);