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_widget_set_uposition(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_paned_set_gutter_size(GTK_PANED(paned), 12);
7021 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7023 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7024 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7026 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7028 gtk_paned_add1(GTK_PANED(paned), notebook);
7029 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7030 gtk_widget_show_all(paned);
7033 if (prefs_common.textfont) {
7034 PangoFontDescription *font_desc;
7036 font_desc = pango_font_description_from_string
7037 (prefs_common.textfont);
7039 gtk_widget_modify_font(text, font_desc);
7040 pango_font_description_free(font_desc);
7044 gtk_action_group_add_actions(action_group, compose_popup_entries,
7045 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7046 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7047 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7048 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7049 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7050 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7051 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7053 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7055 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7056 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7057 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7059 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7061 undostruct = undo_init(text);
7062 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7065 address_completion_start(window);
7067 compose->window = window;
7068 compose->vbox = vbox;
7069 compose->menubar = menubar;
7070 compose->handlebox = handlebox;
7072 compose->vbox2 = vbox2;
7074 compose->paned = paned;
7076 compose->attach_label = attach_lab2;
7078 compose->notebook = notebook;
7079 compose->edit_vbox = edit_vbox;
7080 compose->ruler_hbox = ruler_hbox;
7081 compose->ruler = ruler;
7082 compose->scrolledwin = scrolledwin;
7083 compose->text = text;
7085 compose->focused_editable = NULL;
7087 compose->popupmenu = popupmenu;
7089 compose->tmpl_menu = tmpl_menu;
7091 compose->mode = mode;
7092 compose->rmode = mode;
7094 compose->targetinfo = NULL;
7095 compose->replyinfo = NULL;
7096 compose->fwdinfo = NULL;
7098 compose->replyto = NULL;
7100 compose->bcc = NULL;
7101 compose->followup_to = NULL;
7103 compose->ml_post = NULL;
7105 compose->inreplyto = NULL;
7106 compose->references = NULL;
7107 compose->msgid = NULL;
7108 compose->boundary = NULL;
7110 compose->autowrap = prefs_common.autowrap;
7112 compose->use_signing = FALSE;
7113 compose->use_encryption = FALSE;
7114 compose->privacy_system = NULL;
7116 compose->modified = FALSE;
7118 compose->return_receipt = FALSE;
7120 compose->to_list = NULL;
7121 compose->newsgroup_list = NULL;
7123 compose->undostruct = undostruct;
7125 compose->sig_str = NULL;
7127 compose->exteditor_file = NULL;
7128 compose->exteditor_pid = -1;
7129 compose->exteditor_tag = -1;
7130 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7133 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7134 if (mode != COMPOSE_REDIRECT) {
7135 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7136 strcmp(prefs_common.dictionary, "")) {
7137 gtkaspell = gtkaspell_new(prefs_common.aspell_path,
7138 prefs_common.dictionary,
7139 prefs_common.alt_dictionary,
7140 conv_get_locale_charset_str(),
7141 prefs_common.misspelled_col,
7142 prefs_common.check_while_typing,
7143 prefs_common.recheck_when_changing_dict,
7144 prefs_common.use_alternate,
7145 prefs_common.use_both_dicts,
7146 GTK_TEXT_VIEW(text),
7147 GTK_WINDOW(compose->window),
7148 compose_spell_menu_changed,
7151 alertpanel_error(_("Spell checker could not "
7153 gtkaspell_checkers_strerror());
7154 gtkaspell_checkers_reset_error();
7156 if (!gtkaspell_set_sug_mode(gtkaspell,
7157 prefs_common.aspell_sugmode)) {
7158 debug_print("Aspell: could not set "
7159 "suggestion mode %s\n",
7160 gtkaspell_checkers_strerror());
7161 gtkaspell_checkers_reset_error();
7164 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7168 compose->gtkaspell = gtkaspell;
7169 compose_spell_menu_changed(compose);
7172 compose_select_account(compose, account, TRUE);
7174 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7176 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7177 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7179 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7180 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7182 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7183 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7185 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7186 if (account->protocol != A_NNTP)
7187 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(compose->header_last->combo)->child),
7188 prefs_common_translated_header_name("To:"));
7190 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(compose->header_last->combo)->child),
7191 prefs_common_translated_header_name("Newsgroups:"));
7193 addressbook_set_target_compose(compose);
7195 if (mode != COMPOSE_REDIRECT)
7196 compose_set_template_menu(compose);
7198 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7201 compose_list = g_list_append(compose_list, compose);
7203 if (!prefs_common.show_ruler)
7204 gtk_widget_hide(ruler_hbox);
7206 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7209 compose->priority = PRIORITY_NORMAL;
7210 compose_update_priority_menu_item(compose);
7212 compose_set_out_encoding(compose);
7215 compose_update_actions_menu(compose);
7217 /* Privacy Systems menu */
7218 compose_update_privacy_systems_menu(compose);
7220 activate_privacy_system(compose, account, TRUE);
7221 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7223 gtk_widget_realize(window);
7225 gtk_widget_show(window);
7227 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7228 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7235 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7240 GtkWidget *optmenubox;
7243 GtkWidget *from_name = NULL;
7244 #if !(GTK_CHECK_VERSION(2,12,0))
7245 GtkTooltips *tips = compose->tooltips;
7248 gint num = 0, def_menu = 0;
7250 accounts = account_get_list();
7251 g_return_val_if_fail(accounts != NULL, NULL);
7253 optmenubox = gtk_event_box_new();
7254 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7255 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7257 hbox = gtk_hbox_new(FALSE, 6);
7258 from_name = gtk_entry_new();
7260 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7261 G_CALLBACK(compose_grab_focus_cb), compose);
7263 for (; accounts != NULL; accounts = accounts->next, num++) {
7264 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7265 gchar *name, *from = NULL;
7267 if (ac == compose->account) def_menu = num;
7269 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7272 if (ac == compose->account) {
7273 if (ac->name && *ac->name) {
7275 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7276 from = g_strdup_printf("%s <%s>",
7278 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7280 from = g_strdup_printf("%s",
7282 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7285 COMBOBOX_ADD(menu, name, ac->account_id);
7290 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7292 g_signal_connect(G_OBJECT(optmenu), "changed",
7293 G_CALLBACK(account_activated),
7295 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7296 G_CALLBACK(compose_entry_popup_extend),
7299 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7300 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7302 CLAWS_SET_TIP(optmenubox,
7303 _("Account to use for this email"));
7304 CLAWS_SET_TIP(from_name,
7305 _("Sender address to be used"));
7307 compose->from_name = from_name;
7312 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7314 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7315 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7316 Compose *compose = (Compose *) data;
7318 printf("activated %d\n", value);
7319 compose->priority = value;
7323 static void compose_reply_change_mode(Compose *compose,
7326 gboolean was_modified = compose->modified;
7328 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7330 g_return_if_fail(compose->replyinfo != NULL);
7332 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7334 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7336 if (action == COMPOSE_REPLY_TO_ALL)
7338 if (action == COMPOSE_REPLY_TO_SENDER)
7340 if (action == COMPOSE_REPLY_TO_LIST)
7343 compose_remove_header_entries(compose);
7344 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7345 if (compose->account->set_autocc && compose->account->auto_cc)
7346 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7348 if (compose->account->set_autobcc && compose->account->auto_bcc)
7349 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7351 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7352 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7353 compose_show_first_last_header(compose, TRUE);
7354 compose->modified = was_modified;
7355 compose_set_title(compose);
7358 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7360 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7361 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7362 Compose *compose = (Compose *) data;
7365 compose_reply_change_mode(compose, value);
7368 static void compose_update_priority_menu_item(Compose * compose)
7370 GtkWidget *menuitem = NULL;
7371 switch (compose->priority) {
7372 case PRIORITY_HIGHEST:
7373 menuitem = gtk_ui_manager_get_widget
7374 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7377 menuitem = gtk_ui_manager_get_widget
7378 (compose->ui_manager, "/Menu/Options/Priority/High");
7380 case PRIORITY_NORMAL:
7381 menuitem = gtk_ui_manager_get_widget
7382 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7385 menuitem = gtk_ui_manager_get_widget
7386 (compose->ui_manager, "/Menu/Options/Priority/Low");
7388 case PRIORITY_LOWEST:
7389 menuitem = gtk_ui_manager_get_widget
7390 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7393 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7396 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7398 Compose *compose = (Compose *) data;
7400 gboolean can_sign = FALSE, can_encrypt = FALSE;
7402 g_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7404 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7407 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7408 g_free(compose->privacy_system);
7409 compose->privacy_system = NULL;
7410 if (systemid != NULL) {
7411 compose->privacy_system = g_strdup(systemid);
7413 can_sign = privacy_system_can_sign(systemid);
7414 can_encrypt = privacy_system_can_encrypt(systemid);
7417 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7419 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7420 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7423 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7425 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7426 GtkWidget *menuitem = NULL;
7428 gboolean can_sign = FALSE, can_encrypt = FALSE;
7429 gboolean found = FALSE;
7431 if (compose->privacy_system != NULL) {
7433 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7434 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7435 g_return_if_fail(menuitem != NULL);
7437 amenu = GTK_MENU_SHELL(menuitem)->children;
7439 while (amenu != NULL) {
7440 GList *alist = amenu->next;
7442 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7443 if (systemid != NULL) {
7444 if (strcmp(systemid, compose->privacy_system) == 0) {
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 menuitem = GTK_WIDGET(amenu->data);
7456 can_encrypt = FALSE;
7463 if (menuitem != NULL)
7464 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7466 if (warn && !found && strlen(compose->privacy_system)) {
7467 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7468 "will not be able to sign or encrypt this message."),
7469 compose->privacy_system);
7473 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7474 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7477 static void compose_set_out_encoding(Compose *compose)
7479 CharSet out_encoding;
7480 const gchar *branch = NULL;
7481 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7483 switch(out_encoding) {
7484 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7485 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7486 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7487 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7488 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7489 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7490 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7491 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7492 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7493 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7494 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7495 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7496 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7497 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7498 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7499 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7500 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7501 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7502 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7503 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7504 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7505 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7506 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7507 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7508 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7509 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7510 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7511 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7512 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7513 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7514 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7515 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7517 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7520 static void compose_set_template_menu(Compose *compose)
7522 GSList *tmpl_list, *cur;
7526 tmpl_list = template_get_config();
7528 menu = gtk_menu_new();
7530 gtk_menu_set_accel_group (GTK_MENU (menu),
7531 gtk_ui_manager_get_accel_group(compose->ui_manager));
7532 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7533 Template *tmpl = (Template *)cur->data;
7534 gchar *accel_path = NULL;
7535 item = gtk_menu_item_new_with_label(tmpl->name);
7536 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7537 g_signal_connect(G_OBJECT(item), "activate",
7538 G_CALLBACK(compose_template_activate_cb),
7540 g_object_set_data(G_OBJECT(item), "template", tmpl);
7541 gtk_widget_show(item);
7542 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7543 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7547 gtk_widget_show(menu);
7548 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7551 void compose_update_actions_menu(Compose *compose)
7553 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7556 static void compose_update_privacy_systems_menu(Compose *compose)
7558 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7559 GSList *systems, *cur;
7561 GtkWidget *system_none;
7563 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7564 GtkWidget *privacy_menu = gtk_menu_new();
7566 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7567 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7569 g_signal_connect(G_OBJECT(system_none), "activate",
7570 G_CALLBACK(compose_set_privacy_system_cb), compose);
7572 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7573 gtk_widget_show(system_none);
7575 systems = privacy_get_system_ids();
7576 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7577 gchar *systemid = cur->data;
7579 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7580 widget = gtk_radio_menu_item_new_with_label(group,
7581 privacy_system_get_name(systemid));
7582 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7583 g_strdup(systemid), g_free);
7584 g_signal_connect(G_OBJECT(widget), "activate",
7585 G_CALLBACK(compose_set_privacy_system_cb), compose);
7587 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7588 gtk_widget_show(widget);
7591 g_slist_free(systems);
7592 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7593 gtk_widget_show_all(privacy_menu);
7594 gtk_widget_show_all(privacy_menuitem);
7597 void compose_reflect_prefs_all(void)
7602 for (cur = compose_list; cur != NULL; cur = cur->next) {
7603 compose = (Compose *)cur->data;
7604 compose_set_template_menu(compose);
7608 void compose_reflect_prefs_pixmap_theme(void)
7613 for (cur = compose_list; cur != NULL; cur = cur->next) {
7614 compose = (Compose *)cur->data;
7615 toolbar_update(TOOLBAR_COMPOSE, compose);
7619 static const gchar *compose_quote_char_from_context(Compose *compose)
7621 const gchar *qmark = NULL;
7623 g_return_val_if_fail(compose != NULL, NULL);
7625 switch (compose->mode) {
7626 /* use forward-specific quote char */
7627 case COMPOSE_FORWARD:
7628 case COMPOSE_FORWARD_AS_ATTACH:
7629 case COMPOSE_FORWARD_INLINE:
7630 if (compose->folder && compose->folder->prefs &&
7631 compose->folder->prefs->forward_with_format)
7632 qmark = compose->folder->prefs->forward_quotemark;
7633 else if (compose->account->forward_with_format)
7634 qmark = compose->account->forward_quotemark;
7636 qmark = prefs_common.fw_quotemark;
7639 /* use reply-specific quote char in all other modes */
7641 if (compose->folder && compose->folder->prefs &&
7642 compose->folder->prefs->reply_with_format)
7643 qmark = compose->folder->prefs->reply_quotemark;
7644 else if (compose->account->reply_with_format)
7645 qmark = compose->account->reply_quotemark;
7647 qmark = prefs_common.quotemark;
7651 if (qmark == NULL || *qmark == '\0')
7657 static void compose_template_apply(Compose *compose, Template *tmpl,
7661 GtkTextBuffer *buffer;
7665 gchar *parsed_str = NULL;
7666 gint cursor_pos = 0;
7667 const gchar *err_msg = _("Template body format error at line %d.");
7670 /* process the body */
7672 text = GTK_TEXT_VIEW(compose->text);
7673 buffer = gtk_text_view_get_buffer(text);
7676 qmark = compose_quote_char_from_context(compose);
7678 if (compose->replyinfo != NULL) {
7681 gtk_text_buffer_set_text(buffer, "", -1);
7682 mark = gtk_text_buffer_get_insert(buffer);
7683 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7685 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7686 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7688 } else if (compose->fwdinfo != NULL) {
7691 gtk_text_buffer_set_text(buffer, "", -1);
7692 mark = gtk_text_buffer_get_insert(buffer);
7693 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7695 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7696 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7699 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7701 GtkTextIter start, end;
7704 gtk_text_buffer_get_start_iter(buffer, &start);
7705 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7706 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7708 /* clear the buffer now */
7710 gtk_text_buffer_set_text(buffer, "", -1);
7712 parsed_str = compose_quote_fmt(compose, dummyinfo,
7713 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7714 procmsg_msginfo_free( dummyinfo );
7720 gtk_text_buffer_set_text(buffer, "", -1);
7721 mark = gtk_text_buffer_get_insert(buffer);
7722 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7725 if (replace && parsed_str && compose->account->auto_sig)
7726 compose_insert_sig(compose, FALSE);
7728 if (replace && parsed_str) {
7729 gtk_text_buffer_get_start_iter(buffer, &iter);
7730 gtk_text_buffer_place_cursor(buffer, &iter);
7734 cursor_pos = quote_fmt_get_cursor_pos();
7735 compose->set_cursor_pos = cursor_pos;
7736 if (cursor_pos == -1)
7738 gtk_text_buffer_get_start_iter(buffer, &iter);
7739 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7740 gtk_text_buffer_place_cursor(buffer, &iter);
7743 /* process the other fields */
7745 compose_template_apply_fields(compose, tmpl);
7746 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7747 quote_fmt_reset_vartable();
7748 compose_changed_cb(NULL, compose);
7751 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7753 MsgInfo* dummyinfo = NULL;
7754 MsgInfo *msginfo = NULL;
7757 if (compose->replyinfo != NULL)
7758 msginfo = compose->replyinfo;
7759 else if (compose->fwdinfo != NULL)
7760 msginfo = compose->fwdinfo;
7762 dummyinfo = compose_msginfo_new_from_compose(compose);
7763 msginfo = dummyinfo;
7766 if (tmpl->from && *tmpl->from != '\0') {
7768 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7769 compose->gtkaspell);
7771 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7773 quote_fmt_scan_string(tmpl->from);
7776 buf = quote_fmt_get_buffer();
7778 alertpanel_error(_("Template From format error."));
7780 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
7784 if (tmpl->to && *tmpl->to != '\0') {
7786 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7787 compose->gtkaspell);
7789 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7791 quote_fmt_scan_string(tmpl->to);
7794 buf = quote_fmt_get_buffer();
7796 alertpanel_error(_("Template To format error."));
7798 compose_entry_append(compose, buf, COMPOSE_TO);
7802 if (tmpl->cc && *tmpl->cc != '\0') {
7804 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7805 compose->gtkaspell);
7807 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7809 quote_fmt_scan_string(tmpl->cc);
7812 buf = quote_fmt_get_buffer();
7814 alertpanel_error(_("Template Cc format error."));
7816 compose_entry_append(compose, buf, COMPOSE_CC);
7820 if (tmpl->bcc && *tmpl->bcc != '\0') {
7822 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7823 compose->gtkaspell);
7825 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7827 quote_fmt_scan_string(tmpl->bcc);
7830 buf = quote_fmt_get_buffer();
7832 alertpanel_error(_("Template Bcc format error."));
7834 compose_entry_append(compose, buf, COMPOSE_BCC);
7838 /* process the subject */
7839 if (tmpl->subject && *tmpl->subject != '\0') {
7841 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7842 compose->gtkaspell);
7844 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7846 quote_fmt_scan_string(tmpl->subject);
7849 buf = quote_fmt_get_buffer();
7851 alertpanel_error(_("Template subject format error."));
7853 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
7857 procmsg_msginfo_free( dummyinfo );
7860 static void compose_destroy(Compose *compose)
7862 GtkTextBuffer *buffer;
7863 GtkClipboard *clipboard;
7865 compose_list = g_list_remove(compose_list, compose);
7867 if (compose->updating) {
7868 debug_print("danger, not destroying anything now\n");
7869 compose->deferred_destroy = TRUE;
7872 /* NOTE: address_completion_end() does nothing with the window
7873 * however this may change. */
7874 address_completion_end(compose->window);
7876 slist_free_strings(compose->to_list);
7877 g_slist_free(compose->to_list);
7878 slist_free_strings(compose->newsgroup_list);
7879 g_slist_free(compose->newsgroup_list);
7880 slist_free_strings(compose->header_list);
7881 g_slist_free(compose->header_list);
7883 procmsg_msginfo_free(compose->targetinfo);
7884 procmsg_msginfo_free(compose->replyinfo);
7885 procmsg_msginfo_free(compose->fwdinfo);
7887 g_free(compose->replyto);
7888 g_free(compose->cc);
7889 g_free(compose->bcc);
7890 g_free(compose->newsgroups);
7891 g_free(compose->followup_to);
7893 g_free(compose->ml_post);
7895 g_free(compose->inreplyto);
7896 g_free(compose->references);
7897 g_free(compose->msgid);
7898 g_free(compose->boundary);
7900 g_free(compose->redirect_filename);
7901 if (compose->undostruct)
7902 undo_destroy(compose->undostruct);
7904 g_free(compose->sig_str);
7906 g_free(compose->exteditor_file);
7908 g_free(compose->orig_charset);
7910 g_free(compose->privacy_system);
7912 if (addressbook_get_target_compose() == compose)
7913 addressbook_set_target_compose(NULL);
7916 if (compose->gtkaspell) {
7917 gtkaspell_delete(compose->gtkaspell);
7918 compose->gtkaspell = NULL;
7922 prefs_common.compose_width = compose->scrolledwin->allocation.width;
7923 prefs_common.compose_height = compose->window->allocation.height;
7925 if (!gtk_widget_get_parent(compose->paned))
7926 gtk_widget_destroy(compose->paned);
7927 gtk_widget_destroy(compose->popupmenu);
7929 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
7930 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7931 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
7933 gtk_widget_destroy(compose->window);
7934 toolbar_destroy(compose->toolbar);
7935 g_free(compose->toolbar);
7936 g_mutex_free(compose->mutex);
7940 static void compose_attach_info_free(AttachInfo *ainfo)
7942 g_free(ainfo->file);
7943 g_free(ainfo->content_type);
7944 g_free(ainfo->name);
7948 static void compose_attach_update_label(Compose *compose)
7953 GtkTreeModel *model;
7958 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
7959 if(!gtk_tree_model_get_iter_first(model, &iter)) {
7960 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
7964 while(gtk_tree_model_iter_next(model, &iter))
7967 text = g_strdup_printf("(%d)", i);
7968 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
7972 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
7974 Compose *compose = (Compose *)data;
7975 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
7976 GtkTreeSelection *selection;
7978 GtkTreeModel *model;
7980 selection = gtk_tree_view_get_selection(tree_view);
7981 sel = gtk_tree_selection_get_selected_rows(selection, &model);
7986 for (cur = sel; cur != NULL; cur = cur->next) {
7987 GtkTreePath *path = cur->data;
7988 GtkTreeRowReference *ref = gtk_tree_row_reference_new
7991 gtk_tree_path_free(path);
7994 for (cur = sel; cur != NULL; cur = cur->next) {
7995 GtkTreeRowReference *ref = cur->data;
7996 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
7999 if (gtk_tree_model_get_iter(model, &iter, path))
8000 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8002 gtk_tree_path_free(path);
8003 gtk_tree_row_reference_free(ref);
8007 compose_attach_update_label(compose);
8010 static struct _AttachProperty
8013 GtkWidget *mimetype_entry;
8014 GtkWidget *encoding_optmenu;
8015 GtkWidget *path_entry;
8016 GtkWidget *filename_entry;
8018 GtkWidget *cancel_btn;
8021 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8023 gtk_tree_path_free((GtkTreePath *)ptr);
8026 static void compose_attach_property(GtkAction *action, gpointer data)
8028 Compose *compose = (Compose *)data;
8029 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8031 GtkComboBox *optmenu;
8032 GtkTreeSelection *selection;
8034 GtkTreeModel *model;
8037 static gboolean cancelled;
8039 /* only if one selected */
8040 selection = gtk_tree_view_get_selection(tree_view);
8041 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8044 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8048 path = (GtkTreePath *) sel->data;
8049 gtk_tree_model_get_iter(model, &iter, path);
8050 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8053 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8059 if (!attach_prop.window)
8060 compose_attach_property_create(&cancelled);
8061 gtk_widget_grab_focus(attach_prop.ok_btn);
8062 gtk_widget_show(attach_prop.window);
8063 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8065 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8066 if (ainfo->encoding == ENC_UNKNOWN)
8067 combobox_select_by_data(optmenu, ENC_BASE64);
8069 combobox_select_by_data(optmenu, ainfo->encoding);
8071 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8072 ainfo->content_type ? ainfo->content_type : "");
8073 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8074 ainfo->file ? ainfo->file : "");
8075 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8076 ainfo->name ? ainfo->name : "");
8079 const gchar *entry_text;
8081 gchar *cnttype = NULL;
8088 gtk_widget_hide(attach_prop.window);
8093 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8094 if (*entry_text != '\0') {
8097 text = g_strstrip(g_strdup(entry_text));
8098 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8099 cnttype = g_strdup(text);
8102 alertpanel_error(_("Invalid MIME type."));
8108 ainfo->encoding = combobox_get_active_data(optmenu);
8110 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8111 if (*entry_text != '\0') {
8112 if (is_file_exist(entry_text) &&
8113 (size = get_file_size(entry_text)) > 0)
8114 file = g_strdup(entry_text);
8117 (_("File doesn't exist or is empty."));
8123 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8124 if (*entry_text != '\0') {
8125 g_free(ainfo->name);
8126 ainfo->name = g_strdup(entry_text);
8130 g_free(ainfo->content_type);
8131 ainfo->content_type = cnttype;
8134 g_free(ainfo->file);
8138 ainfo->size = (goffset)size;
8140 /* update tree store */
8141 text = to_human_readable(ainfo->size);
8142 gtk_tree_model_get_iter(model, &iter, path);
8143 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8144 COL_MIMETYPE, ainfo->content_type,
8146 COL_NAME, ainfo->name,
8152 gtk_tree_path_free(path);
8155 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8157 label = gtk_label_new(str); \
8158 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8159 GTK_FILL, 0, 0, 0); \
8160 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8162 entry = gtk_entry_new(); \
8163 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8164 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8167 static void compose_attach_property_create(gboolean *cancelled)
8173 GtkWidget *mimetype_entry;
8176 GtkListStore *optmenu_menu;
8177 GtkWidget *path_entry;
8178 GtkWidget *filename_entry;
8181 GtkWidget *cancel_btn;
8182 GList *mime_type_list, *strlist;
8185 debug_print("Creating attach_property window...\n");
8187 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8188 gtk_widget_set_size_request(window, 480, -1);
8189 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8190 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8191 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8192 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8193 g_signal_connect(G_OBJECT(window), "delete_event",
8194 G_CALLBACK(attach_property_delete_event),
8196 g_signal_connect(G_OBJECT(window), "key_press_event",
8197 G_CALLBACK(attach_property_key_pressed),
8200 vbox = gtk_vbox_new(FALSE, 8);
8201 gtk_container_add(GTK_CONTAINER(window), vbox);
8203 table = gtk_table_new(4, 2, FALSE);
8204 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8205 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8206 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8208 label = gtk_label_new(_("MIME type"));
8209 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8211 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8212 mimetype_entry = gtk_combo_box_entry_new_text();
8213 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8214 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8216 /* stuff with list */
8217 mime_type_list = procmime_get_mime_type_list();
8219 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8220 MimeType *type = (MimeType *) mime_type_list->data;
8223 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8225 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8228 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8229 (GCompareFunc)strcmp2);
8232 for (mime_type_list = strlist; mime_type_list != NULL;
8233 mime_type_list = mime_type_list->next) {
8234 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8235 g_free(mime_type_list->data);
8237 g_list_free(strlist);
8238 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8239 mimetype_entry = GTK_BIN(mimetype_entry)->child;
8241 label = gtk_label_new(_("Encoding"));
8242 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8244 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8246 hbox = gtk_hbox_new(FALSE, 0);
8247 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8248 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8250 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8251 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8253 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8254 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8255 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8256 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8257 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8259 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8261 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8262 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8264 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8265 &ok_btn, GTK_STOCK_OK,
8267 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8268 gtk_widget_grab_default(ok_btn);
8270 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8271 G_CALLBACK(attach_property_ok),
8273 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8274 G_CALLBACK(attach_property_cancel),
8277 gtk_widget_show_all(vbox);
8279 attach_prop.window = window;
8280 attach_prop.mimetype_entry = mimetype_entry;
8281 attach_prop.encoding_optmenu = optmenu;
8282 attach_prop.path_entry = path_entry;
8283 attach_prop.filename_entry = filename_entry;
8284 attach_prop.ok_btn = ok_btn;
8285 attach_prop.cancel_btn = cancel_btn;
8288 #undef SET_LABEL_AND_ENTRY
8290 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8296 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8302 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8303 gboolean *cancelled)
8311 static gboolean attach_property_key_pressed(GtkWidget *widget,
8313 gboolean *cancelled)
8315 if (event && event->keyval == GDK_Escape) {
8322 static void compose_exec_ext_editor(Compose *compose)
8329 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8330 G_DIR_SEPARATOR, compose);
8332 if (pipe(pipe_fds) < 0) {
8338 if ((pid = fork()) < 0) {
8345 /* close the write side of the pipe */
8348 compose->exteditor_file = g_strdup(tmp);
8349 compose->exteditor_pid = pid;
8351 compose_set_ext_editor_sensitive(compose, FALSE);
8353 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8354 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8358 } else { /* process-monitoring process */
8364 /* close the read side of the pipe */
8367 if (compose_write_body_to_file(compose, tmp) < 0) {
8368 fd_write_all(pipe_fds[1], "2\n", 2);
8372 pid_ed = compose_exec_ext_editor_real(tmp);
8374 fd_write_all(pipe_fds[1], "1\n", 2);
8378 /* wait until editor is terminated */
8379 waitpid(pid_ed, NULL, 0);
8381 fd_write_all(pipe_fds[1], "0\n", 2);
8388 #endif /* G_OS_UNIX */
8392 static gint compose_exec_ext_editor_real(const gchar *file)
8399 g_return_val_if_fail(file != NULL, -1);
8401 if ((pid = fork()) < 0) {
8406 if (pid != 0) return pid;
8408 /* grandchild process */
8410 if (setpgid(0, getppid()))
8413 if (prefs_common_get_ext_editor_cmd() &&
8414 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8415 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8416 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8418 if (prefs_common_get_ext_editor_cmd())
8419 g_warning("External editor command line is invalid: '%s'\n",
8420 prefs_common_get_ext_editor_cmd());
8421 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8424 cmdline = strsplit_with_quote(buf, " ", 1024);
8425 execvp(cmdline[0], cmdline);
8428 g_strfreev(cmdline);
8433 static gboolean compose_ext_editor_kill(Compose *compose)
8435 pid_t pgid = compose->exteditor_pid * -1;
8438 ret = kill(pgid, 0);
8440 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8444 msg = g_strdup_printf
8445 (_("The external editor is still working.\n"
8446 "Force terminating the process?\n"
8447 "process group id: %d"), -pgid);
8448 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8449 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8453 if (val == G_ALERTALTERNATE) {
8454 g_source_remove(compose->exteditor_tag);
8455 g_io_channel_shutdown(compose->exteditor_ch,
8457 g_io_channel_unref(compose->exteditor_ch);
8459 if (kill(pgid, SIGTERM) < 0) perror("kill");
8460 waitpid(compose->exteditor_pid, NULL, 0);
8462 g_warning("Terminated process group id: %d", -pgid);
8463 g_warning("Temporary file: %s",
8464 compose->exteditor_file);
8466 compose_set_ext_editor_sensitive(compose, TRUE);
8468 g_free(compose->exteditor_file);
8469 compose->exteditor_file = NULL;
8470 compose->exteditor_pid = -1;
8471 compose->exteditor_ch = NULL;
8472 compose->exteditor_tag = -1;
8480 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8484 Compose *compose = (Compose *)data;
8487 debug_print(_("Compose: input from monitoring process\n"));
8489 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8491 g_io_channel_shutdown(source, FALSE, NULL);
8492 g_io_channel_unref(source);
8494 waitpid(compose->exteditor_pid, NULL, 0);
8496 if (buf[0] == '0') { /* success */
8497 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8498 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8500 gtk_text_buffer_set_text(buffer, "", -1);
8501 compose_insert_file(compose, compose->exteditor_file);
8502 compose_changed_cb(NULL, compose);
8504 if (claws_unlink(compose->exteditor_file) < 0)
8505 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8506 } else if (buf[0] == '1') { /* failed */
8507 g_warning("Couldn't exec external editor\n");
8508 if (claws_unlink(compose->exteditor_file) < 0)
8509 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8510 } else if (buf[0] == '2') {
8511 g_warning("Couldn't write to file\n");
8512 } else if (buf[0] == '3') {
8513 g_warning("Pipe read failed\n");
8516 compose_set_ext_editor_sensitive(compose, TRUE);
8518 g_free(compose->exteditor_file);
8519 compose->exteditor_file = NULL;
8520 compose->exteditor_pid = -1;
8521 compose->exteditor_ch = NULL;
8522 compose->exteditor_tag = -1;
8527 static void compose_set_ext_editor_sensitive(Compose *compose,
8530 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8531 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8532 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8533 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8534 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8535 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8536 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8538 gtk_widget_set_sensitive(compose->text, sensitive);
8539 if (compose->toolbar->send_btn)
8540 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8541 if (compose->toolbar->sendl_btn)
8542 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8543 if (compose->toolbar->draft_btn)
8544 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8545 if (compose->toolbar->insert_btn)
8546 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8547 if (compose->toolbar->sig_btn)
8548 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8549 if (compose->toolbar->exteditor_btn)
8550 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8551 if (compose->toolbar->linewrap_current_btn)
8552 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8553 if (compose->toolbar->linewrap_all_btn)
8554 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8556 #endif /* G_OS_UNIX */
8559 * compose_undo_state_changed:
8561 * Change the sensivity of the menuentries undo and redo
8563 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8564 gint redo_state, gpointer data)
8566 Compose *compose = (Compose *)data;
8568 switch (undo_state) {
8569 case UNDO_STATE_TRUE:
8570 if (!undostruct->undo_state) {
8571 undostruct->undo_state = TRUE;
8572 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8575 case UNDO_STATE_FALSE:
8576 if (undostruct->undo_state) {
8577 undostruct->undo_state = FALSE;
8578 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8581 case UNDO_STATE_UNCHANGED:
8583 case UNDO_STATE_REFRESH:
8584 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8587 g_warning("Undo state not recognized");
8591 switch (redo_state) {
8592 case UNDO_STATE_TRUE:
8593 if (!undostruct->redo_state) {
8594 undostruct->redo_state = TRUE;
8595 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8598 case UNDO_STATE_FALSE:
8599 if (undostruct->redo_state) {
8600 undostruct->redo_state = FALSE;
8601 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8604 case UNDO_STATE_UNCHANGED:
8606 case UNDO_STATE_REFRESH:
8607 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8610 g_warning("Redo state not recognized");
8615 /* callback functions */
8617 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8618 * includes "non-client" (windows-izm) in calculation, so this calculation
8619 * may not be accurate.
8621 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8622 GtkAllocation *allocation,
8623 GtkSHRuler *shruler)
8625 if (prefs_common.show_ruler) {
8626 gint char_width = 0, char_height = 0;
8627 gint line_width_in_chars;
8629 gtkut_get_font_size(GTK_WIDGET(widget),
8630 &char_width, &char_height);
8631 line_width_in_chars =
8632 (allocation->width - allocation->x) / char_width;
8634 /* got the maximum */
8635 gtk_ruler_set_range(GTK_RULER(shruler),
8636 0.0, line_width_in_chars, 0,
8637 /*line_width_in_chars*/ char_width);
8643 static void account_activated(GtkComboBox *optmenu, gpointer data)
8645 Compose *compose = (Compose *)data;
8648 gchar *folderidentifier;
8649 gint account_id = 0;
8653 /* Get ID of active account in the combo box */
8654 menu = gtk_combo_box_get_model(optmenu);
8655 gtk_combo_box_get_active_iter(optmenu, &iter);
8656 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8658 ac = account_find_from_id(account_id);
8659 g_return_if_fail(ac != NULL);
8661 if (ac != compose->account)
8662 compose_select_account(compose, ac, FALSE);
8664 /* Set message save folder */
8665 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8666 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8668 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8669 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8671 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8672 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8673 folderidentifier = folder_item_get_identifier(account_get_special_folder
8674 (compose->account, F_OUTBOX));
8675 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
8676 g_free(folderidentifier);
8680 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8681 GtkTreeViewColumn *column, Compose *compose)
8683 compose_attach_property(NULL, compose);
8686 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8689 Compose *compose = (Compose *)data;
8690 GtkTreeSelection *attach_selection;
8691 gint attach_nr_selected;
8693 if (!event) return FALSE;
8695 if (event->button == 3) {
8696 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8697 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8699 if (attach_nr_selected > 0)
8701 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8702 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8704 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8705 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8708 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8709 NULL, NULL, event->button, event->time);
8716 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8719 Compose *compose = (Compose *)data;
8721 if (!event) return FALSE;
8723 switch (event->keyval) {
8725 compose_attach_remove_selected(NULL, compose);
8731 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8733 toolbar_comp_set_sensitive(compose, allow);
8734 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8735 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8737 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8739 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8740 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8741 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8743 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8747 static void compose_send_cb(GtkAction *action, gpointer data)
8749 Compose *compose = (Compose *)data;
8751 if (prefs_common.work_offline &&
8752 !inc_offline_should_override(TRUE,
8753 _("Claws Mail needs network access in order "
8754 "to send this email.")))
8757 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8758 g_source_remove(compose->draft_timeout_tag);
8759 compose->draft_timeout_tag = -1;
8762 compose_send(compose);
8765 static void compose_send_later_cb(GtkAction *action, gpointer data)
8767 Compose *compose = (Compose *)data;
8771 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8775 compose_close(compose);
8776 } else if (val == -1) {
8777 alertpanel_error(_("Could not queue message."));
8778 } else if (val == -2) {
8779 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8780 } else if (val == -3) {
8781 if (privacy_peek_error())
8782 alertpanel_error(_("Could not queue message for sending:\n\n"
8783 "Signature failed: %s"), privacy_get_error());
8784 } else if (val == -4) {
8785 alertpanel_error(_("Could not queue message for sending:\n\n"
8786 "Charset conversion failed."));
8787 } else if (val == -5) {
8788 alertpanel_error(_("Could not queue message for sending:\n\n"
8789 "Couldn't get recipient encryption key."));
8790 } else if (val == -6) {
8793 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
8796 #define DRAFTED_AT_EXIT "drafted_at_exit"
8797 static void compose_register_draft(MsgInfo *info)
8799 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8800 DRAFTED_AT_EXIT, NULL);
8801 FILE *fp = fopen(filepath, "ab");
8804 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
8812 gboolean compose_draft (gpointer data, guint action)
8814 Compose *compose = (Compose *)data;
8818 MsgFlags flag = {0, 0};
8819 static gboolean lock = FALSE;
8820 MsgInfo *newmsginfo;
8822 gboolean target_locked = FALSE;
8823 gboolean err = FALSE;
8825 if (lock) return FALSE;
8827 if (compose->sending)
8830 draft = account_get_special_folder(compose->account, F_DRAFT);
8831 g_return_val_if_fail(draft != NULL, FALSE);
8833 if (!g_mutex_trylock(compose->mutex)) {
8834 /* we don't want to lock the mutex once it's available,
8835 * because as the only other part of compose.c locking
8836 * it is compose_close - which means once unlocked,
8837 * the compose struct will be freed */
8838 debug_print("couldn't lock mutex, probably sending\n");
8844 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
8845 G_DIR_SEPARATOR, compose);
8846 if ((fp = g_fopen(tmp, "wb")) == NULL) {
8847 FILE_OP_ERROR(tmp, "fopen");
8851 /* chmod for security */
8852 if (change_file_mode_rw(fp, tmp) < 0) {
8853 FILE_OP_ERROR(tmp, "chmod");
8854 g_warning("can't change file mode\n");
8857 /* Save draft infos */
8858 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
8859 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
8861 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
8862 gchar *savefolderid;
8864 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8865 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
8866 g_free(savefolderid);
8868 if (compose->return_receipt) {
8869 err |= (fprintf(fp, "RRCPT:1\n") < 0);
8871 if (compose->privacy_system) {
8872 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
8873 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
8874 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
8877 /* Message-ID of message replying to */
8878 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
8881 folderid = folder_item_get_identifier(compose->replyinfo->folder);
8882 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
8885 /* Message-ID of message forwarding to */
8886 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
8889 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
8890 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
8894 /* end of headers */
8895 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
8902 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
8906 if (fclose(fp) == EOF) {
8910 if (compose->targetinfo) {
8911 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
8912 flag.perm_flags = target_locked?MSG_LOCKED:0;
8914 flag.tmp_flags = MSG_DRAFT;
8916 folder_item_scan(draft);
8917 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
8918 MsgInfo *tmpinfo = NULL;
8919 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
8920 if (compose->msgid) {
8921 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
8924 msgnum = tmpinfo->msgnum;
8925 procmsg_msginfo_free(tmpinfo);
8926 debug_print("got draft msgnum %d from scanning\n", msgnum);
8928 debug_print("didn't get draft msgnum after scanning\n");
8931 debug_print("got draft msgnum %d from adding\n", msgnum);
8937 if (action != COMPOSE_AUTO_SAVE) {
8938 if (action != COMPOSE_DRAFT_FOR_EXIT)
8939 alertpanel_error(_("Could not save draft."));
8942 gtkut_window_popup(compose->window);
8943 val = alertpanel_full(_("Could not save draft"),
8944 _("Could not save draft.\n"
8945 "Do you want to cancel exit or discard this email?"),
8946 _("_Cancel exit"), _("_Discard email"), NULL,
8947 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
8948 if (val == G_ALERTALTERNATE) {
8950 g_mutex_unlock(compose->mutex); /* must be done before closing */
8951 compose_close(compose);
8955 g_mutex_unlock(compose->mutex); /* must be done before closing */
8964 if (compose->mode == COMPOSE_REEDIT) {
8965 compose_remove_reedit_target(compose, TRUE);
8968 newmsginfo = folder_item_get_msginfo(draft, msgnum);
8971 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
8973 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
8975 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
8976 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
8977 procmsg_msginfo_set_flags(newmsginfo, 0,
8978 MSG_HAS_ATTACHMENT);
8980 if (action == COMPOSE_DRAFT_FOR_EXIT) {
8981 compose_register_draft(newmsginfo);
8983 procmsg_msginfo_free(newmsginfo);
8986 folder_item_scan(draft);
8988 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
8990 g_mutex_unlock(compose->mutex); /* must be done before closing */
8991 compose_close(compose);
8997 path = folder_item_fetch_msg(draft, msgnum);
8999 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9002 if (g_stat(path, &s) < 0) {
9003 FILE_OP_ERROR(path, "stat");
9009 procmsg_msginfo_free(compose->targetinfo);
9010 compose->targetinfo = procmsg_msginfo_new();
9011 compose->targetinfo->msgnum = msgnum;
9012 compose->targetinfo->size = (goffset)s.st_size;
9013 compose->targetinfo->mtime = s.st_mtime;
9014 compose->targetinfo->folder = draft;
9016 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9017 compose->mode = COMPOSE_REEDIT;
9019 if (action == COMPOSE_AUTO_SAVE) {
9020 compose->autosaved_draft = compose->targetinfo;
9022 compose->modified = FALSE;
9023 compose_set_title(compose);
9027 g_mutex_unlock(compose->mutex);
9031 void compose_clear_exit_drafts(void)
9033 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9034 DRAFTED_AT_EXIT, NULL);
9035 if (is_file_exist(filepath))
9036 claws_unlink(filepath);
9041 void compose_reopen_exit_drafts(void)
9043 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9044 DRAFTED_AT_EXIT, NULL);
9045 FILE *fp = fopen(filepath, "rb");
9049 while (fgets(buf, sizeof(buf), fp)) {
9050 gchar **parts = g_strsplit(buf, "\t", 2);
9051 const gchar *folder = parts[0];
9052 int msgnum = parts[1] ? atoi(parts[1]):-1;
9054 if (folder && *folder && msgnum > -1) {
9055 FolderItem *item = folder_find_item_from_identifier(folder);
9056 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9058 compose_reedit(info, FALSE);
9065 compose_clear_exit_drafts();
9068 static void compose_save_cb(GtkAction *action, gpointer data)
9070 compose_draft(data, COMPOSE_KEEP_EDITING);
9073 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9075 if (compose && file_list) {
9078 for ( tmp = file_list; tmp; tmp = tmp->next) {
9079 gchar *file = (gchar *) tmp->data;
9080 gchar *utf8_filename = conv_filename_to_utf8(file);
9081 compose_attach_append(compose, file, utf8_filename, NULL);
9082 compose_changed_cb(NULL, compose);
9087 g_free(utf8_filename);
9092 static void compose_attach_cb(GtkAction *action, gpointer data)
9094 Compose *compose = (Compose *)data;
9097 if (compose->redirect_filename != NULL)
9100 file_list = filesel_select_multiple_files_open(_("Select file"));
9103 compose_attach_from_list(compose, file_list, TRUE);
9104 g_list_free(file_list);
9108 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9110 Compose *compose = (Compose *)data;
9113 file_list = filesel_select_multiple_files_open(_("Select file"));
9118 for ( tmp = file_list; tmp; tmp = tmp->next) {
9119 gchar *file = (gchar *) tmp->data;
9120 gchar *filedup = g_strdup(file);
9121 gchar *shortfile = g_path_get_basename(filedup);
9122 ComposeInsertResult res;
9124 res = compose_insert_file(compose, file);
9125 if (res == COMPOSE_INSERT_READ_ERROR) {
9126 alertpanel_error(_("File '%s' could not be read."), shortfile);
9127 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9128 alertpanel_error(_("File '%s' contained invalid characters\n"
9129 "for the current encoding, insertion may be incorrect."), shortfile);
9135 g_list_free(file_list);
9139 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9141 Compose *compose = (Compose *)data;
9143 compose_insert_sig(compose, FALSE);
9146 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9150 Compose *compose = (Compose *)data;
9152 gtkut_widget_get_uposition(widget, &x, &y);
9153 prefs_common.compose_x = x;
9154 prefs_common.compose_y = y;
9156 if (compose->sending || compose->updating)
9158 compose_close_cb(NULL, compose);
9162 void compose_close_toolbar(Compose *compose)
9164 compose_close_cb(NULL, compose);
9167 static void compose_close_cb(GtkAction *action, gpointer data)
9169 Compose *compose = (Compose *)data;
9173 if (compose->exteditor_tag != -1) {
9174 if (!compose_ext_editor_kill(compose))
9179 if (compose->modified) {
9180 if (!g_mutex_trylock(compose->mutex)) {
9181 /* we don't want to lock the mutex once it's available,
9182 * because as the only other part of compose.c locking
9183 * it is compose_close - which means once unlocked,
9184 * the compose struct will be freed */
9185 debug_print("couldn't lock mutex, probably sending\n");
9188 val = alertpanel(_("Discard message"),
9189 _("This message has been modified. Discard it?"),
9190 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9191 g_mutex_unlock(compose->mutex);
9193 case G_ALERTDEFAULT:
9194 if (prefs_common.autosave)
9195 compose_remove_draft(compose);
9197 case G_ALERTALTERNATE:
9198 compose_draft(data, COMPOSE_QUIT_EDITING);
9205 compose_close(compose);
9208 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9210 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9211 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9212 Compose *compose = (Compose *) data;
9215 compose->out_encoding = (CharSet)value;
9218 static void compose_address_cb(GtkAction *action, gpointer data)
9220 Compose *compose = (Compose *)data;
9222 addressbook_open(compose);
9225 static void about_show_cb(GtkAction *action, gpointer data)
9230 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9232 Compose *compose = (Compose *)data;
9237 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9238 g_return_if_fail(tmpl != NULL);
9240 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9242 val = alertpanel(_("Apply template"), msg,
9243 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9246 if (val == G_ALERTDEFAULT)
9247 compose_template_apply(compose, tmpl, TRUE);
9248 else if (val == G_ALERTALTERNATE)
9249 compose_template_apply(compose, tmpl, FALSE);
9252 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9254 Compose *compose = (Compose *)data;
9256 compose_exec_ext_editor(compose);
9259 static void compose_undo_cb(GtkAction *action, gpointer data)
9261 Compose *compose = (Compose *)data;
9262 gboolean prev_autowrap = compose->autowrap;
9264 compose->autowrap = FALSE;
9265 undo_undo(compose->undostruct);
9266 compose->autowrap = prev_autowrap;
9269 static void compose_redo_cb(GtkAction *action, gpointer data)
9271 Compose *compose = (Compose *)data;
9272 gboolean prev_autowrap = compose->autowrap;
9274 compose->autowrap = FALSE;
9275 undo_redo(compose->undostruct);
9276 compose->autowrap = prev_autowrap;
9279 static void entry_cut_clipboard(GtkWidget *entry)
9281 if (GTK_IS_EDITABLE(entry))
9282 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9283 else if (GTK_IS_TEXT_VIEW(entry))
9284 gtk_text_buffer_cut_clipboard(
9285 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9286 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9290 static void entry_copy_clipboard(GtkWidget *entry)
9292 if (GTK_IS_EDITABLE(entry))
9293 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9294 else if (GTK_IS_TEXT_VIEW(entry))
9295 gtk_text_buffer_copy_clipboard(
9296 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9297 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9300 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9301 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9303 if (GTK_IS_TEXT_VIEW(entry)) {
9304 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9305 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9306 GtkTextIter start_iter, end_iter;
9308 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9310 if (contents == NULL)
9313 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9315 /* we shouldn't delete the selection when middle-click-pasting, or we
9316 * can't mid-click-paste our own selection */
9317 if (clip != GDK_SELECTION_PRIMARY) {
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 gtk_widget_ref(compose->notebook);
9827 gtk_widget_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 gtk_widget_unref(compose->notebook);
9833 gtk_widget_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 gtk_widget_ref(compose->notebook);
9844 gtk_widget_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 gtk_widget_unref(compose->notebook);
9850 gtk_widget_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);