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_remove_submenu(GTK_MENU_ITEM(parent_item));
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);
6690 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
6692 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
6693 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
6695 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
6697 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
6698 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
6699 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
6702 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
6703 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
6704 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6705 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
6706 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
6707 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
6708 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6709 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
6710 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6711 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
6714 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
6715 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
6716 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6718 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
6719 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
6720 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
6722 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
6723 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
6724 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
6725 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
6727 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
6729 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
6730 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
6731 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
6732 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
6733 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
6734 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
6735 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
6736 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
6737 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
6738 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
6739 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
6740 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
6741 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
6742 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
6743 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
6745 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6747 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
6748 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
6749 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
6750 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
6752 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6754 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
6758 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
6759 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
6760 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
6761 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
6762 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
6763 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
6767 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
6768 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
6769 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
6770 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
6771 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
6773 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6774 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
6775 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6776 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
6777 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
6780 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6781 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
6782 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
6783 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
6784 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
6785 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
6786 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
6788 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6789 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
6790 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6791 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
6792 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6794 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
6796 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
6797 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6798 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
6799 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
6800 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6802 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
6803 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)
6804 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)
6805 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
6807 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
6809 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
6810 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)
6811 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)
6813 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
6815 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
6816 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)
6817 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
6819 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
6820 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)
6821 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
6823 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
6825 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
6826 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)
6827 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
6828 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
6829 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
6831 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
6832 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)
6833 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)
6834 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
6835 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
6837 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
6838 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
6839 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
6840 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
6841 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
6843 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
6844 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
6845 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)
6847 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
6848 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
6849 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
6853 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
6854 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
6855 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
6856 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6857 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
6858 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6861 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
6863 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
6864 gtk_widget_show_all(menubar);
6866 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
6867 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
6869 if (prefs_common.toolbar_detachable) {
6870 handlebox = gtk_handle_box_new();
6872 handlebox = gtk_hbox_new(FALSE, 0);
6874 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
6876 gtk_widget_realize(handlebox);
6878 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
6881 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
6885 vbox2 = gtk_vbox_new(FALSE, 2);
6886 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
6887 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
6890 notebook = gtk_notebook_new();
6891 gtk_widget_set_size_request(notebook, -1, 130);
6892 gtk_widget_show(notebook);
6894 /* header labels and entries */
6895 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6896 compose_create_header(compose),
6897 gtk_label_new_with_mnemonic(_("Hea_der")));
6898 /* attachment list */
6899 attach_hbox = gtk_hbox_new(FALSE, 0);
6900 gtk_widget_show(attach_hbox);
6902 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
6903 gtk_widget_show(attach_lab1);
6904 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
6906 attach_lab2 = gtk_label_new("");
6907 gtk_widget_show(attach_lab2);
6908 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
6910 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6911 compose_create_attach(compose),
6914 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6915 compose_create_others(compose),
6916 gtk_label_new_with_mnemonic(_("Othe_rs")));
6919 subject_hbox = gtk_hbox_new(FALSE, 0);
6920 gtk_widget_show(subject_hbox);
6922 subject_frame = gtk_frame_new(NULL);
6923 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
6924 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
6925 gtk_widget_show(subject_frame);
6927 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
6928 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
6929 gtk_widget_show(subject);
6931 label = gtk_label_new(_("Subject:"));
6932 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
6933 gtk_widget_show(label);
6935 subject_entry = gtk_entry_new();
6936 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
6937 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
6938 G_CALLBACK(compose_grab_focus_cb), compose);
6939 gtk_widget_show(subject_entry);
6940 compose->subject_entry = subject_entry;
6941 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
6943 edit_vbox = gtk_vbox_new(FALSE, 0);
6945 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
6948 ruler_hbox = gtk_hbox_new(FALSE, 0);
6949 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
6951 ruler = gtk_shruler_new();
6952 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
6953 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
6957 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6958 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
6959 GTK_POLICY_AUTOMATIC,
6960 GTK_POLICY_AUTOMATIC);
6961 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
6963 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
6964 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
6966 text = gtk_text_view_new();
6967 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
6968 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
6969 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
6970 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
6971 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
6973 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
6975 g_signal_connect_after(G_OBJECT(text), "size_allocate",
6976 G_CALLBACK(compose_edit_size_alloc),
6978 g_signal_connect(G_OBJECT(buffer), "changed",
6979 G_CALLBACK(compose_changed_cb), compose);
6980 g_signal_connect(G_OBJECT(text), "grab_focus",
6981 G_CALLBACK(compose_grab_focus_cb), compose);
6982 g_signal_connect(G_OBJECT(buffer), "insert_text",
6983 G_CALLBACK(text_inserted), compose);
6984 g_signal_connect(G_OBJECT(text), "button_press_event",
6985 G_CALLBACK(text_clicked), compose);
6987 g_signal_connect(G_OBJECT(text), "popup-menu",
6988 G_CALLBACK(compose_popup_menu), compose);
6990 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
6991 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6992 g_signal_connect(G_OBJECT(text), "tap-and-hold",
6993 G_CALLBACK(compose_popup_menu), compose);
6995 g_signal_connect(G_OBJECT(subject_entry), "changed",
6996 G_CALLBACK(compose_changed_cb), compose);
6999 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7000 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7001 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7002 g_signal_connect(G_OBJECT(text), "drag_data_received",
7003 G_CALLBACK(compose_insert_drag_received_cb),
7005 g_signal_connect(G_OBJECT(text), "drag-drop",
7006 G_CALLBACK(compose_drag_drop),
7008 gtk_widget_show_all(vbox);
7010 /* pane between attach clist and text */
7011 paned = gtk_vpaned_new();
7012 gtk_paned_set_gutter_size(GTK_PANED(paned), 12);
7013 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7015 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7016 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7018 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7020 gtk_paned_add1(GTK_PANED(paned), notebook);
7021 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7022 gtk_widget_show_all(paned);
7025 if (prefs_common.textfont) {
7026 PangoFontDescription *font_desc;
7028 font_desc = pango_font_description_from_string
7029 (prefs_common.textfont);
7031 gtk_widget_modify_font(text, font_desc);
7032 pango_font_description_free(font_desc);
7036 gtk_action_group_add_actions(action_group, compose_popup_entries,
7037 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7038 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7039 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7040 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7041 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7042 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7043 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7045 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7047 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7048 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7049 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7051 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7053 undostruct = undo_init(text);
7054 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7057 address_completion_start(window);
7059 compose->window = window;
7060 compose->vbox = vbox;
7061 compose->menubar = menubar;
7062 compose->handlebox = handlebox;
7064 compose->vbox2 = vbox2;
7066 compose->paned = paned;
7068 compose->attach_label = attach_lab2;
7070 compose->notebook = notebook;
7071 compose->edit_vbox = edit_vbox;
7072 compose->ruler_hbox = ruler_hbox;
7073 compose->ruler = ruler;
7074 compose->scrolledwin = scrolledwin;
7075 compose->text = text;
7077 compose->focused_editable = NULL;
7079 compose->popupmenu = popupmenu;
7081 compose->tmpl_menu = tmpl_menu;
7083 compose->mode = mode;
7084 compose->rmode = mode;
7086 compose->targetinfo = NULL;
7087 compose->replyinfo = NULL;
7088 compose->fwdinfo = NULL;
7090 compose->replyto = NULL;
7092 compose->bcc = NULL;
7093 compose->followup_to = NULL;
7095 compose->ml_post = NULL;
7097 compose->inreplyto = NULL;
7098 compose->references = NULL;
7099 compose->msgid = NULL;
7100 compose->boundary = NULL;
7102 compose->autowrap = prefs_common.autowrap;
7104 compose->use_signing = FALSE;
7105 compose->use_encryption = FALSE;
7106 compose->privacy_system = NULL;
7108 compose->modified = FALSE;
7110 compose->return_receipt = FALSE;
7112 compose->to_list = NULL;
7113 compose->newsgroup_list = NULL;
7115 compose->undostruct = undostruct;
7117 compose->sig_str = NULL;
7119 compose->exteditor_file = NULL;
7120 compose->exteditor_pid = -1;
7121 compose->exteditor_tag = -1;
7122 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7125 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7126 if (mode != COMPOSE_REDIRECT) {
7127 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7128 strcmp(prefs_common.dictionary, "")) {
7129 gtkaspell = gtkaspell_new(prefs_common.aspell_path,
7130 prefs_common.dictionary,
7131 prefs_common.alt_dictionary,
7132 conv_get_locale_charset_str(),
7133 prefs_common.misspelled_col,
7134 prefs_common.check_while_typing,
7135 prefs_common.recheck_when_changing_dict,
7136 prefs_common.use_alternate,
7137 prefs_common.use_both_dicts,
7138 GTK_TEXT_VIEW(text),
7139 GTK_WINDOW(compose->window),
7140 compose_spell_menu_changed,
7143 alertpanel_error(_("Spell checker could not "
7145 gtkaspell_checkers_strerror());
7146 gtkaspell_checkers_reset_error();
7148 if (!gtkaspell_set_sug_mode(gtkaspell,
7149 prefs_common.aspell_sugmode)) {
7150 debug_print("Aspell: could not set "
7151 "suggestion mode %s\n",
7152 gtkaspell_checkers_strerror());
7153 gtkaspell_checkers_reset_error();
7156 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7160 compose->gtkaspell = gtkaspell;
7161 compose_spell_menu_changed(compose);
7164 compose_select_account(compose, account, TRUE);
7166 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7168 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7169 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7171 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7172 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7174 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7175 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7177 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7178 if (account->protocol != A_NNTP)
7179 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(compose->header_last->combo)->child),
7180 prefs_common_translated_header_name("To:"));
7182 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(compose->header_last->combo)->child),
7183 prefs_common_translated_header_name("Newsgroups:"));
7185 addressbook_set_target_compose(compose);
7187 if (mode != COMPOSE_REDIRECT)
7188 compose_set_template_menu(compose);
7190 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7193 compose_list = g_list_append(compose_list, compose);
7195 if (!prefs_common.show_ruler)
7196 gtk_widget_hide(ruler_hbox);
7198 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7201 compose->priority = PRIORITY_NORMAL;
7202 compose_update_priority_menu_item(compose);
7204 compose_set_out_encoding(compose);
7207 compose_update_actions_menu(compose);
7209 /* Privacy Systems menu */
7210 compose_update_privacy_systems_menu(compose);
7212 activate_privacy_system(compose, account, TRUE);
7213 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7215 gtk_widget_realize(window);
7217 gtk_widget_show(window);
7219 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7220 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7227 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7232 GtkWidget *optmenubox;
7235 GtkWidget *from_name = NULL;
7236 #if !(GTK_CHECK_VERSION(2,12,0))
7237 GtkTooltips *tips = compose->tooltips;
7240 gint num = 0, def_menu = 0;
7242 accounts = account_get_list();
7243 g_return_val_if_fail(accounts != NULL, NULL);
7245 optmenubox = gtk_event_box_new();
7246 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7247 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7249 hbox = gtk_hbox_new(FALSE, 6);
7250 from_name = gtk_entry_new();
7252 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7253 G_CALLBACK(compose_grab_focus_cb), compose);
7255 for (; accounts != NULL; accounts = accounts->next, num++) {
7256 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7257 gchar *name, *from = NULL;
7259 if (ac == compose->account) def_menu = num;
7261 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7264 if (ac == compose->account) {
7265 if (ac->name && *ac->name) {
7267 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7268 from = g_strdup_printf("%s <%s>",
7270 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7272 from = g_strdup_printf("%s",
7274 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7277 COMBOBOX_ADD(menu, name, ac->account_id);
7282 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7284 g_signal_connect(G_OBJECT(optmenu), "changed",
7285 G_CALLBACK(account_activated),
7287 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7288 G_CALLBACK(compose_entry_popup_extend),
7291 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7292 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7294 CLAWS_SET_TIP(optmenubox,
7295 _("Account to use for this email"));
7296 CLAWS_SET_TIP(from_name,
7297 _("Sender address to be used"));
7299 compose->from_name = from_name;
7304 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7306 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7307 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7308 Compose *compose = (Compose *) data;
7310 printf("activated %d\n", value);
7311 compose->priority = value;
7315 static void compose_reply_change_mode(Compose *compose,
7318 gboolean was_modified = compose->modified;
7320 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7322 g_return_if_fail(compose->replyinfo != NULL);
7324 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7326 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7328 if (action == COMPOSE_REPLY_TO_ALL)
7330 if (action == COMPOSE_REPLY_TO_SENDER)
7332 if (action == COMPOSE_REPLY_TO_LIST)
7335 compose_remove_header_entries(compose);
7336 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7337 if (compose->account->set_autocc && compose->account->auto_cc)
7338 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7340 if (compose->account->set_autobcc && compose->account->auto_bcc)
7341 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7343 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7344 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7345 compose_show_first_last_header(compose, TRUE);
7346 compose->modified = was_modified;
7347 compose_set_title(compose);
7350 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7352 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7353 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7354 Compose *compose = (Compose *) data;
7357 compose_reply_change_mode(compose, value);
7360 static void compose_update_priority_menu_item(Compose * compose)
7362 GtkWidget *menuitem = NULL;
7363 switch (compose->priority) {
7364 case PRIORITY_HIGHEST:
7365 menuitem = gtk_ui_manager_get_widget
7366 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7369 menuitem = gtk_ui_manager_get_widget
7370 (compose->ui_manager, "/Menu/Options/Priority/High");
7372 case PRIORITY_NORMAL:
7373 menuitem = gtk_ui_manager_get_widget
7374 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7377 menuitem = gtk_ui_manager_get_widget
7378 (compose->ui_manager, "/Menu/Options/Priority/Low");
7380 case PRIORITY_LOWEST:
7381 menuitem = gtk_ui_manager_get_widget
7382 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7385 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7388 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7390 Compose *compose = (Compose *) data;
7392 gboolean can_sign = FALSE, can_encrypt = FALSE;
7394 g_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7396 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7399 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7400 g_free(compose->privacy_system);
7401 compose->privacy_system = NULL;
7402 if (systemid != NULL) {
7403 compose->privacy_system = g_strdup(systemid);
7405 can_sign = privacy_system_can_sign(systemid);
7406 can_encrypt = privacy_system_can_encrypt(systemid);
7409 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7411 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7412 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7415 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7417 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7418 GtkWidget *menuitem = NULL;
7420 gboolean can_sign = FALSE, can_encrypt = FALSE;
7421 gboolean found = FALSE;
7423 if (compose->privacy_system != NULL) {
7425 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7426 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7427 g_return_if_fail(menuitem != NULL);
7429 amenu = GTK_MENU_SHELL(menuitem)->children;
7431 while (amenu != NULL) {
7432 GList *alist = amenu->next;
7434 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7435 if (systemid != NULL) {
7436 if (strcmp(systemid, compose->privacy_system) == 0) {
7437 menuitem = GTK_WIDGET(amenu->data);
7439 can_sign = privacy_system_can_sign(systemid);
7440 can_encrypt = privacy_system_can_encrypt(systemid);
7444 } else if (strlen(compose->privacy_system) == 0) {
7445 menuitem = GTK_WIDGET(amenu->data);
7448 can_encrypt = FALSE;
7455 if (menuitem != NULL)
7456 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7458 if (warn && !found && strlen(compose->privacy_system)) {
7459 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7460 "will not be able to sign or encrypt this message."),
7461 compose->privacy_system);
7465 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7466 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7469 static void compose_set_out_encoding(Compose *compose)
7471 CharSet out_encoding;
7472 const gchar *branch = NULL;
7473 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7475 switch(out_encoding) {
7476 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7477 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7478 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7479 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7480 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7481 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7482 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7483 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7484 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7485 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7486 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7487 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7488 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7489 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7490 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7491 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7492 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7493 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7494 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7495 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7496 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7497 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7498 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7499 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7500 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7501 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7502 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7503 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7504 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7505 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7506 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7507 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7509 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7512 static void compose_set_template_menu(Compose *compose)
7514 GSList *tmpl_list, *cur;
7518 tmpl_list = template_get_config();
7520 menu = gtk_menu_new();
7522 gtk_menu_set_accel_group (GTK_MENU (menu),
7523 gtk_ui_manager_get_accel_group(compose->ui_manager));
7524 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7525 Template *tmpl = (Template *)cur->data;
7526 gchar *accel_path = NULL;
7527 item = gtk_menu_item_new_with_label(tmpl->name);
7528 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7529 g_signal_connect(G_OBJECT(item), "activate",
7530 G_CALLBACK(compose_template_activate_cb),
7532 g_object_set_data(G_OBJECT(item), "template", tmpl);
7533 gtk_widget_show(item);
7534 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7535 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7539 gtk_widget_show(menu);
7540 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7543 void compose_update_actions_menu(Compose *compose)
7545 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7548 static void compose_update_privacy_systems_menu(Compose *compose)
7550 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7551 GSList *systems, *cur;
7553 GtkWidget *system_none;
7555 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7556 GtkWidget *privacy_menu = gtk_menu_new();
7558 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7559 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7561 g_signal_connect(G_OBJECT(system_none), "activate",
7562 G_CALLBACK(compose_set_privacy_system_cb), compose);
7564 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7565 gtk_widget_show(system_none);
7567 systems = privacy_get_system_ids();
7568 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7569 gchar *systemid = cur->data;
7571 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7572 widget = gtk_radio_menu_item_new_with_label(group,
7573 privacy_system_get_name(systemid));
7574 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7575 g_strdup(systemid), g_free);
7576 g_signal_connect(G_OBJECT(widget), "activate",
7577 G_CALLBACK(compose_set_privacy_system_cb), compose);
7579 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7580 gtk_widget_show(widget);
7583 g_slist_free(systems);
7584 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7585 gtk_widget_show_all(privacy_menu);
7586 gtk_widget_show_all(privacy_menuitem);
7589 void compose_reflect_prefs_all(void)
7594 for (cur = compose_list; cur != NULL; cur = cur->next) {
7595 compose = (Compose *)cur->data;
7596 compose_set_template_menu(compose);
7600 void compose_reflect_prefs_pixmap_theme(void)
7605 for (cur = compose_list; cur != NULL; cur = cur->next) {
7606 compose = (Compose *)cur->data;
7607 toolbar_update(TOOLBAR_COMPOSE, compose);
7611 static const gchar *compose_quote_char_from_context(Compose *compose)
7613 const gchar *qmark = NULL;
7615 g_return_val_if_fail(compose != NULL, NULL);
7617 switch (compose->mode) {
7618 /* use forward-specific quote char */
7619 case COMPOSE_FORWARD:
7620 case COMPOSE_FORWARD_AS_ATTACH:
7621 case COMPOSE_FORWARD_INLINE:
7622 if (compose->folder && compose->folder->prefs &&
7623 compose->folder->prefs->forward_with_format)
7624 qmark = compose->folder->prefs->forward_quotemark;
7625 else if (compose->account->forward_with_format)
7626 qmark = compose->account->forward_quotemark;
7628 qmark = prefs_common.fw_quotemark;
7631 /* use reply-specific quote char in all other modes */
7633 if (compose->folder && compose->folder->prefs &&
7634 compose->folder->prefs->reply_with_format)
7635 qmark = compose->folder->prefs->reply_quotemark;
7636 else if (compose->account->reply_with_format)
7637 qmark = compose->account->reply_quotemark;
7639 qmark = prefs_common.quotemark;
7643 if (qmark == NULL || *qmark == '\0')
7649 static void compose_template_apply(Compose *compose, Template *tmpl,
7653 GtkTextBuffer *buffer;
7657 gchar *parsed_str = NULL;
7658 gint cursor_pos = 0;
7659 const gchar *err_msg = _("Template body format error at line %d.");
7662 /* process the body */
7664 text = GTK_TEXT_VIEW(compose->text);
7665 buffer = gtk_text_view_get_buffer(text);
7668 qmark = compose_quote_char_from_context(compose);
7670 if (compose->replyinfo != NULL) {
7673 gtk_text_buffer_set_text(buffer, "", -1);
7674 mark = gtk_text_buffer_get_insert(buffer);
7675 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7677 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7678 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7680 } else if (compose->fwdinfo != NULL) {
7683 gtk_text_buffer_set_text(buffer, "", -1);
7684 mark = gtk_text_buffer_get_insert(buffer);
7685 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7687 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7688 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7691 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7693 GtkTextIter start, end;
7696 gtk_text_buffer_get_start_iter(buffer, &start);
7697 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7698 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7700 /* clear the buffer now */
7702 gtk_text_buffer_set_text(buffer, "", -1);
7704 parsed_str = compose_quote_fmt(compose, dummyinfo,
7705 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7706 procmsg_msginfo_free( dummyinfo );
7712 gtk_text_buffer_set_text(buffer, "", -1);
7713 mark = gtk_text_buffer_get_insert(buffer);
7714 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7717 if (replace && parsed_str && compose->account->auto_sig)
7718 compose_insert_sig(compose, FALSE);
7720 if (replace && parsed_str) {
7721 gtk_text_buffer_get_start_iter(buffer, &iter);
7722 gtk_text_buffer_place_cursor(buffer, &iter);
7726 cursor_pos = quote_fmt_get_cursor_pos();
7727 compose->set_cursor_pos = cursor_pos;
7728 if (cursor_pos == -1)
7730 gtk_text_buffer_get_start_iter(buffer, &iter);
7731 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7732 gtk_text_buffer_place_cursor(buffer, &iter);
7735 /* process the other fields */
7737 compose_template_apply_fields(compose, tmpl);
7738 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7739 quote_fmt_reset_vartable();
7740 compose_changed_cb(NULL, compose);
7743 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7745 MsgInfo* dummyinfo = NULL;
7746 MsgInfo *msginfo = NULL;
7749 if (compose->replyinfo != NULL)
7750 msginfo = compose->replyinfo;
7751 else if (compose->fwdinfo != NULL)
7752 msginfo = compose->fwdinfo;
7754 dummyinfo = compose_msginfo_new_from_compose(compose);
7755 msginfo = dummyinfo;
7758 if (tmpl->from && *tmpl->from != '\0') {
7760 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7761 compose->gtkaspell);
7763 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7765 quote_fmt_scan_string(tmpl->from);
7768 buf = quote_fmt_get_buffer();
7770 alertpanel_error(_("Template From format error."));
7772 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
7776 if (tmpl->to && *tmpl->to != '\0') {
7778 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7779 compose->gtkaspell);
7781 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7783 quote_fmt_scan_string(tmpl->to);
7786 buf = quote_fmt_get_buffer();
7788 alertpanel_error(_("Template To format error."));
7790 compose_entry_append(compose, buf, COMPOSE_TO);
7794 if (tmpl->cc && *tmpl->cc != '\0') {
7796 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7797 compose->gtkaspell);
7799 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7801 quote_fmt_scan_string(tmpl->cc);
7804 buf = quote_fmt_get_buffer();
7806 alertpanel_error(_("Template Cc format error."));
7808 compose_entry_append(compose, buf, COMPOSE_CC);
7812 if (tmpl->bcc && *tmpl->bcc != '\0') {
7814 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7815 compose->gtkaspell);
7817 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7819 quote_fmt_scan_string(tmpl->bcc);
7822 buf = quote_fmt_get_buffer();
7824 alertpanel_error(_("Template Bcc format error."));
7826 compose_entry_append(compose, buf, COMPOSE_BCC);
7830 /* process the subject */
7831 if (tmpl->subject && *tmpl->subject != '\0') {
7833 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7834 compose->gtkaspell);
7836 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7838 quote_fmt_scan_string(tmpl->subject);
7841 buf = quote_fmt_get_buffer();
7843 alertpanel_error(_("Template subject format error."));
7845 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
7849 procmsg_msginfo_free( dummyinfo );
7852 static void compose_destroy(Compose *compose)
7854 GtkTextBuffer *buffer;
7855 GtkClipboard *clipboard;
7857 compose_list = g_list_remove(compose_list, compose);
7859 if (compose->updating) {
7860 debug_print("danger, not destroying anything now\n");
7861 compose->deferred_destroy = TRUE;
7864 /* NOTE: address_completion_end() does nothing with the window
7865 * however this may change. */
7866 address_completion_end(compose->window);
7868 slist_free_strings(compose->to_list);
7869 g_slist_free(compose->to_list);
7870 slist_free_strings(compose->newsgroup_list);
7871 g_slist_free(compose->newsgroup_list);
7872 slist_free_strings(compose->header_list);
7873 g_slist_free(compose->header_list);
7875 procmsg_msginfo_free(compose->targetinfo);
7876 procmsg_msginfo_free(compose->replyinfo);
7877 procmsg_msginfo_free(compose->fwdinfo);
7879 g_free(compose->replyto);
7880 g_free(compose->cc);
7881 g_free(compose->bcc);
7882 g_free(compose->newsgroups);
7883 g_free(compose->followup_to);
7885 g_free(compose->ml_post);
7887 g_free(compose->inreplyto);
7888 g_free(compose->references);
7889 g_free(compose->msgid);
7890 g_free(compose->boundary);
7892 g_free(compose->redirect_filename);
7893 if (compose->undostruct)
7894 undo_destroy(compose->undostruct);
7896 g_free(compose->sig_str);
7898 g_free(compose->exteditor_file);
7900 g_free(compose->orig_charset);
7902 g_free(compose->privacy_system);
7904 if (addressbook_get_target_compose() == compose)
7905 addressbook_set_target_compose(NULL);
7908 if (compose->gtkaspell) {
7909 gtkaspell_delete(compose->gtkaspell);
7910 compose->gtkaspell = NULL;
7914 prefs_common.compose_width = compose->scrolledwin->allocation.width;
7915 prefs_common.compose_height = compose->window->allocation.height;
7917 if (!gtk_widget_get_parent(compose->paned))
7918 gtk_widget_destroy(compose->paned);
7919 gtk_widget_destroy(compose->popupmenu);
7921 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
7922 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7923 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
7925 gtk_widget_destroy(compose->window);
7926 toolbar_destroy(compose->toolbar);
7927 g_free(compose->toolbar);
7928 g_mutex_free(compose->mutex);
7932 static void compose_attach_info_free(AttachInfo *ainfo)
7934 g_free(ainfo->file);
7935 g_free(ainfo->content_type);
7936 g_free(ainfo->name);
7940 static void compose_attach_update_label(Compose *compose)
7945 GtkTreeModel *model;
7950 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
7951 if(!gtk_tree_model_get_iter_first(model, &iter)) {
7952 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
7956 while(gtk_tree_model_iter_next(model, &iter))
7959 text = g_strdup_printf("(%d)", i);
7960 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
7964 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
7966 Compose *compose = (Compose *)data;
7967 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
7968 GtkTreeSelection *selection;
7970 GtkTreeModel *model;
7972 selection = gtk_tree_view_get_selection(tree_view);
7973 sel = gtk_tree_selection_get_selected_rows(selection, &model);
7978 for (cur = sel; cur != NULL; cur = cur->next) {
7979 GtkTreePath *path = cur->data;
7980 GtkTreeRowReference *ref = gtk_tree_row_reference_new
7983 gtk_tree_path_free(path);
7986 for (cur = sel; cur != NULL; cur = cur->next) {
7987 GtkTreeRowReference *ref = cur->data;
7988 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
7991 if (gtk_tree_model_get_iter(model, &iter, path))
7992 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
7994 gtk_tree_path_free(path);
7995 gtk_tree_row_reference_free(ref);
7999 compose_attach_update_label(compose);
8002 static struct _AttachProperty
8005 GtkWidget *mimetype_entry;
8006 GtkWidget *encoding_optmenu;
8007 GtkWidget *path_entry;
8008 GtkWidget *filename_entry;
8010 GtkWidget *cancel_btn;
8013 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8015 gtk_tree_path_free((GtkTreePath *)ptr);
8018 static void compose_attach_property(GtkAction *action, gpointer data)
8020 Compose *compose = (Compose *)data;
8021 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8023 GtkComboBox *optmenu;
8024 GtkTreeSelection *selection;
8026 GtkTreeModel *model;
8029 static gboolean cancelled;
8031 /* only if one selected */
8032 selection = gtk_tree_view_get_selection(tree_view);
8033 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8036 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8040 path = (GtkTreePath *) sel->data;
8041 gtk_tree_model_get_iter(model, &iter, path);
8042 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8045 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8051 if (!attach_prop.window)
8052 compose_attach_property_create(&cancelled);
8053 gtk_widget_grab_focus(attach_prop.ok_btn);
8054 gtk_widget_show(attach_prop.window);
8055 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8057 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8058 if (ainfo->encoding == ENC_UNKNOWN)
8059 combobox_select_by_data(optmenu, ENC_BASE64);
8061 combobox_select_by_data(optmenu, ainfo->encoding);
8063 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8064 ainfo->content_type ? ainfo->content_type : "");
8065 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8066 ainfo->file ? ainfo->file : "");
8067 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8068 ainfo->name ? ainfo->name : "");
8071 const gchar *entry_text;
8073 gchar *cnttype = NULL;
8080 gtk_widget_hide(attach_prop.window);
8085 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8086 if (*entry_text != '\0') {
8089 text = g_strstrip(g_strdup(entry_text));
8090 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8091 cnttype = g_strdup(text);
8094 alertpanel_error(_("Invalid MIME type."));
8100 ainfo->encoding = combobox_get_active_data(optmenu);
8102 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8103 if (*entry_text != '\0') {
8104 if (is_file_exist(entry_text) &&
8105 (size = get_file_size(entry_text)) > 0)
8106 file = g_strdup(entry_text);
8109 (_("File doesn't exist or is empty."));
8115 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8116 if (*entry_text != '\0') {
8117 g_free(ainfo->name);
8118 ainfo->name = g_strdup(entry_text);
8122 g_free(ainfo->content_type);
8123 ainfo->content_type = cnttype;
8126 g_free(ainfo->file);
8130 ainfo->size = (goffset)size;
8132 /* update tree store */
8133 text = to_human_readable(ainfo->size);
8134 gtk_tree_model_get_iter(model, &iter, path);
8135 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8136 COL_MIMETYPE, ainfo->content_type,
8138 COL_NAME, ainfo->name,
8144 gtk_tree_path_free(path);
8147 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8149 label = gtk_label_new(str); \
8150 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8151 GTK_FILL, 0, 0, 0); \
8152 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8154 entry = gtk_entry_new(); \
8155 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8156 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8159 static void compose_attach_property_create(gboolean *cancelled)
8165 GtkWidget *mimetype_entry;
8168 GtkListStore *optmenu_menu;
8169 GtkWidget *path_entry;
8170 GtkWidget *filename_entry;
8173 GtkWidget *cancel_btn;
8174 GList *mime_type_list, *strlist;
8177 debug_print("Creating attach_property window...\n");
8179 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8180 gtk_widget_set_size_request(window, 480, -1);
8181 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8182 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8183 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8184 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8185 g_signal_connect(G_OBJECT(window), "delete_event",
8186 G_CALLBACK(attach_property_delete_event),
8188 g_signal_connect(G_OBJECT(window), "key_press_event",
8189 G_CALLBACK(attach_property_key_pressed),
8192 vbox = gtk_vbox_new(FALSE, 8);
8193 gtk_container_add(GTK_CONTAINER(window), vbox);
8195 table = gtk_table_new(4, 2, FALSE);
8196 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8197 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8198 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8200 label = gtk_label_new(_("MIME type"));
8201 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8203 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8204 mimetype_entry = gtk_combo_box_entry_new_text();
8205 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8206 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8208 /* stuff with list */
8209 mime_type_list = procmime_get_mime_type_list();
8211 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8212 MimeType *type = (MimeType *) mime_type_list->data;
8215 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8217 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8220 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8221 (GCompareFunc)strcmp2);
8224 for (mime_type_list = strlist; mime_type_list != NULL;
8225 mime_type_list = mime_type_list->next) {
8226 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8227 g_free(mime_type_list->data);
8229 g_list_free(strlist);
8230 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8231 mimetype_entry = GTK_BIN(mimetype_entry)->child;
8233 label = gtk_label_new(_("Encoding"));
8234 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8236 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8238 hbox = gtk_hbox_new(FALSE, 0);
8239 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8240 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8242 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8243 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8245 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8246 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8247 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8248 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8249 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8251 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8253 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8254 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8256 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8257 &ok_btn, GTK_STOCK_OK,
8259 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8260 gtk_widget_grab_default(ok_btn);
8262 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8263 G_CALLBACK(attach_property_ok),
8265 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8266 G_CALLBACK(attach_property_cancel),
8269 gtk_widget_show_all(vbox);
8271 attach_prop.window = window;
8272 attach_prop.mimetype_entry = mimetype_entry;
8273 attach_prop.encoding_optmenu = optmenu;
8274 attach_prop.path_entry = path_entry;
8275 attach_prop.filename_entry = filename_entry;
8276 attach_prop.ok_btn = ok_btn;
8277 attach_prop.cancel_btn = cancel_btn;
8280 #undef SET_LABEL_AND_ENTRY
8282 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8288 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8294 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8295 gboolean *cancelled)
8303 static gboolean attach_property_key_pressed(GtkWidget *widget,
8305 gboolean *cancelled)
8307 if (event && event->keyval == GDK_Escape) {
8314 static void compose_exec_ext_editor(Compose *compose)
8321 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8322 G_DIR_SEPARATOR, compose);
8324 if (pipe(pipe_fds) < 0) {
8330 if ((pid = fork()) < 0) {
8337 /* close the write side of the pipe */
8340 compose->exteditor_file = g_strdup(tmp);
8341 compose->exteditor_pid = pid;
8343 compose_set_ext_editor_sensitive(compose, FALSE);
8345 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8346 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8350 } else { /* process-monitoring process */
8356 /* close the read side of the pipe */
8359 if (compose_write_body_to_file(compose, tmp) < 0) {
8360 fd_write_all(pipe_fds[1], "2\n", 2);
8364 pid_ed = compose_exec_ext_editor_real(tmp);
8366 fd_write_all(pipe_fds[1], "1\n", 2);
8370 /* wait until editor is terminated */
8371 waitpid(pid_ed, NULL, 0);
8373 fd_write_all(pipe_fds[1], "0\n", 2);
8380 #endif /* G_OS_UNIX */
8384 static gint compose_exec_ext_editor_real(const gchar *file)
8391 g_return_val_if_fail(file != NULL, -1);
8393 if ((pid = fork()) < 0) {
8398 if (pid != 0) return pid;
8400 /* grandchild process */
8402 if (setpgid(0, getppid()))
8405 if (prefs_common_get_ext_editor_cmd() &&
8406 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8407 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8408 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8410 if (prefs_common_get_ext_editor_cmd())
8411 g_warning("External editor command line is invalid: '%s'\n",
8412 prefs_common_get_ext_editor_cmd());
8413 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8416 cmdline = strsplit_with_quote(buf, " ", 1024);
8417 execvp(cmdline[0], cmdline);
8420 g_strfreev(cmdline);
8425 static gboolean compose_ext_editor_kill(Compose *compose)
8427 pid_t pgid = compose->exteditor_pid * -1;
8430 ret = kill(pgid, 0);
8432 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8436 msg = g_strdup_printf
8437 (_("The external editor is still working.\n"
8438 "Force terminating the process?\n"
8439 "process group id: %d"), -pgid);
8440 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8441 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8445 if (val == G_ALERTALTERNATE) {
8446 g_source_remove(compose->exteditor_tag);
8447 g_io_channel_shutdown(compose->exteditor_ch,
8449 g_io_channel_unref(compose->exteditor_ch);
8451 if (kill(pgid, SIGTERM) < 0) perror("kill");
8452 waitpid(compose->exteditor_pid, NULL, 0);
8454 g_warning("Terminated process group id: %d", -pgid);
8455 g_warning("Temporary file: %s",
8456 compose->exteditor_file);
8458 compose_set_ext_editor_sensitive(compose, TRUE);
8460 g_free(compose->exteditor_file);
8461 compose->exteditor_file = NULL;
8462 compose->exteditor_pid = -1;
8463 compose->exteditor_ch = NULL;
8464 compose->exteditor_tag = -1;
8472 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8476 Compose *compose = (Compose *)data;
8479 debug_print(_("Compose: input from monitoring process\n"));
8481 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8483 g_io_channel_shutdown(source, FALSE, NULL);
8484 g_io_channel_unref(source);
8486 waitpid(compose->exteditor_pid, NULL, 0);
8488 if (buf[0] == '0') { /* success */
8489 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8490 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8492 gtk_text_buffer_set_text(buffer, "", -1);
8493 compose_insert_file(compose, compose->exteditor_file);
8494 compose_changed_cb(NULL, compose);
8496 if (claws_unlink(compose->exteditor_file) < 0)
8497 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8498 } else if (buf[0] == '1') { /* failed */
8499 g_warning("Couldn't exec external editor\n");
8500 if (claws_unlink(compose->exteditor_file) < 0)
8501 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8502 } else if (buf[0] == '2') {
8503 g_warning("Couldn't write to file\n");
8504 } else if (buf[0] == '3') {
8505 g_warning("Pipe read failed\n");
8508 compose_set_ext_editor_sensitive(compose, TRUE);
8510 g_free(compose->exteditor_file);
8511 compose->exteditor_file = NULL;
8512 compose->exteditor_pid = -1;
8513 compose->exteditor_ch = NULL;
8514 compose->exteditor_tag = -1;
8519 static void compose_set_ext_editor_sensitive(Compose *compose,
8522 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8523 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8524 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8525 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8526 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8527 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8528 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8530 gtk_widget_set_sensitive(compose->text, sensitive);
8531 if (compose->toolbar->send_btn)
8532 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8533 if (compose->toolbar->sendl_btn)
8534 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8535 if (compose->toolbar->draft_btn)
8536 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8537 if (compose->toolbar->insert_btn)
8538 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8539 if (compose->toolbar->sig_btn)
8540 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8541 if (compose->toolbar->exteditor_btn)
8542 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8543 if (compose->toolbar->linewrap_current_btn)
8544 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8545 if (compose->toolbar->linewrap_all_btn)
8546 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8548 #endif /* G_OS_UNIX */
8551 * compose_undo_state_changed:
8553 * Change the sensivity of the menuentries undo and redo
8555 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8556 gint redo_state, gpointer data)
8558 Compose *compose = (Compose *)data;
8560 switch (undo_state) {
8561 case UNDO_STATE_TRUE:
8562 if (!undostruct->undo_state) {
8563 undostruct->undo_state = TRUE;
8564 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8567 case UNDO_STATE_FALSE:
8568 if (undostruct->undo_state) {
8569 undostruct->undo_state = FALSE;
8570 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8573 case UNDO_STATE_UNCHANGED:
8575 case UNDO_STATE_REFRESH:
8576 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8579 g_warning("Undo state not recognized");
8583 switch (redo_state) {
8584 case UNDO_STATE_TRUE:
8585 if (!undostruct->redo_state) {
8586 undostruct->redo_state = TRUE;
8587 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8590 case UNDO_STATE_FALSE:
8591 if (undostruct->redo_state) {
8592 undostruct->redo_state = FALSE;
8593 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8596 case UNDO_STATE_UNCHANGED:
8598 case UNDO_STATE_REFRESH:
8599 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8602 g_warning("Redo state not recognized");
8607 /* callback functions */
8609 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8610 * includes "non-client" (windows-izm) in calculation, so this calculation
8611 * may not be accurate.
8613 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8614 GtkAllocation *allocation,
8615 GtkSHRuler *shruler)
8617 if (prefs_common.show_ruler) {
8618 gint char_width = 0, char_height = 0;
8619 gint line_width_in_chars;
8621 gtkut_get_font_size(GTK_WIDGET(widget),
8622 &char_width, &char_height);
8623 line_width_in_chars =
8624 (allocation->width - allocation->x) / char_width;
8626 /* got the maximum */
8627 gtk_ruler_set_range(GTK_RULER(shruler),
8628 0.0, line_width_in_chars, 0,
8629 /*line_width_in_chars*/ char_width);
8635 static void account_activated(GtkComboBox *optmenu, gpointer data)
8637 Compose *compose = (Compose *)data;
8640 gchar *folderidentifier;
8641 gint account_id = 0;
8645 /* Get ID of active account in the combo box */
8646 menu = gtk_combo_box_get_model(optmenu);
8647 gtk_combo_box_get_active_iter(optmenu, &iter);
8648 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8650 ac = account_find_from_id(account_id);
8651 g_return_if_fail(ac != NULL);
8653 if (ac != compose->account)
8654 compose_select_account(compose, ac, FALSE);
8656 /* Set message save folder */
8657 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8658 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8660 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8661 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8663 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8664 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8665 folderidentifier = folder_item_get_identifier(account_get_special_folder
8666 (compose->account, F_OUTBOX));
8667 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
8668 g_free(folderidentifier);
8672 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8673 GtkTreeViewColumn *column, Compose *compose)
8675 compose_attach_property(NULL, compose);
8678 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8681 Compose *compose = (Compose *)data;
8682 GtkTreeSelection *attach_selection;
8683 gint attach_nr_selected;
8685 if (!event) return FALSE;
8687 if (event->button == 3) {
8688 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8689 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8691 if (attach_nr_selected > 0)
8693 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8694 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8696 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8697 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8700 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8701 NULL, NULL, event->button, event->time);
8708 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8711 Compose *compose = (Compose *)data;
8713 if (!event) return FALSE;
8715 switch (event->keyval) {
8717 compose_attach_remove_selected(NULL, compose);
8723 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8725 toolbar_comp_set_sensitive(compose, allow);
8726 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8727 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8729 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8731 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8732 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8733 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8735 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8739 static void compose_send_cb(GtkAction *action, gpointer data)
8741 Compose *compose = (Compose *)data;
8743 if (prefs_common.work_offline &&
8744 !inc_offline_should_override(TRUE,
8745 _("Claws Mail needs network access in order "
8746 "to send this email.")))
8749 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8750 g_source_remove(compose->draft_timeout_tag);
8751 compose->draft_timeout_tag = -1;
8754 compose_send(compose);
8757 static void compose_send_later_cb(GtkAction *action, gpointer data)
8759 Compose *compose = (Compose *)data;
8763 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8767 compose_close(compose);
8768 } else if (val == -1) {
8769 alertpanel_error(_("Could not queue message."));
8770 } else if (val == -2) {
8771 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8772 } else if (val == -3) {
8773 if (privacy_peek_error())
8774 alertpanel_error(_("Could not queue message for sending:\n\n"
8775 "Signature failed: %s"), privacy_get_error());
8776 } else if (val == -4) {
8777 alertpanel_error(_("Could not queue message for sending:\n\n"
8778 "Charset conversion failed."));
8779 } else if (val == -5) {
8780 alertpanel_error(_("Could not queue message for sending:\n\n"
8781 "Couldn't get recipient encryption key."));
8782 } else if (val == -6) {
8785 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
8788 #define DRAFTED_AT_EXIT "drafted_at_exit"
8789 static void compose_register_draft(MsgInfo *info)
8791 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8792 DRAFTED_AT_EXIT, NULL);
8793 FILE *fp = fopen(filepath, "ab");
8796 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
8804 gboolean compose_draft (gpointer data, guint action)
8806 Compose *compose = (Compose *)data;
8810 MsgFlags flag = {0, 0};
8811 static gboolean lock = FALSE;
8812 MsgInfo *newmsginfo;
8814 gboolean target_locked = FALSE;
8815 gboolean err = FALSE;
8817 if (lock) return FALSE;
8819 if (compose->sending)
8822 draft = account_get_special_folder(compose->account, F_DRAFT);
8823 g_return_val_if_fail(draft != NULL, FALSE);
8825 if (!g_mutex_trylock(compose->mutex)) {
8826 /* we don't want to lock the mutex once it's available,
8827 * because as the only other part of compose.c locking
8828 * it is compose_close - which means once unlocked,
8829 * the compose struct will be freed */
8830 debug_print("couldn't lock mutex, probably sending\n");
8836 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
8837 G_DIR_SEPARATOR, compose);
8838 if ((fp = g_fopen(tmp, "wb")) == NULL) {
8839 FILE_OP_ERROR(tmp, "fopen");
8843 /* chmod for security */
8844 if (change_file_mode_rw(fp, tmp) < 0) {
8845 FILE_OP_ERROR(tmp, "chmod");
8846 g_warning("can't change file mode\n");
8849 /* Save draft infos */
8850 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
8851 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
8853 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
8854 gchar *savefolderid;
8856 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8857 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
8858 g_free(savefolderid);
8860 if (compose->return_receipt) {
8861 err |= (fprintf(fp, "RRCPT:1\n") < 0);
8863 if (compose->privacy_system) {
8864 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
8865 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
8866 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
8869 /* Message-ID of message replying to */
8870 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
8873 folderid = folder_item_get_identifier(compose->replyinfo->folder);
8874 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
8877 /* Message-ID of message forwarding to */
8878 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
8881 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
8882 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
8886 /* end of headers */
8887 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
8894 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
8898 if (fclose(fp) == EOF) {
8902 if (compose->targetinfo) {
8903 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
8904 flag.perm_flags = target_locked?MSG_LOCKED:0;
8906 flag.tmp_flags = MSG_DRAFT;
8908 folder_item_scan(draft);
8909 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
8910 MsgInfo *tmpinfo = NULL;
8911 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
8912 if (compose->msgid) {
8913 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
8916 msgnum = tmpinfo->msgnum;
8917 procmsg_msginfo_free(tmpinfo);
8918 debug_print("got draft msgnum %d from scanning\n", msgnum);
8920 debug_print("didn't get draft msgnum after scanning\n");
8923 debug_print("got draft msgnum %d from adding\n", msgnum);
8929 if (action != COMPOSE_AUTO_SAVE) {
8930 if (action != COMPOSE_DRAFT_FOR_EXIT)
8931 alertpanel_error(_("Could not save draft."));
8934 gtkut_window_popup(compose->window);
8935 val = alertpanel_full(_("Could not save draft"),
8936 _("Could not save draft.\n"
8937 "Do you want to cancel exit or discard this email?"),
8938 _("_Cancel exit"), _("_Discard email"), NULL,
8939 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
8940 if (val == G_ALERTALTERNATE) {
8942 g_mutex_unlock(compose->mutex); /* must be done before closing */
8943 compose_close(compose);
8947 g_mutex_unlock(compose->mutex); /* must be done before closing */
8956 if (compose->mode == COMPOSE_REEDIT) {
8957 compose_remove_reedit_target(compose, TRUE);
8960 newmsginfo = folder_item_get_msginfo(draft, msgnum);
8963 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
8965 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
8967 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
8968 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
8969 procmsg_msginfo_set_flags(newmsginfo, 0,
8970 MSG_HAS_ATTACHMENT);
8972 if (action == COMPOSE_DRAFT_FOR_EXIT) {
8973 compose_register_draft(newmsginfo);
8975 procmsg_msginfo_free(newmsginfo);
8978 folder_item_scan(draft);
8980 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
8982 g_mutex_unlock(compose->mutex); /* must be done before closing */
8983 compose_close(compose);
8989 path = folder_item_fetch_msg(draft, msgnum);
8991 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
8994 if (g_stat(path, &s) < 0) {
8995 FILE_OP_ERROR(path, "stat");
9001 procmsg_msginfo_free(compose->targetinfo);
9002 compose->targetinfo = procmsg_msginfo_new();
9003 compose->targetinfo->msgnum = msgnum;
9004 compose->targetinfo->size = (goffset)s.st_size;
9005 compose->targetinfo->mtime = s.st_mtime;
9006 compose->targetinfo->folder = draft;
9008 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9009 compose->mode = COMPOSE_REEDIT;
9011 if (action == COMPOSE_AUTO_SAVE) {
9012 compose->autosaved_draft = compose->targetinfo;
9014 compose->modified = FALSE;
9015 compose_set_title(compose);
9019 g_mutex_unlock(compose->mutex);
9023 void compose_clear_exit_drafts(void)
9025 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9026 DRAFTED_AT_EXIT, NULL);
9027 if (is_file_exist(filepath))
9028 claws_unlink(filepath);
9033 void compose_reopen_exit_drafts(void)
9035 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9036 DRAFTED_AT_EXIT, NULL);
9037 FILE *fp = fopen(filepath, "rb");
9041 while (fgets(buf, sizeof(buf), fp)) {
9042 gchar **parts = g_strsplit(buf, "\t", 2);
9043 const gchar *folder = parts[0];
9044 int msgnum = parts[1] ? atoi(parts[1]):-1;
9046 if (folder && *folder && msgnum > -1) {
9047 FolderItem *item = folder_find_item_from_identifier(folder);
9048 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9050 compose_reedit(info, FALSE);
9057 compose_clear_exit_drafts();
9060 static void compose_save_cb(GtkAction *action, gpointer data)
9062 compose_draft(data, COMPOSE_KEEP_EDITING);
9065 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9067 if (compose && file_list) {
9070 for ( tmp = file_list; tmp; tmp = tmp->next) {
9071 gchar *file = (gchar *) tmp->data;
9072 gchar *utf8_filename = conv_filename_to_utf8(file);
9073 compose_attach_append(compose, file, utf8_filename, NULL);
9074 compose_changed_cb(NULL, compose);
9079 g_free(utf8_filename);
9084 static void compose_attach_cb(GtkAction *action, gpointer data)
9086 Compose *compose = (Compose *)data;
9089 if (compose->redirect_filename != NULL)
9092 file_list = filesel_select_multiple_files_open(_("Select file"));
9095 compose_attach_from_list(compose, file_list, TRUE);
9096 g_list_free(file_list);
9100 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9102 Compose *compose = (Compose *)data;
9105 file_list = filesel_select_multiple_files_open(_("Select file"));
9110 for ( tmp = file_list; tmp; tmp = tmp->next) {
9111 gchar *file = (gchar *) tmp->data;
9112 gchar *filedup = g_strdup(file);
9113 gchar *shortfile = g_path_get_basename(filedup);
9114 ComposeInsertResult res;
9116 res = compose_insert_file(compose, file);
9117 if (res == COMPOSE_INSERT_READ_ERROR) {
9118 alertpanel_error(_("File '%s' could not be read."), shortfile);
9119 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9120 alertpanel_error(_("File '%s' contained invalid characters\n"
9121 "for the current encoding, insertion may be incorrect."), shortfile);
9127 g_list_free(file_list);
9131 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9133 Compose *compose = (Compose *)data;
9135 compose_insert_sig(compose, FALSE);
9138 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9142 Compose *compose = (Compose *)data;
9144 gtkut_widget_get_uposition(widget, &x, &y);
9145 prefs_common.compose_x = x;
9146 prefs_common.compose_y = y;
9148 if (compose->sending || compose->updating)
9150 compose_close_cb(NULL, compose);
9154 void compose_close_toolbar(Compose *compose)
9156 compose_close_cb(NULL, compose);
9159 static void compose_close_cb(GtkAction *action, gpointer data)
9161 Compose *compose = (Compose *)data;
9165 if (compose->exteditor_tag != -1) {
9166 if (!compose_ext_editor_kill(compose))
9171 if (compose->modified) {
9172 if (!g_mutex_trylock(compose->mutex)) {
9173 /* we don't want to lock the mutex once it's available,
9174 * because as the only other part of compose.c locking
9175 * it is compose_close - which means once unlocked,
9176 * the compose struct will be freed */
9177 debug_print("couldn't lock mutex, probably sending\n");
9180 val = alertpanel(_("Discard message"),
9181 _("This message has been modified. Discard it?"),
9182 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9183 g_mutex_unlock(compose->mutex);
9185 case G_ALERTDEFAULT:
9186 if (prefs_common.autosave)
9187 compose_remove_draft(compose);
9189 case G_ALERTALTERNATE:
9190 compose_draft(data, COMPOSE_QUIT_EDITING);
9197 compose_close(compose);
9200 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9202 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9203 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9204 Compose *compose = (Compose *) data;
9207 compose->out_encoding = (CharSet)value;
9210 static void compose_address_cb(GtkAction *action, gpointer data)
9212 Compose *compose = (Compose *)data;
9214 addressbook_open(compose);
9217 static void about_show_cb(GtkAction *action, gpointer data)
9222 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9224 Compose *compose = (Compose *)data;
9229 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9230 g_return_if_fail(tmpl != NULL);
9232 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9234 val = alertpanel(_("Apply template"), msg,
9235 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9238 if (val == G_ALERTDEFAULT)
9239 compose_template_apply(compose, tmpl, TRUE);
9240 else if (val == G_ALERTALTERNATE)
9241 compose_template_apply(compose, tmpl, FALSE);
9244 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9246 Compose *compose = (Compose *)data;
9248 compose_exec_ext_editor(compose);
9251 static void compose_undo_cb(GtkAction *action, gpointer data)
9253 Compose *compose = (Compose *)data;
9254 gboolean prev_autowrap = compose->autowrap;
9256 compose->autowrap = FALSE;
9257 undo_undo(compose->undostruct);
9258 compose->autowrap = prev_autowrap;
9261 static void compose_redo_cb(GtkAction *action, gpointer data)
9263 Compose *compose = (Compose *)data;
9264 gboolean prev_autowrap = compose->autowrap;
9266 compose->autowrap = FALSE;
9267 undo_redo(compose->undostruct);
9268 compose->autowrap = prev_autowrap;
9271 static void entry_cut_clipboard(GtkWidget *entry)
9273 if (GTK_IS_EDITABLE(entry))
9274 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9275 else if (GTK_IS_TEXT_VIEW(entry))
9276 gtk_text_buffer_cut_clipboard(
9277 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9278 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9282 static void entry_copy_clipboard(GtkWidget *entry)
9284 if (GTK_IS_EDITABLE(entry))
9285 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9286 else if (GTK_IS_TEXT_VIEW(entry))
9287 gtk_text_buffer_copy_clipboard(
9288 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9289 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9292 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9293 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9295 if (GTK_IS_TEXT_VIEW(entry)) {
9296 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9297 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9298 GtkTextIter start_iter, end_iter;
9300 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9302 if (contents == NULL)
9305 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9307 /* we shouldn't delete the selection when middle-click-pasting, or we
9308 * can't mid-click-paste our own selection */
9309 if (clip != GDK_SELECTION_PRIMARY) {
9310 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9313 if (insert_place == NULL) {
9314 /* if insert_place isn't specified, insert at the cursor.
9315 * used for Ctrl-V pasting */
9316 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9317 start = gtk_text_iter_get_offset(&start_iter);
9318 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9320 /* if insert_place is specified, paste here.
9321 * used for mid-click-pasting */
9322 start = gtk_text_iter_get_offset(insert_place);
9323 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9327 /* paste unwrapped: mark the paste so it's not wrapped later */
9328 end = start + strlen(contents);
9329 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9330 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9331 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9332 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9333 /* rewrap paragraph now (after a mid-click-paste) */
9334 mark_start = gtk_text_buffer_get_insert(buffer);
9335 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9336 gtk_text_iter_backward_char(&start_iter);
9337 compose_beautify_paragraph(compose, &start_iter, TRUE);
9339 } else if (GTK_IS_EDITABLE(entry))
9340 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9342 compose->modified = TRUE;
9345 static void entry_allsel(GtkWidget *entry)
9347 if (GTK_IS_EDITABLE(entry))
9348 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9349 else if (GTK_IS_TEXT_VIEW(entry)) {
9350 GtkTextIter startiter, enditer;
9351 GtkTextBuffer *textbuf;
9353 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9354 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9355 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9357 gtk_text_buffer_move_mark_by_name(textbuf,
9358 "selection_bound", &startiter);
9359 gtk_text_buffer_move_mark_by_name(textbuf,
9360 "insert", &enditer);
9364 static void compose_cut_cb(GtkAction *action, gpointer data)
9366 Compose *compose = (Compose *)data;
9367 if (compose->focused_editable
9368 #ifndef GENERIC_UMPC
9369 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9372 entry_cut_clipboard(compose->focused_editable);
9375 static void compose_copy_cb(GtkAction *action, gpointer data)
9377 Compose *compose = (Compose *)data;
9378 if (compose->focused_editable
9379 #ifndef GENERIC_UMPC
9380 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9383 entry_copy_clipboard(compose->focused_editable);
9386 static void compose_paste_cb(GtkAction *action, gpointer data)
9388 Compose *compose = (Compose *)data;
9390 GtkTextBuffer *buffer;
9392 if (compose->focused_editable &&
9393 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9394 entry_paste_clipboard(compose, compose->focused_editable,
9395 prefs_common.linewrap_pastes,
9396 GDK_SELECTION_CLIPBOARD, NULL);
9400 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9402 Compose *compose = (Compose *)data;
9403 gint wrap_quote = prefs_common.linewrap_quote;
9404 if (compose->focused_editable
9405 #ifndef GENERIC_UMPC
9406 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9409 /* let text_insert() (called directly or at a later time
9410 * after the gtk_editable_paste_clipboard) know that
9411 * text is to be inserted as a quotation. implemented
9412 * by using a simple refcount... */
9413 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9414 G_OBJECT(compose->focused_editable),
9415 "paste_as_quotation"));
9416 g_object_set_data(G_OBJECT(compose->focused_editable),
9417 "paste_as_quotation",
9418 GINT_TO_POINTER(paste_as_quotation + 1));
9419 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9420 entry_paste_clipboard(compose, compose->focused_editable,
9421 prefs_common.linewrap_pastes,
9422 GDK_SELECTION_CLIPBOARD, NULL);
9423 prefs_common.linewrap_quote = wrap_quote;
9427 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9429 Compose *compose = (Compose *)data;
9431 GtkTextBuffer *buffer;
9433 if (compose->focused_editable
9434 #ifndef GENERIC_UMPC
9435 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9438 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9439 GDK_SELECTION_CLIPBOARD, NULL);
9443 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9445 Compose *compose = (Compose *)data;
9447 GtkTextBuffer *buffer;
9449 if (compose->focused_editable
9450 #ifndef GENERIC_UMPC
9451 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9454 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9455 GDK_SELECTION_CLIPBOARD, NULL);
9459 static void compose_allsel_cb(GtkAction *action, gpointer data)
9461 Compose *compose = (Compose *)data;
9462 if (compose->focused_editable
9463 #ifndef GENERIC_UMPC
9464 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9467 entry_allsel(compose->focused_editable);
9470 static void textview_move_beginning_of_line (GtkTextView *text)
9472 GtkTextBuffer *buffer;
9476 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9478 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9479 mark = gtk_text_buffer_get_insert(buffer);
9480 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9481 gtk_text_iter_set_line_offset(&ins, 0);
9482 gtk_text_buffer_place_cursor(buffer, &ins);
9485 static void textview_move_forward_character (GtkTextView *text)
9487 GtkTextBuffer *buffer;
9491 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9493 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9494 mark = gtk_text_buffer_get_insert(buffer);
9495 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9496 if (gtk_text_iter_forward_cursor_position(&ins))
9497 gtk_text_buffer_place_cursor(buffer, &ins);
9500 static void textview_move_backward_character (GtkTextView *text)
9502 GtkTextBuffer *buffer;
9506 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9508 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9509 mark = gtk_text_buffer_get_insert(buffer);
9510 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9511 if (gtk_text_iter_backward_cursor_position(&ins))
9512 gtk_text_buffer_place_cursor(buffer, &ins);
9515 static void textview_move_forward_word (GtkTextView *text)
9517 GtkTextBuffer *buffer;
9522 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9524 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9525 mark = gtk_text_buffer_get_insert(buffer);
9526 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9527 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9528 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9529 gtk_text_iter_backward_word_start(&ins);
9530 gtk_text_buffer_place_cursor(buffer, &ins);
9534 static void textview_move_backward_word (GtkTextView *text)
9536 GtkTextBuffer *buffer;
9541 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9543 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9544 mark = gtk_text_buffer_get_insert(buffer);
9545 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9546 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9547 if (gtk_text_iter_backward_word_starts(&ins, 1))
9548 gtk_text_buffer_place_cursor(buffer, &ins);
9551 static void textview_move_end_of_line (GtkTextView *text)
9553 GtkTextBuffer *buffer;
9557 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9559 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9560 mark = gtk_text_buffer_get_insert(buffer);
9561 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9562 if (gtk_text_iter_forward_to_line_end(&ins))
9563 gtk_text_buffer_place_cursor(buffer, &ins);
9566 static void textview_move_next_line (GtkTextView *text)
9568 GtkTextBuffer *buffer;
9573 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9575 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9576 mark = gtk_text_buffer_get_insert(buffer);
9577 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9578 offset = gtk_text_iter_get_line_offset(&ins);
9579 if (gtk_text_iter_forward_line(&ins)) {
9580 gtk_text_iter_set_line_offset(&ins, offset);
9581 gtk_text_buffer_place_cursor(buffer, &ins);
9585 static void textview_move_previous_line (GtkTextView *text)
9587 GtkTextBuffer *buffer;
9592 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9594 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9595 mark = gtk_text_buffer_get_insert(buffer);
9596 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9597 offset = gtk_text_iter_get_line_offset(&ins);
9598 if (gtk_text_iter_backward_line(&ins)) {
9599 gtk_text_iter_set_line_offset(&ins, offset);
9600 gtk_text_buffer_place_cursor(buffer, &ins);
9604 static void textview_delete_forward_character (GtkTextView *text)
9606 GtkTextBuffer *buffer;
9608 GtkTextIter ins, end_iter;
9610 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9612 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9613 mark = gtk_text_buffer_get_insert(buffer);
9614 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9616 if (gtk_text_iter_forward_char(&end_iter)) {
9617 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9621 static void textview_delete_backward_character (GtkTextView *text)
9623 GtkTextBuffer *buffer;
9625 GtkTextIter ins, end_iter;
9627 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9629 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9630 mark = gtk_text_buffer_get_insert(buffer);
9631 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9633 if (gtk_text_iter_backward_char(&end_iter)) {
9634 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9638 static void textview_delete_forward_word (GtkTextView *text)
9640 GtkTextBuffer *buffer;
9642 GtkTextIter ins, end_iter;
9644 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9646 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9647 mark = gtk_text_buffer_get_insert(buffer);
9648 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9650 if (gtk_text_iter_forward_word_end(&end_iter)) {
9651 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9655 static void textview_delete_backward_word (GtkTextView *text)
9657 GtkTextBuffer *buffer;
9659 GtkTextIter ins, end_iter;
9661 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9663 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9664 mark = gtk_text_buffer_get_insert(buffer);
9665 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9667 if (gtk_text_iter_backward_word_start(&end_iter)) {
9668 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9672 static void textview_delete_line (GtkTextView *text)
9674 GtkTextBuffer *buffer;
9676 GtkTextIter ins, start_iter, end_iter;
9678 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9680 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9681 mark = gtk_text_buffer_get_insert(buffer);
9682 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9685 gtk_text_iter_set_line_offset(&start_iter, 0);
9688 if (gtk_text_iter_ends_line(&end_iter)){
9689 if (!gtk_text_iter_forward_char(&end_iter))
9690 gtk_text_iter_backward_char(&start_iter);
9693 gtk_text_iter_forward_to_line_end(&end_iter);
9694 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9697 static void textview_delete_to_line_end (GtkTextView *text)
9699 GtkTextBuffer *buffer;
9701 GtkTextIter ins, end_iter;
9703 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9705 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9706 mark = gtk_text_buffer_get_insert(buffer);
9707 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9709 if (gtk_text_iter_ends_line(&end_iter))
9710 gtk_text_iter_forward_char(&end_iter);
9712 gtk_text_iter_forward_to_line_end(&end_iter);
9713 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9716 #define DO_ACTION(name, act) { \
9717 if(!strcmp(name, a_name)) { \
9721 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
9723 const gchar *a_name = gtk_action_get_name(action);
9724 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
9725 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
9726 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
9727 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
9728 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
9729 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
9730 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
9731 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
9732 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
9733 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
9734 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
9735 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
9736 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
9737 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
9741 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
9743 Compose *compose = (Compose *)data;
9744 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9745 ComposeCallAdvancedAction action = -1;
9747 action = compose_call_advanced_action_from_path(gaction);
9750 void (*do_action) (GtkTextView *text);
9751 } action_table[] = {
9752 {textview_move_beginning_of_line},
9753 {textview_move_forward_character},
9754 {textview_move_backward_character},
9755 {textview_move_forward_word},
9756 {textview_move_backward_word},
9757 {textview_move_end_of_line},
9758 {textview_move_next_line},
9759 {textview_move_previous_line},
9760 {textview_delete_forward_character},
9761 {textview_delete_backward_character},
9762 {textview_delete_forward_word},
9763 {textview_delete_backward_word},
9764 {textview_delete_line},
9765 {textview_delete_to_line_end}
9768 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
9770 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
9771 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
9772 if (action_table[action].do_action)
9773 action_table[action].do_action(text);
9775 g_warning("Not implemented yet.");
9779 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
9783 if (GTK_IS_EDITABLE(widget)) {
9784 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
9785 gtk_editable_set_position(GTK_EDITABLE(widget),
9788 if (widget->parent && widget->parent->parent
9789 && widget->parent->parent->parent) {
9790 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
9791 gint y = widget->allocation.y;
9792 gint height = widget->allocation.height;
9793 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
9794 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
9796 if (y < (int)shown->value) {
9797 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
9799 if (y + height > (int)shown->value + (int)shown->page_size) {
9800 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
9801 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9802 y + height - (int)shown->page_size - 1);
9804 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9805 (int)shown->upper - (int)shown->page_size - 1);
9812 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
9813 compose->focused_editable = widget;
9816 if (GTK_IS_TEXT_VIEW(widget)
9817 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
9818 gtk_widget_ref(compose->notebook);
9819 gtk_widget_ref(compose->edit_vbox);
9820 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9821 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9822 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
9823 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
9824 gtk_widget_unref(compose->notebook);
9825 gtk_widget_unref(compose->edit_vbox);
9826 g_signal_handlers_block_by_func(G_OBJECT(widget),
9827 G_CALLBACK(compose_grab_focus_cb),
9829 gtk_widget_grab_focus(widget);
9830 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9831 G_CALLBACK(compose_grab_focus_cb),
9833 } else if (!GTK_IS_TEXT_VIEW(widget)
9834 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
9835 gtk_widget_ref(compose->notebook);
9836 gtk_widget_ref(compose->edit_vbox);
9837 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9838 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9839 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
9840 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
9841 gtk_widget_unref(compose->notebook);
9842 gtk_widget_unref(compose->edit_vbox);
9843 g_signal_handlers_block_by_func(G_OBJECT(widget),
9844 G_CALLBACK(compose_grab_focus_cb),
9846 gtk_widget_grab_focus(widget);
9847 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9848 G_CALLBACK(compose_grab_focus_cb),
9854 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
9856 compose->modified = TRUE;
9857 #ifndef GENERIC_UMPC
9858 compose_set_title(compose);
9862 static void compose_wrap_cb(GtkAction *action, gpointer data)
9864 Compose *compose = (Compose *)data;
9865 compose_beautify_paragraph(compose, NULL, TRUE);
9868 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
9870 Compose *compose = (Compose *)data;
9871 compose_wrap_all_full(compose, TRUE);
9874 static void compose_find_cb(GtkAction *action, gpointer data)
9876 Compose *compose = (Compose *)data;
9878 message_search_compose(compose);
9881 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
9884 Compose *compose = (Compose *)data;
9885 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9886 if (compose->autowrap)
9887 compose_wrap_all_full(compose, TRUE);
9888 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9891 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
9893 Compose *compose = (Compose *)data;
9895 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9898 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
9900 Compose *compose = (Compose *)data;
9902 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9905 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
9907 g_free(compose->privacy_system);
9909 compose->privacy_system = g_strdup(account->default_privacy_system);
9910 compose_update_privacy_system_menu_item(compose, warn);
9913 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
9915 Compose *compose = (Compose *)data;
9917 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
9918 gtk_widget_show(compose->ruler_hbox);
9919 prefs_common.show_ruler = TRUE;
9921 gtk_widget_hide(compose->ruler_hbox);
9922 gtk_widget_queue_resize(compose->edit_vbox);
9923 prefs_common.show_ruler = FALSE;
9927 static void compose_attach_drag_received_cb (GtkWidget *widget,
9928 GdkDragContext *context,
9931 GtkSelectionData *data,
9936 Compose *compose = (Compose *)user_data;
9939 if (gdk_atom_name(data->type) &&
9940 !strcmp(gdk_atom_name(data->type), "text/uri-list")
9941 && gtk_drag_get_source_widget(context) !=
9942 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
9943 list = uri_list_extract_filenames((const gchar *)data->data);
9944 for (tmp = list; tmp != NULL; tmp = tmp->next) {
9945 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
9946 compose_attach_append
9947 (compose, (const gchar *)tmp->data,
9948 utf8_filename, NULL);
9949 g_free(utf8_filename);
9951 if (list) compose_changed_cb(NULL, compose);
9952 list_free_strings(list);
9954 } else if (gtk_drag_get_source_widget(context)
9955 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
9956 /* comes from our summaryview */
9957 SummaryView * summaryview = NULL;
9958 GSList * list = NULL, *cur = NULL;
9960 if (mainwindow_get_mainwindow())
9961 summaryview = mainwindow_get_mainwindow()->summaryview;
9964 list = summary_get_selected_msg_list(summaryview);
9966 for (cur = list; cur; cur = cur->next) {
9967 MsgInfo *msginfo = (MsgInfo *)cur->data;
9970 file = procmsg_get_message_file_full(msginfo,
9973 compose_attach_append(compose, (const gchar *)file,
9974 (const gchar *)file, "message/rfc822");
9982 static gboolean compose_drag_drop(GtkWidget *widget,
9983 GdkDragContext *drag_context,
9985 guint time, gpointer user_data)
9987 /* not handling this signal makes compose_insert_drag_received_cb
9992 static void compose_insert_drag_received_cb (GtkWidget *widget,
9993 GdkDragContext *drag_context,
9996 GtkSelectionData *data,
10001 Compose *compose = (Compose *)user_data;
10004 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10006 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10007 AlertValue val = G_ALERTDEFAULT;
10009 list = uri_list_extract_filenames((const gchar *)data->data);
10011 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10012 /* Assume a list of no files, and data has ://, is a remote link */
10013 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10014 gchar *tmpfile = get_tmp_file();
10015 str_write_to_file(tmpdata, tmpfile);
10017 compose_insert_file(compose, tmpfile);
10018 claws_unlink(tmpfile);
10020 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10021 compose_beautify_paragraph(compose, NULL, TRUE);
10024 switch (prefs_common.compose_dnd_mode) {
10025 case COMPOSE_DND_ASK:
10026 val = alertpanel_full(_("Insert or attach?"),
10027 _("Do you want to insert the contents of the file(s) "
10028 "into the message body, or attach it to the email?"),
10029 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10030 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10032 case COMPOSE_DND_INSERT:
10033 val = G_ALERTALTERNATE;
10035 case COMPOSE_DND_ATTACH:
10036 val = G_ALERTOTHER;
10039 /* unexpected case */
10040 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10043 if (val & G_ALERTDISABLE) {
10044 val &= ~G_ALERTDISABLE;
10045 /* remember what action to perform by default, only if we don't click Cancel */
10046 if (val == G_ALERTALTERNATE)
10047 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10048 else if (val == G_ALERTOTHER)
10049 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10052 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10053 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10054 list_free_strings(list);
10057 } else if (val == G_ALERTOTHER) {
10058 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10059 list_free_strings(list);
10064 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10065 compose_insert_file(compose, (const gchar *)tmp->data);
10067 list_free_strings(list);
10069 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10072 #if GTK_CHECK_VERSION(2, 8, 0)
10073 /* do nothing, handled by GTK */
10075 gchar *tmpfile = get_tmp_file();
10076 str_write_to_file((const gchar *)data->data, tmpfile);
10077 compose_insert_file(compose, tmpfile);
10078 claws_unlink(tmpfile);
10080 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10084 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10087 static void compose_header_drag_received_cb (GtkWidget *widget,
10088 GdkDragContext *drag_context,
10091 GtkSelectionData *data,
10094 gpointer user_data)
10096 GtkEditable *entry = (GtkEditable *)user_data;
10097 gchar *email = (gchar *)data->data;
10099 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10102 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10103 gchar *decoded=g_new(gchar, strlen(email));
10106 email += strlen("mailto:");
10107 decode_uri(decoded, email); /* will fit */
10108 gtk_editable_delete_text(entry, 0, -1);
10109 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10110 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10114 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10117 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10119 Compose *compose = (Compose *)data;
10121 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10122 compose->return_receipt = TRUE;
10124 compose->return_receipt = FALSE;
10127 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10129 Compose *compose = (Compose *)data;
10131 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10132 compose->remove_references = TRUE;
10134 compose->remove_references = FALSE;
10137 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10138 GdkEventKey *event,
10139 ComposeHeaderEntry *headerentry)
10141 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10142 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10143 !(event->state & GDK_MODIFIER_MASK) &&
10144 (event->keyval == GDK_BackSpace) &&
10145 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10146 gtk_container_remove
10147 (GTK_CONTAINER(headerentry->compose->header_table),
10148 headerentry->combo);
10149 gtk_container_remove
10150 (GTK_CONTAINER(headerentry->compose->header_table),
10151 headerentry->entry);
10152 headerentry->compose->header_list =
10153 g_slist_remove(headerentry->compose->header_list,
10155 g_free(headerentry);
10156 } else if (event->keyval == GDK_Tab) {
10157 if (headerentry->compose->header_last == headerentry) {
10158 /* Override default next focus, and give it to subject_entry
10159 * instead of notebook tabs
10161 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10162 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10169 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10170 ComposeHeaderEntry *headerentry)
10172 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10173 compose_create_header_entry(headerentry->compose);
10174 g_signal_handlers_disconnect_matched
10175 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10176 0, 0, NULL, NULL, headerentry);
10178 /* Automatically scroll down */
10179 compose_show_first_last_header(headerentry->compose, FALSE);
10185 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10187 GtkAdjustment *vadj;
10189 g_return_if_fail(compose);
10190 g_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10191 g_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10193 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10194 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10195 gtk_adjustment_changed(vadj);
10198 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10199 const gchar *text, gint len, Compose *compose)
10201 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10202 (G_OBJECT(compose->text), "paste_as_quotation"));
10205 g_return_if_fail(text != NULL);
10207 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10208 G_CALLBACK(text_inserted),
10210 if (paste_as_quotation) {
10212 const gchar *qmark;
10214 GtkTextIter start_iter;
10217 len = strlen(text);
10219 new_text = g_strndup(text, len);
10221 qmark = compose_quote_char_from_context(compose);
10223 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10224 gtk_text_buffer_place_cursor(buffer, iter);
10226 pos = gtk_text_iter_get_offset(iter);
10228 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10229 _("Quote format error at line %d."));
10230 quote_fmt_reset_vartable();
10232 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10233 GINT_TO_POINTER(paste_as_quotation - 1));
10235 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10236 gtk_text_buffer_place_cursor(buffer, iter);
10237 gtk_text_buffer_delete_mark(buffer, mark);
10239 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10240 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10241 compose_beautify_paragraph(compose, &start_iter, FALSE);
10242 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10243 gtk_text_buffer_delete_mark(buffer, mark);
10245 if (strcmp(text, "\n") || compose->automatic_break
10246 || gtk_text_iter_starts_line(iter))
10247 gtk_text_buffer_insert(buffer, iter, text, len);
10249 /* check if the preceding is just whitespace or quote */
10250 GtkTextIter start_line;
10251 gchar *tmp = NULL, *quote = NULL;
10252 gint quote_len = 0, is_normal = 0;
10253 start_line = *iter;
10254 gtk_text_iter_set_line_offset(&start_line, 0);
10255 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10257 if (*tmp == '\0') {
10260 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10268 gtk_text_buffer_insert(buffer, iter, text, len);
10270 gtk_text_buffer_insert_with_tags_by_name(buffer,
10271 iter, text, len, "no_join", NULL);
10276 if (!paste_as_quotation) {
10277 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10278 compose_beautify_paragraph(compose, iter, FALSE);
10279 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10280 gtk_text_buffer_delete_mark(buffer, mark);
10283 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10284 G_CALLBACK(text_inserted),
10286 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10288 if (prefs_common.autosave &&
10289 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10290 compose->draft_timeout_tag != -2 /* disabled while loading */)
10291 compose->draft_timeout_tag = g_timeout_add
10292 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10294 static gint compose_defer_auto_save_draft(Compose *compose)
10296 compose->draft_timeout_tag = -1;
10297 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10302 static void compose_check_all(GtkAction *action, gpointer data)
10304 Compose *compose = (Compose *)data;
10305 if (compose->gtkaspell)
10306 gtkaspell_check_all(compose->gtkaspell);
10309 static void compose_highlight_all(GtkAction *action, gpointer data)
10311 Compose *compose = (Compose *)data;
10312 if (compose->gtkaspell)
10313 gtkaspell_highlight_all(compose->gtkaspell);
10316 static void compose_check_backwards(GtkAction *action, gpointer data)
10318 Compose *compose = (Compose *)data;
10319 if (compose->gtkaspell)
10320 gtkaspell_check_backwards(compose->gtkaspell);
10322 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10326 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10328 Compose *compose = (Compose *)data;
10329 if (compose->gtkaspell)
10330 gtkaspell_check_forwards_go(compose->gtkaspell);
10332 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10338 *\brief Guess originating forward account from MsgInfo and several
10339 * "common preference" settings. Return NULL if no guess.
10341 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10343 PrefsAccount *account = NULL;
10345 g_return_val_if_fail(msginfo, NULL);
10346 g_return_val_if_fail(msginfo->folder, NULL);
10347 g_return_val_if_fail(msginfo->folder->prefs, NULL);
10349 if (msginfo->folder->prefs->enable_default_account)
10350 account = account_find_from_id(msginfo->folder->prefs->default_account);
10353 account = msginfo->folder->folder->account;
10355 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10357 Xstrdup_a(to, msginfo->to, return NULL);
10358 extract_address(to);
10359 account = account_find_from_address(to, FALSE);
10362 if (!account && prefs_common.forward_account_autosel) {
10363 gchar cc[BUFFSIZE];
10364 if (!procheader_get_header_from_msginfo
10365 (msginfo, cc,sizeof cc , "Cc:")) {
10366 gchar *buf = cc + strlen("Cc:");
10367 extract_address(buf);
10368 account = account_find_from_address(buf, FALSE);
10372 if (!account && prefs_common.forward_account_autosel) {
10373 gchar deliveredto[BUFFSIZE];
10374 if (!procheader_get_header_from_msginfo
10375 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10376 gchar *buf = deliveredto + strlen("Delivered-To:");
10377 extract_address(buf);
10378 account = account_find_from_address(buf, FALSE);
10385 gboolean compose_close(Compose *compose)
10389 if (!g_mutex_trylock(compose->mutex)) {
10390 /* we have to wait for the (possibly deferred by auto-save)
10391 * drafting to be done, before destroying the compose under
10393 debug_print("waiting for drafting to finish...\n");
10394 compose_allow_user_actions(compose, FALSE);
10395 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10398 g_return_val_if_fail(compose, FALSE);
10399 gtkut_widget_get_uposition(compose->window, &x, &y);
10400 prefs_common.compose_x = x;
10401 prefs_common.compose_y = y;
10402 g_mutex_unlock(compose->mutex);
10403 compose_destroy(compose);
10408 * Add entry field for each address in list.
10409 * \param compose E-Mail composition object.
10410 * \param listAddress List of (formatted) E-Mail addresses.
10412 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10415 node = listAddress;
10417 addr = ( gchar * ) node->data;
10418 compose_entry_append( compose, addr, COMPOSE_TO );
10419 node = g_list_next( node );
10423 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10424 guint action, gboolean opening_multiple)
10426 gchar *body = NULL;
10427 GSList *new_msglist = NULL;
10428 MsgInfo *tmp_msginfo = NULL;
10429 gboolean originally_enc = FALSE;
10430 Compose *compose = NULL;
10432 g_return_if_fail(msgview != NULL);
10434 g_return_if_fail(msginfo_list != NULL);
10436 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10437 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10438 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10440 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10441 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10442 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10443 orig_msginfo, mimeinfo);
10444 if (tmp_msginfo != NULL) {
10445 new_msglist = g_slist_append(NULL, tmp_msginfo);
10447 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10448 tmp_msginfo->folder = orig_msginfo->folder;
10449 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10450 if (orig_msginfo->tags)
10451 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10456 if (!opening_multiple)
10457 body = messageview_get_selection(msgview);
10460 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10461 procmsg_msginfo_free(tmp_msginfo);
10462 g_slist_free(new_msglist);
10464 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10466 if (compose && originally_enc) {
10467 compose_force_encryption(compose, compose->account, FALSE);
10473 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10476 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
10477 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10478 GSList *cur = msginfo_list;
10479 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10480 "messages. Opening the windows "
10481 "could take some time. Do you "
10482 "want to continue?"),
10483 g_slist_length(msginfo_list));
10484 if (g_slist_length(msginfo_list) > 9
10485 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10486 != G_ALERTALTERNATE) {
10491 /* We'll open multiple compose windows */
10492 /* let the WM place the next windows */
10493 compose_force_window_origin = FALSE;
10494 for (; cur; cur = cur->next) {
10496 tmplist.data = cur->data;
10497 tmplist.next = NULL;
10498 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10500 compose_force_window_origin = TRUE;
10502 /* forwarding multiple mails as attachments is done via a
10503 * single compose window */
10504 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10508 void compose_set_position(Compose *compose, gint pos)
10510 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10512 gtkut_text_view_set_position(text, pos);
10515 gboolean compose_search_string(Compose *compose,
10516 const gchar *str, gboolean case_sens)
10518 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10520 return gtkut_text_view_search_string(text, str, case_sens);
10523 gboolean compose_search_string_backward(Compose *compose,
10524 const gchar *str, gboolean case_sens)
10526 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10528 return gtkut_text_view_search_string_backward(text, str, case_sens);
10531 /* allocate a msginfo structure and populate its data from a compose data structure */
10532 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10534 MsgInfo *newmsginfo;
10536 gchar buf[BUFFSIZE];
10538 g_return_val_if_fail( compose != NULL, NULL );
10540 newmsginfo = procmsg_msginfo_new();
10543 get_rfc822_date(buf, sizeof(buf));
10544 newmsginfo->date = g_strdup(buf);
10547 if (compose->from_name) {
10548 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10549 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10553 if (compose->subject_entry)
10554 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10556 /* to, cc, reply-to, newsgroups */
10557 for (list = compose->header_list; list; list = list->next) {
10558 gchar *header = gtk_editable_get_chars(
10560 GTK_BIN(((ComposeHeaderEntry *)list->data)->combo)->child), 0, -1);
10561 gchar *entry = gtk_editable_get_chars(
10562 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10564 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10565 if ( newmsginfo->to == NULL ) {
10566 newmsginfo->to = g_strdup(entry);
10567 } else if (entry && *entry) {
10568 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10569 g_free(newmsginfo->to);
10570 newmsginfo->to = tmp;
10573 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10574 if ( newmsginfo->cc == NULL ) {
10575 newmsginfo->cc = g_strdup(entry);
10576 } else if (entry && *entry) {
10577 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10578 g_free(newmsginfo->cc);
10579 newmsginfo->cc = tmp;
10582 if ( strcasecmp(header,
10583 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10584 if ( newmsginfo->newsgroups == NULL ) {
10585 newmsginfo->newsgroups = g_strdup(entry);
10586 } else if (entry && *entry) {
10587 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10588 g_free(newmsginfo->newsgroups);
10589 newmsginfo->newsgroups = tmp;
10597 /* other data is unset */
10603 /* update compose's dictionaries from folder dict settings */
10604 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10605 FolderItem *folder_item)
10607 g_return_if_fail(compose != NULL);
10609 if (compose->gtkaspell && folder_item && folder_item->prefs) {
10610 FolderItemPrefs *prefs = folder_item->prefs;
10612 if (prefs->enable_default_dictionary)
10613 gtkaspell_change_dict(compose->gtkaspell,
10614 prefs->default_dictionary, FALSE);
10615 if (folder_item->prefs->enable_default_alt_dictionary)
10616 gtkaspell_change_alt_dict(compose->gtkaspell,
10617 prefs->default_alt_dictionary);
10618 if (prefs->enable_default_dictionary
10619 || prefs->enable_default_alt_dictionary)
10620 compose_spell_menu_changed(compose);