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;
1961 gboolean autowrap = prefs_common.autowrap;
1963 g_return_val_if_fail(msginfo != NULL, NULL);
1964 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1966 if (compose_put_existing_to_front(msginfo)) {
1970 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
1971 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
1972 gchar queueheader_buf[BUFFSIZE];
1975 /* Select Account from queue headers */
1976 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1977 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
1978 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
1979 account = account_find_from_id(id);
1981 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1982 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
1983 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
1984 account = account_find_from_id(id);
1986 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1987 sizeof(queueheader_buf), "NAID:")) {
1988 id = atoi(&queueheader_buf[strlen("NAID:")]);
1989 account = account_find_from_id(id);
1991 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1992 sizeof(queueheader_buf), "MAID:")) {
1993 id = atoi(&queueheader_buf[strlen("MAID:")]);
1994 account = account_find_from_id(id);
1996 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1997 sizeof(queueheader_buf), "S:")) {
1998 account = account_find_from_address(queueheader_buf, FALSE);
2000 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2001 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2002 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2003 use_signing = param;
2006 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2007 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2008 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2009 use_signing = param;
2012 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2013 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2014 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2015 use_encryption = param;
2017 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2018 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2019 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2020 use_encryption = param;
2022 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2023 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2024 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2027 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2028 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2029 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2031 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2032 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2033 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2035 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2036 sizeof(queueheader_buf), "X-Priority: ")) {
2037 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2040 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2041 sizeof(queueheader_buf), "RMID:")) {
2042 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2043 if (tokens[0] && tokens[1] && tokens[2]) {
2044 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2045 if (orig_item != NULL) {
2046 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2051 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2052 sizeof(queueheader_buf), "FMID:")) {
2053 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2054 if (tokens[0] && tokens[1] && tokens[2]) {
2055 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2056 if (orig_item != NULL) {
2057 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2063 account = msginfo->folder->folder->account;
2066 if (!account && prefs_common.reedit_account_autosel) {
2067 gchar from[BUFFSIZE];
2068 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2069 extract_address(from);
2070 account = account_find_from_address(from, FALSE);
2074 account = cur_account;
2076 g_return_val_if_fail(account != NULL, NULL);
2078 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2080 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2081 compose->autowrap = autowrap;
2082 compose->replyinfo = replyinfo;
2083 compose->fwdinfo = fwdinfo;
2085 compose->updating = TRUE;
2086 compose->priority = priority;
2088 if (privacy_system != NULL) {
2089 compose->privacy_system = privacy_system;
2090 compose_use_signing(compose, use_signing);
2091 compose_use_encryption(compose, use_encryption);
2092 compose_update_privacy_system_menu_item(compose, FALSE);
2094 activate_privacy_system(compose, account, FALSE);
2097 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2099 compose_extract_original_charset(compose);
2101 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2102 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2103 gchar queueheader_buf[BUFFSIZE];
2105 /* Set message save folder */
2106 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2109 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2110 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
2111 gtk_editable_insert_text(GTK_EDITABLE(compose->savemsg_entry), &queueheader_buf[4], strlen(&queueheader_buf[4]), &startpos);
2113 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2114 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2116 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2121 if (compose_parse_header(compose, msginfo) < 0) {
2122 compose->updating = FALSE;
2123 compose_destroy(compose);
2126 compose_reedit_set_entry(compose, msginfo);
2128 textview = GTK_TEXT_VIEW(compose->text);
2129 textbuf = gtk_text_view_get_buffer(textview);
2130 compose_create_tags(textview, compose);
2132 mark = gtk_text_buffer_get_insert(textbuf);
2133 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2135 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2136 G_CALLBACK(compose_changed_cb),
2139 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2140 fp = procmime_get_first_encrypted_text_content(msginfo);
2142 compose_force_encryption(compose, account, TRUE);
2145 fp = procmime_get_first_text_content(msginfo);
2148 g_warning("Can't get text part\n");
2152 gboolean prev_autowrap = compose->autowrap;
2153 GtkTextBuffer *buffer = textbuf;
2155 while (fgets(buf, sizeof(buf), fp) != NULL) {
2157 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2163 compose_attach_parts(compose, msginfo);
2165 compose_colorize_signature(compose);
2167 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2168 G_CALLBACK(compose_changed_cb),
2171 gtk_widget_grab_focus(compose->text);
2173 if (prefs_common.auto_exteditor) {
2174 compose_exec_ext_editor(compose);
2176 compose->modified = FALSE;
2177 compose_set_title(compose);
2179 compose->updating = FALSE;
2180 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2181 SCROLL_TO_CURSOR(compose);
2183 if (compose->deferred_destroy) {
2184 compose_destroy(compose);
2188 compose->sig_str = compose_get_signature_str(compose);
2193 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2200 g_return_val_if_fail(msginfo != NULL, NULL);
2203 account = account_get_reply_account(msginfo,
2204 prefs_common.reply_account_autosel);
2205 g_return_val_if_fail(account != NULL, NULL);
2207 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2209 compose->updating = TRUE;
2211 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2212 compose->replyinfo = NULL;
2213 compose->fwdinfo = NULL;
2215 compose_show_first_last_header(compose, TRUE);
2217 gtk_widget_grab_focus(compose->header_last->entry);
2219 filename = procmsg_get_message_file(msginfo);
2221 if (filename == NULL) {
2222 compose->updating = FALSE;
2223 compose_destroy(compose);
2228 compose->redirect_filename = filename;
2230 /* Set save folder */
2231 item = msginfo->folder;
2232 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2233 gchar *folderidentifier;
2235 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2236 folderidentifier = folder_item_get_identifier(item);
2237 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
2238 g_free(folderidentifier);
2241 compose_attach_parts(compose, msginfo);
2243 if (msginfo->subject)
2244 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2246 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2248 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2249 _("Message redirect format error at line %d."));
2250 quote_fmt_reset_vartable();
2251 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2253 compose_colorize_signature(compose);
2256 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2257 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2258 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2260 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2261 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2262 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2263 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2264 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2265 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2266 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2267 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2269 if (compose->toolbar->draft_btn)
2270 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2271 if (compose->toolbar->insert_btn)
2272 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2273 if (compose->toolbar->attach_btn)
2274 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2275 if (compose->toolbar->sig_btn)
2276 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2277 if (compose->toolbar->exteditor_btn)
2278 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2279 if (compose->toolbar->linewrap_current_btn)
2280 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2281 if (compose->toolbar->linewrap_all_btn)
2282 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2284 compose->modified = FALSE;
2285 compose_set_title(compose);
2286 compose->updating = FALSE;
2287 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2288 SCROLL_TO_CURSOR(compose);
2290 if (compose->deferred_destroy) {
2291 compose_destroy(compose);
2298 GList *compose_get_compose_list(void)
2300 return compose_list;
2303 void compose_entry_append(Compose *compose, const gchar *address,
2304 ComposeEntryType type)
2306 const gchar *header;
2308 gboolean in_quote = FALSE;
2309 if (!address || *address == '\0') return;
2316 header = N_("Bcc:");
2318 case COMPOSE_REPLYTO:
2319 header = N_("Reply-To:");
2321 case COMPOSE_NEWSGROUPS:
2322 header = N_("Newsgroups:");
2324 case COMPOSE_FOLLOWUPTO:
2325 header = N_( "Followup-To:");
2332 header = prefs_common_translated_header_name(header);
2334 cur = begin = (gchar *)address;
2336 /* we separate the line by commas, but not if we're inside a quoted
2338 while (*cur != '\0') {
2340 in_quote = !in_quote;
2341 if (*cur == ',' && !in_quote) {
2342 gchar *tmp = g_strdup(begin);
2344 tmp[cur-begin]='\0';
2347 while (*tmp == ' ' || *tmp == '\t')
2349 compose_add_header_entry(compose, header, tmp);
2356 gchar *tmp = g_strdup(begin);
2358 tmp[cur-begin]='\0';
2361 while (*tmp == ' ' || *tmp == '\t')
2363 compose_add_header_entry(compose, header, tmp);
2368 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2370 static GdkColor yellow;
2371 static GdkColor black;
2372 static gboolean yellow_initialised = FALSE;
2376 if (!yellow_initialised) {
2377 gdk_color_parse("#f5f6be", &yellow);
2378 gdk_color_parse("#000000", &black);
2379 yellow_initialised = gdk_colormap_alloc_color(
2380 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2381 yellow_initialised &= gdk_colormap_alloc_color(
2382 gdk_colormap_get_system(), &black, FALSE, TRUE);
2385 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2386 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2387 if (gtk_entry_get_text(entry) &&
2388 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2389 if (yellow_initialised) {
2390 gtk_widget_modify_base(
2391 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2392 GTK_STATE_NORMAL, &yellow);
2393 gtk_widget_modify_text(
2394 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2395 GTK_STATE_NORMAL, &black);
2401 void compose_toolbar_cb(gint action, gpointer data)
2403 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2404 Compose *compose = (Compose*)toolbar_item->parent;
2406 g_return_if_fail(compose != NULL);
2410 compose_send_cb(NULL, compose);
2413 compose_send_later_cb(NULL, compose);
2416 compose_draft(compose, COMPOSE_QUIT_EDITING);
2419 compose_insert_file_cb(NULL, compose);
2422 compose_attach_cb(NULL, compose);
2425 compose_insert_sig(compose, FALSE);
2428 compose_ext_editor_cb(NULL, compose);
2430 case A_LINEWRAP_CURRENT:
2431 compose_beautify_paragraph(compose, NULL, TRUE);
2433 case A_LINEWRAP_ALL:
2434 compose_wrap_all_full(compose, TRUE);
2437 compose_address_cb(NULL, compose);
2440 case A_CHECK_SPELLING:
2441 compose_check_all(NULL, compose);
2449 static void compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2454 gchar *subject = NULL;
2458 gchar **attach = NULL;
2460 /* get mailto parts but skip from */
2461 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2464 compose_entry_append(compose, to, to_type);
2466 compose_entry_append(compose, cc, COMPOSE_CC);
2468 compose_entry_append(compose, bcc, COMPOSE_BCC);
2470 if (!g_utf8_validate (subject, -1, NULL)) {
2471 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2472 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2475 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2479 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2480 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2483 gboolean prev_autowrap = compose->autowrap;
2485 compose->autowrap = FALSE;
2487 mark = gtk_text_buffer_get_insert(buffer);
2488 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2490 if (!g_utf8_validate (body, -1, NULL)) {
2491 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2492 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2495 gtk_text_buffer_insert(buffer, &iter, body, -1);
2497 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2499 compose->autowrap = prev_autowrap;
2500 if (compose->autowrap)
2501 compose_wrap_all(compose);
2505 gint i = 0, att = 0;
2506 gchar *warn_files = NULL;
2507 while (attach[i] != NULL) {
2508 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2509 if (utf8_filename) {
2510 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2511 gchar *tmp = g_strdup_printf("%s%s\n",
2512 warn_files?warn_files:"",
2518 g_free(utf8_filename);
2520 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2525 alertpanel_notice(ngettext(
2526 "The following file has been attached: \n%s",
2527 "The following files have been attached: \n%s", att), warn_files);
2539 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2541 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2542 {"Cc:", NULL, TRUE},
2543 {"References:", NULL, FALSE},
2544 {"Bcc:", NULL, TRUE},
2545 {"Newsgroups:", NULL, TRUE},
2546 {"Followup-To:", NULL, TRUE},
2547 {"List-Post:", NULL, FALSE},
2548 {"X-Priority:", NULL, FALSE},
2549 {NULL, NULL, FALSE}};
2565 g_return_val_if_fail(msginfo != NULL, -1);
2567 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2568 procheader_get_header_fields(fp, hentry);
2571 if (hentry[H_REPLY_TO].body != NULL) {
2572 if (hentry[H_REPLY_TO].body[0] != '\0') {
2574 conv_unmime_header(hentry[H_REPLY_TO].body,
2577 g_free(hentry[H_REPLY_TO].body);
2578 hentry[H_REPLY_TO].body = NULL;
2580 if (hentry[H_CC].body != NULL) {
2581 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2582 g_free(hentry[H_CC].body);
2583 hentry[H_CC].body = NULL;
2585 if (hentry[H_REFERENCES].body != NULL) {
2586 if (compose->mode == COMPOSE_REEDIT)
2587 compose->references = hentry[H_REFERENCES].body;
2589 compose->references = compose_parse_references
2590 (hentry[H_REFERENCES].body, msginfo->msgid);
2591 g_free(hentry[H_REFERENCES].body);
2593 hentry[H_REFERENCES].body = NULL;
2595 if (hentry[H_BCC].body != NULL) {
2596 if (compose->mode == COMPOSE_REEDIT)
2598 conv_unmime_header(hentry[H_BCC].body, NULL);
2599 g_free(hentry[H_BCC].body);
2600 hentry[H_BCC].body = NULL;
2602 if (hentry[H_NEWSGROUPS].body != NULL) {
2603 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2604 hentry[H_NEWSGROUPS].body = NULL;
2606 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2607 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2608 compose->followup_to =
2609 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2612 g_free(hentry[H_FOLLOWUP_TO].body);
2613 hentry[H_FOLLOWUP_TO].body = NULL;
2615 if (hentry[H_LIST_POST].body != NULL) {
2618 extract_address(hentry[H_LIST_POST].body);
2619 if (hentry[H_LIST_POST].body[0] != '\0') {
2620 scan_mailto_url(hentry[H_LIST_POST].body,
2621 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2623 g_free(compose->ml_post);
2624 compose->ml_post = to;
2627 g_free(hentry[H_LIST_POST].body);
2628 hentry[H_LIST_POST].body = NULL;
2631 /* CLAWS - X-Priority */
2632 if (compose->mode == COMPOSE_REEDIT)
2633 if (hentry[H_X_PRIORITY].body != NULL) {
2636 priority = atoi(hentry[H_X_PRIORITY].body);
2637 g_free(hentry[H_X_PRIORITY].body);
2639 hentry[H_X_PRIORITY].body = NULL;
2641 if (priority < PRIORITY_HIGHEST ||
2642 priority > PRIORITY_LOWEST)
2643 priority = PRIORITY_NORMAL;
2645 compose->priority = priority;
2648 if (compose->mode == COMPOSE_REEDIT) {
2649 if (msginfo->inreplyto && *msginfo->inreplyto)
2650 compose->inreplyto = g_strdup(msginfo->inreplyto);
2654 if (msginfo->msgid && *msginfo->msgid)
2655 compose->inreplyto = g_strdup(msginfo->msgid);
2657 if (!compose->references) {
2658 if (msginfo->msgid && *msginfo->msgid) {
2659 if (msginfo->inreplyto && *msginfo->inreplyto)
2660 compose->references =
2661 g_strdup_printf("<%s>\n\t<%s>",
2665 compose->references =
2666 g_strconcat("<", msginfo->msgid, ">",
2668 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2669 compose->references =
2670 g_strconcat("<", msginfo->inreplyto, ">",
2678 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2680 GSList *ref_id_list, *cur;
2684 ref_id_list = references_list_append(NULL, ref);
2685 if (!ref_id_list) return NULL;
2686 if (msgid && *msgid)
2687 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2692 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2693 /* "<" + Message-ID + ">" + CR+LF+TAB */
2694 len += strlen((gchar *)cur->data) + 5;
2696 if (len > MAX_REFERENCES_LEN) {
2697 /* remove second message-ID */
2698 if (ref_id_list && ref_id_list->next &&
2699 ref_id_list->next->next) {
2700 g_free(ref_id_list->next->data);
2701 ref_id_list = g_slist_remove
2702 (ref_id_list, ref_id_list->next->data);
2704 slist_free_strings(ref_id_list);
2705 g_slist_free(ref_id_list);
2712 new_ref = g_string_new("");
2713 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2714 if (new_ref->len > 0)
2715 g_string_append(new_ref, "\n\t");
2716 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2719 slist_free_strings(ref_id_list);
2720 g_slist_free(ref_id_list);
2722 new_ref_str = new_ref->str;
2723 g_string_free(new_ref, FALSE);
2728 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2729 const gchar *fmt, const gchar *qmark,
2730 const gchar *body, gboolean rewrap,
2731 gboolean need_unescape,
2732 const gchar *err_msg)
2734 MsgInfo* dummyinfo = NULL;
2735 gchar *quote_str = NULL;
2737 gboolean prev_autowrap;
2738 const gchar *trimmed_body = body;
2739 gint cursor_pos = -1;
2740 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2741 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2746 SIGNAL_BLOCK(buffer);
2749 dummyinfo = compose_msginfo_new_from_compose(compose);
2750 msginfo = dummyinfo;
2753 if (qmark != NULL) {
2755 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2756 compose->gtkaspell);
2758 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2760 quote_fmt_scan_string(qmark);
2763 buf = quote_fmt_get_buffer();
2765 alertpanel_error(_("Quote mark format error."));
2767 Xstrdup_a(quote_str, buf, goto error)
2770 if (fmt && *fmt != '\0') {
2773 while (*trimmed_body == '\n')
2777 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2778 compose->gtkaspell);
2780 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2782 if (need_unescape) {
2785 /* decode \-escape sequences in the internal representation of the quote format */
2786 tmp = malloc(strlen(fmt)+1);
2787 pref_get_unescaped_pref(tmp, fmt);
2788 quote_fmt_scan_string(tmp);
2792 quote_fmt_scan_string(fmt);
2796 buf = quote_fmt_get_buffer();
2798 gint line = quote_fmt_get_line();
2799 alertpanel_error(err_msg, line);
2805 prev_autowrap = compose->autowrap;
2806 compose->autowrap = FALSE;
2808 mark = gtk_text_buffer_get_insert(buffer);
2809 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2810 if (g_utf8_validate(buf, -1, NULL)) {
2811 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2813 gchar *tmpout = NULL;
2814 tmpout = conv_codeset_strdup
2815 (buf, conv_get_locale_charset_str_no_utf8(),
2817 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2819 tmpout = g_malloc(strlen(buf)*2+1);
2820 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2822 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2826 cursor_pos = quote_fmt_get_cursor_pos();
2827 if (cursor_pos == -1)
2828 cursor_pos = gtk_text_iter_get_offset(&iter);
2829 compose->set_cursor_pos = cursor_pos;
2831 gtk_text_buffer_get_start_iter(buffer, &iter);
2832 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2833 gtk_text_buffer_place_cursor(buffer, &iter);
2835 compose->autowrap = prev_autowrap;
2836 if (compose->autowrap && rewrap)
2837 compose_wrap_all(compose);
2844 SIGNAL_UNBLOCK(buffer);
2846 procmsg_msginfo_free( dummyinfo );
2851 /* if ml_post is of type addr@host and from is of type
2852 * addr-anything@host, return TRUE
2854 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2856 gchar *left_ml = NULL;
2857 gchar *right_ml = NULL;
2858 gchar *left_from = NULL;
2859 gchar *right_from = NULL;
2860 gboolean result = FALSE;
2862 if (!ml_post || !from)
2865 left_ml = g_strdup(ml_post);
2866 if (strstr(left_ml, "@")) {
2867 right_ml = strstr(left_ml, "@")+1;
2868 *(strstr(left_ml, "@")) = '\0';
2871 left_from = g_strdup(from);
2872 if (strstr(left_from, "@")) {
2873 right_from = strstr(left_from, "@")+1;
2874 *(strstr(left_from, "@")) = '\0';
2877 if (left_ml && left_from && right_ml && right_from
2878 && !strncmp(left_from, left_ml, strlen(left_ml))
2879 && !strcmp(right_from, right_ml)) {
2888 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2890 gchar *my_addr1, *my_addr2;
2892 if (!addr1 || !addr2)
2895 Xstrdup_a(my_addr1, addr1, return FALSE);
2896 Xstrdup_a(my_addr2, addr2, return FALSE);
2898 extract_address(my_addr1);
2899 extract_address(my_addr2);
2901 return !strcasecmp(my_addr1, my_addr2);
2904 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2905 gboolean to_all, gboolean to_ml,
2907 gboolean followup_and_reply_to)
2909 GSList *cc_list = NULL;
2912 gchar *replyto = NULL;
2913 GHashTable *to_table;
2915 gboolean reply_to_ml = FALSE;
2916 gboolean default_reply_to = FALSE;
2918 g_return_if_fail(compose->account != NULL);
2919 g_return_if_fail(msginfo != NULL);
2921 reply_to_ml = to_ml && compose->ml_post;
2923 default_reply_to = msginfo->folder &&
2924 msginfo->folder->prefs->enable_default_reply_to;
2926 if (compose->account->protocol != A_NNTP) {
2927 if (reply_to_ml && !default_reply_to) {
2929 gboolean is_subscr = is_subscription(compose->ml_post,
2932 /* normal answer to ml post with a reply-to */
2933 compose_entry_append(compose,
2936 if (compose->replyto
2937 && !same_address(compose->ml_post, compose->replyto))
2938 compose_entry_append(compose,
2942 /* answer to subscription confirmation */
2943 if (compose->replyto)
2944 compose_entry_append(compose,
2947 else if (msginfo->from)
2948 compose_entry_append(compose,
2953 else if (!(to_all || to_sender) && default_reply_to) {
2954 compose_entry_append(compose,
2955 msginfo->folder->prefs->default_reply_to,
2957 compose_entry_mark_default_to(compose,
2958 msginfo->folder->prefs->default_reply_to);
2963 Xstrdup_a(tmp1, msginfo->from, return);
2964 extract_address(tmp1);
2965 if (to_all || to_sender ||
2966 !account_find_from_address(tmp1, FALSE))
2967 compose_entry_append(compose,
2968 (compose->replyto && !to_sender)
2969 ? compose->replyto :
2970 msginfo->from ? msginfo->from : "",
2972 else if (!to_all && !to_sender) {
2973 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2974 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
2975 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2976 if (compose->replyto) {
2977 compose_entry_append(compose,
2981 compose_entry_append(compose,
2982 msginfo->from ? msginfo->from : "",
2986 /* replying to own mail, use original recp */
2987 compose_entry_append(compose,
2988 msginfo->to ? msginfo->to : "",
2990 compose_entry_append(compose,
2991 msginfo->cc ? msginfo->cc : "",
2997 if (to_sender || (compose->followup_to &&
2998 !strncmp(compose->followup_to, "poster", 6)))
2999 compose_entry_append
3001 (compose->replyto ? compose->replyto :
3002 msginfo->from ? msginfo->from : ""),
3005 else if (followup_and_reply_to || to_all) {
3006 compose_entry_append
3008 (compose->replyto ? compose->replyto :
3009 msginfo->from ? msginfo->from : ""),
3012 compose_entry_append
3014 compose->followup_to ? compose->followup_to :
3015 compose->newsgroups ? compose->newsgroups : "",
3016 COMPOSE_NEWSGROUPS);
3019 compose_entry_append
3021 compose->followup_to ? compose->followup_to :
3022 compose->newsgroups ? compose->newsgroups : "",
3023 COMPOSE_NEWSGROUPS);
3026 if (msginfo->subject && *msginfo->subject) {
3030 buf = p = g_strdup(msginfo->subject);
3031 p += subject_get_prefix_length(p);
3032 memmove(buf, p, strlen(p) + 1);
3034 buf2 = g_strdup_printf("Re: %s", buf);
3035 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3040 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3042 if (to_ml && compose->ml_post) return;
3043 if (!to_all || compose->account->protocol == A_NNTP) return;
3045 if (compose->replyto) {
3046 Xstrdup_a(replyto, compose->replyto, return);
3047 extract_address(replyto);
3049 if (msginfo->from) {
3050 Xstrdup_a(from, msginfo->from, return);
3051 extract_address(from);
3054 if (replyto && from)
3055 cc_list = address_list_append_with_comments(cc_list, from);
3056 if (to_all && msginfo->folder &&
3057 msginfo->folder->prefs->enable_default_reply_to)
3058 cc_list = address_list_append_with_comments(cc_list,
3059 msginfo->folder->prefs->default_reply_to);
3060 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3061 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3063 to_table = g_hash_table_new(g_str_hash, g_str_equal);
3065 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
3066 if (compose->account) {
3067 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
3068 GINT_TO_POINTER(1));
3070 /* remove address on To: and that of current account */
3071 for (cur = cc_list; cur != NULL; ) {
3072 GSList *next = cur->next;
3075 addr = g_utf8_strdown(cur->data, -1);
3076 extract_address(addr);
3078 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3079 cc_list = g_slist_remove(cc_list, cur->data);
3081 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3085 hash_free_strings(to_table);
3086 g_hash_table_destroy(to_table);
3089 for (cur = cc_list; cur != NULL; cur = cur->next)
3090 compose_entry_append(compose, (gchar *)cur->data,
3092 slist_free_strings(cc_list);
3093 g_slist_free(cc_list);
3098 #define SET_ENTRY(entry, str) \
3101 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3104 #define SET_ADDRESS(type, str) \
3107 compose_entry_append(compose, str, type); \
3110 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3112 g_return_if_fail(msginfo != NULL);
3114 SET_ENTRY(subject_entry, msginfo->subject);
3115 SET_ENTRY(from_name, msginfo->from);
3116 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3117 SET_ADDRESS(COMPOSE_CC, compose->cc);
3118 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3119 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3120 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3121 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3123 compose_update_priority_menu_item(compose);
3124 compose_update_privacy_system_menu_item(compose, FALSE);
3125 compose_show_first_last_header(compose, TRUE);
3131 static void compose_insert_sig(Compose *compose, gboolean replace)
3133 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3134 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3136 GtkTextIter iter, iter_end;
3137 gint cur_pos, ins_pos;
3138 gboolean prev_autowrap;
3139 gboolean found = FALSE;
3140 gboolean exists = FALSE;
3142 g_return_if_fail(compose->account != NULL);
3146 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3147 G_CALLBACK(compose_changed_cb),
3150 mark = gtk_text_buffer_get_insert(buffer);
3151 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3152 cur_pos = gtk_text_iter_get_offset (&iter);
3155 gtk_text_buffer_get_end_iter(buffer, &iter);
3157 exists = (compose->sig_str != NULL);
3160 GtkTextIter first_iter, start_iter, end_iter;
3162 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3164 if (!exists || compose->sig_str[0] == '\0')
3167 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3168 compose->signature_tag);
3171 /* include previous \n\n */
3172 gtk_text_iter_backward_chars(&first_iter, 1);
3173 start_iter = first_iter;
3174 end_iter = first_iter;
3176 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3177 compose->signature_tag);
3178 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3179 compose->signature_tag);
3181 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3187 g_free(compose->sig_str);
3188 compose->sig_str = compose_get_signature_str(compose);
3190 cur_pos = gtk_text_iter_get_offset(&iter);
3192 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3193 g_free(compose->sig_str);
3194 compose->sig_str = NULL;
3196 if (compose->sig_inserted == FALSE)
3197 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3198 compose->sig_inserted = TRUE;
3200 cur_pos = gtk_text_iter_get_offset(&iter);
3201 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3203 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3204 gtk_text_iter_forward_chars(&iter, 1);
3205 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3206 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3208 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3209 cur_pos = gtk_text_buffer_get_char_count (buffer);
3212 /* put the cursor where it should be
3213 * either where the quote_fmt says, either where it was */
3214 if (compose->set_cursor_pos < 0)
3215 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3217 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3218 compose->set_cursor_pos);
3220 gtk_text_buffer_place_cursor(buffer, &iter);
3221 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3222 G_CALLBACK(compose_changed_cb),
3228 static gchar *compose_get_signature_str(Compose *compose)
3230 gchar *sig_body = NULL;
3231 gchar *sig_str = NULL;
3232 gchar *utf8_sig_str = NULL;
3234 g_return_val_if_fail(compose->account != NULL, NULL);
3236 if (!compose->account->sig_path)
3239 if (compose->account->sig_type == SIG_FILE) {
3240 if (!is_file_or_fifo_exist(compose->account->sig_path)) {
3241 g_warning("can't open signature file: %s\n",
3242 compose->account->sig_path);
3247 if (compose->account->sig_type == SIG_COMMAND)
3248 sig_body = get_command_output(compose->account->sig_path);
3252 tmp = file_read_to_str(compose->account->sig_path);
3255 sig_body = normalize_newlines(tmp);
3259 if (compose->account->sig_sep) {
3260 sig_str = g_strconcat("\n", compose->account->sig_sep, "\n", sig_body,
3264 sig_str = g_strconcat("\n", sig_body, NULL);
3267 if (g_utf8_validate(sig_str, -1, NULL) == TRUE)
3268 utf8_sig_str = sig_str;
3270 utf8_sig_str = conv_codeset_strdup
3271 (sig_str, conv_get_locale_charset_str_no_utf8(),
3277 return utf8_sig_str;
3280 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3283 GtkTextBuffer *buffer;
3286 const gchar *cur_encoding;
3287 gchar buf[BUFFSIZE];
3290 gboolean prev_autowrap;
3291 gboolean badtxt = FALSE;
3293 g_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3295 if ((fp = g_fopen(file, "rb")) == NULL) {
3296 FILE_OP_ERROR(file, "fopen");
3297 return COMPOSE_INSERT_READ_ERROR;
3300 prev_autowrap = compose->autowrap;
3301 compose->autowrap = FALSE;
3303 text = GTK_TEXT_VIEW(compose->text);
3304 buffer = gtk_text_view_get_buffer(text);
3305 mark = gtk_text_buffer_get_insert(buffer);
3306 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3308 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3309 G_CALLBACK(text_inserted),
3312 cur_encoding = conv_get_locale_charset_str_no_utf8();
3314 while (fgets(buf, sizeof(buf), fp) != NULL) {
3317 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3318 str = g_strdup(buf);
3320 str = conv_codeset_strdup
3321 (buf, cur_encoding, CS_INTERNAL);
3324 /* strip <CR> if DOS/Windows file,
3325 replace <CR> with <LF> if Macintosh file. */
3328 if (len > 0 && str[len - 1] != '\n') {
3330 if (str[len] == '\r') str[len] = '\n';
3333 gtk_text_buffer_insert(buffer, &iter, str, -1);
3337 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3338 G_CALLBACK(text_inserted),
3340 compose->autowrap = prev_autowrap;
3341 if (compose->autowrap)
3342 compose_wrap_all(compose);
3347 return COMPOSE_INSERT_INVALID_CHARACTER;
3349 return COMPOSE_INSERT_SUCCESS;
3352 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3353 const gchar *filename,
3354 const gchar *content_type)
3362 GtkListStore *store;
3364 gboolean has_binary = FALSE;
3366 if (!is_file_exist(file)) {
3367 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3368 gboolean result = FALSE;
3369 if (file_from_uri && is_file_exist(file_from_uri)) {
3370 result = compose_attach_append(
3371 compose, file_from_uri,
3375 g_free(file_from_uri);
3378 alertpanel_error("File %s doesn't exist\n", filename);
3381 if ((size = get_file_size(file)) < 0) {
3382 alertpanel_error("Can't get file size of %s\n", filename);
3386 alertpanel_error(_("File %s is empty."), filename);
3389 if ((fp = g_fopen(file, "rb")) == NULL) {
3390 alertpanel_error(_("Can't read %s."), filename);
3395 ainfo = g_new0(AttachInfo, 1);
3396 auto_ainfo = g_auto_pointer_new_with_free
3397 (ainfo, (GFreeFunc) compose_attach_info_free);
3398 ainfo->file = g_strdup(file);
3401 ainfo->content_type = g_strdup(content_type);
3402 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3404 MsgFlags flags = {0, 0};
3406 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3407 ainfo->encoding = ENC_7BIT;
3409 ainfo->encoding = ENC_8BIT;
3411 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3412 if (msginfo && msginfo->subject)
3413 name = g_strdup(msginfo->subject);
3415 name = g_path_get_basename(filename ? filename : file);
3417 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3419 procmsg_msginfo_free(msginfo);
3421 if (!g_ascii_strncasecmp(content_type, "text", 4))
3422 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3424 ainfo->encoding = ENC_BASE64;
3425 name = g_path_get_basename(filename ? filename : file);
3426 ainfo->name = g_strdup(name);
3430 ainfo->content_type = procmime_get_mime_type(file);
3431 if (!ainfo->content_type) {
3432 ainfo->content_type =
3433 g_strdup("application/octet-stream");
3434 ainfo->encoding = ENC_BASE64;
3435 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3437 procmime_get_encoding_for_text_file(file, &has_binary);
3439 ainfo->encoding = ENC_BASE64;
3440 name = g_path_get_basename(filename ? filename : file);
3441 ainfo->name = g_strdup(name);
3445 if (ainfo->name != NULL
3446 && !strcmp(ainfo->name, ".")) {
3447 g_free(ainfo->name);
3451 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3452 g_free(ainfo->content_type);
3453 ainfo->content_type = g_strdup("application/octet-stream");
3456 ainfo->size = (goffset)size;
3457 size_text = to_human_readable((goffset)size);
3459 store = GTK_LIST_STORE(gtk_tree_view_get_model
3460 (GTK_TREE_VIEW(compose->attach_clist)));
3462 gtk_list_store_append(store, &iter);
3463 gtk_list_store_set(store, &iter,
3464 COL_MIMETYPE, ainfo->content_type,
3465 COL_SIZE, size_text,
3466 COL_NAME, ainfo->name,
3468 COL_AUTODATA, auto_ainfo,
3471 g_auto_pointer_free(auto_ainfo);
3472 compose_attach_update_label(compose);
3476 static void compose_use_signing(Compose *compose, gboolean use_signing)
3478 compose->use_signing = use_signing;
3479 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3482 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3484 compose->use_encryption = use_encryption;
3485 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3488 #define NEXT_PART_NOT_CHILD(info) \
3490 node = info->node; \
3491 while (node->children) \
3492 node = g_node_last_child(node); \
3493 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3496 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3500 MimeInfo *firsttext = NULL;
3501 MimeInfo *encrypted = NULL;
3504 const gchar *partname = NULL;
3506 mimeinfo = procmime_scan_message(msginfo);
3507 if (!mimeinfo) return;
3509 if (mimeinfo->node->children == NULL) {
3510 procmime_mimeinfo_free_all(mimeinfo);
3514 /* find first content part */
3515 child = (MimeInfo *) mimeinfo->node->children->data;
3516 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3517 child = (MimeInfo *)child->node->children->data;
3519 if (child->type == MIMETYPE_TEXT) {
3521 debug_print("First text part found\n");
3522 } else if (compose->mode == COMPOSE_REEDIT &&
3523 child->type == MIMETYPE_APPLICATION &&
3524 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3525 encrypted = (MimeInfo *)child->node->parent->data;
3528 child = (MimeInfo *) mimeinfo->node->children->data;
3529 while (child != NULL) {
3532 if (child == encrypted) {
3533 /* skip this part of tree */
3534 NEXT_PART_NOT_CHILD(child);
3538 if (child->type == MIMETYPE_MULTIPART) {
3539 /* get the actual content */
3540 child = procmime_mimeinfo_next(child);
3544 if (child == firsttext) {
3545 child = procmime_mimeinfo_next(child);
3549 outfile = procmime_get_tmp_file_name(child);
3550 if ((err = procmime_get_part(outfile, child)) < 0)
3551 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3553 gchar *content_type;
3555 content_type = procmime_get_content_type_str(child->type, child->subtype);
3557 /* if we meet a pgp signature, we don't attach it, but
3558 * we force signing. */
3559 if ((strcmp(content_type, "application/pgp-signature") &&
3560 strcmp(content_type, "application/pkcs7-signature") &&
3561 strcmp(content_type, "application/x-pkcs7-signature"))
3562 || compose->mode == COMPOSE_REDIRECT) {
3563 partname = procmime_mimeinfo_get_parameter(child, "filename");
3564 if (partname == NULL)
3565 partname = procmime_mimeinfo_get_parameter(child, "name");
3566 if (partname == NULL)
3568 compose_attach_append(compose, outfile,
3569 partname, content_type);
3571 compose_force_signing(compose, compose->account);
3573 g_free(content_type);
3576 NEXT_PART_NOT_CHILD(child);
3578 procmime_mimeinfo_free_all(mimeinfo);
3581 #undef NEXT_PART_NOT_CHILD
3586 WAIT_FOR_INDENT_CHAR,
3587 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3590 /* return indent length, we allow:
3591 indent characters followed by indent characters or spaces/tabs,
3592 alphabets and numbers immediately followed by indent characters,
3593 and the repeating sequences of the above
3594 If quote ends with multiple spaces, only the first one is included. */
3595 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3596 const GtkTextIter *start, gint *len)
3598 GtkTextIter iter = *start;
3602 IndentState state = WAIT_FOR_INDENT_CHAR;
3605 gint alnum_count = 0;
3606 gint space_count = 0;
3609 if (prefs_common.quote_chars == NULL) {
3613 while (!gtk_text_iter_ends_line(&iter)) {
3614 wc = gtk_text_iter_get_char(&iter);
3615 if (g_unichar_iswide(wc))
3617 clen = g_unichar_to_utf8(wc, ch);
3621 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3622 is_space = g_unichar_isspace(wc);
3624 if (state == WAIT_FOR_INDENT_CHAR) {
3625 if (!is_indent && !g_unichar_isalnum(wc))
3628 quote_len += alnum_count + space_count + 1;
3629 alnum_count = space_count = 0;
3630 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3633 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3634 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3638 else if (is_indent) {
3639 quote_len += alnum_count + space_count + 1;
3640 alnum_count = space_count = 0;
3643 state = WAIT_FOR_INDENT_CHAR;
3647 gtk_text_iter_forward_char(&iter);
3650 if (quote_len > 0 && space_count > 0)
3656 if (quote_len > 0) {
3658 gtk_text_iter_forward_chars(&iter, quote_len);
3659 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3665 /* return TRUE if the line is itemized */
3666 static gboolean compose_is_itemized(GtkTextBuffer *buffer,
3667 const GtkTextIter *start)
3669 GtkTextIter iter = *start;
3674 if (gtk_text_iter_ends_line(&iter))
3678 wc = gtk_text_iter_get_char(&iter);
3679 if (!g_unichar_isspace(wc))
3681 gtk_text_iter_forward_char(&iter);
3682 if (gtk_text_iter_ends_line(&iter))
3686 clen = g_unichar_to_utf8(wc, ch);
3690 if (!strchr("*-+", ch[0]))
3693 gtk_text_iter_forward_char(&iter);
3694 if (gtk_text_iter_ends_line(&iter))
3696 wc = gtk_text_iter_get_char(&iter);
3697 if (g_unichar_isspace(wc))
3703 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3704 const GtkTextIter *start,
3705 GtkTextIter *break_pos,
3709 GtkTextIter iter = *start, line_end = *start;
3710 PangoLogAttr *attrs;
3717 gboolean can_break = FALSE;
3718 gboolean do_break = FALSE;
3719 gboolean was_white = FALSE;
3720 gboolean prev_dont_break = FALSE;
3722 gtk_text_iter_forward_to_line_end(&line_end);
3723 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3724 len = g_utf8_strlen(str, -1);
3728 g_warning("compose_get_line_break_pos: len = 0!\n");
3732 /* g_print("breaking line: %d: %s (len = %d)\n",
3733 gtk_text_iter_get_line(&iter), str, len); */
3735 attrs = g_new(PangoLogAttr, len + 1);
3737 pango_default_break(str, -1, NULL, attrs, len + 1);
3741 /* skip quote and leading spaces */
3742 for (i = 0; *p != '\0' && i < len; i++) {
3745 wc = g_utf8_get_char(p);
3746 if (i >= quote_len && !g_unichar_isspace(wc))
3748 if (g_unichar_iswide(wc))
3750 else if (*p == '\t')
3754 p = g_utf8_next_char(p);
3757 for (; *p != '\0' && i < len; i++) {
3758 PangoLogAttr *attr = attrs + i;
3762 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3765 was_white = attr->is_white;
3767 /* don't wrap URI */
3768 if ((uri_len = get_uri_len(p)) > 0) {
3770 if (pos > 0 && col > max_col) {
3780 wc = g_utf8_get_char(p);
3781 if (g_unichar_iswide(wc)) {
3783 if (prev_dont_break && can_break && attr->is_line_break)
3785 } else if (*p == '\t')
3789 if (pos > 0 && col > max_col) {
3794 if (*p == '-' || *p == '/')
3795 prev_dont_break = TRUE;
3797 prev_dont_break = FALSE;
3799 p = g_utf8_next_char(p);
3803 debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3808 *break_pos = *start;
3809 gtk_text_iter_set_line_offset(break_pos, pos);
3814 static gboolean compose_join_next_line(Compose *compose,
3815 GtkTextBuffer *buffer,
3817 const gchar *quote_str)
3819 GtkTextIter iter_ = *iter, cur, prev, next, end;
3820 PangoLogAttr attrs[3];
3822 gchar *next_quote_str;
3825 gboolean keep_cursor = FALSE;
3827 if (!gtk_text_iter_forward_line(&iter_) ||
3828 gtk_text_iter_ends_line(&iter_)) {
3831 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
3833 if ((quote_str || next_quote_str) &&
3834 strcmp2(quote_str, next_quote_str) != 0) {
3835 g_free(next_quote_str);
3838 g_free(next_quote_str);
3841 if (quote_len > 0) {
3842 gtk_text_iter_forward_chars(&end, quote_len);
3843 if (gtk_text_iter_ends_line(&end)) {
3848 /* don't join itemized lines */
3849 if (compose_is_itemized(buffer, &end)) {
3853 /* don't join signature separator */
3854 if (compose_is_sig_separator(compose, buffer, &iter_)) {
3857 /* delete quote str */
3859 gtk_text_buffer_delete(buffer, &iter_, &end);
3861 /* don't join line breaks put by the user */
3863 gtk_text_iter_backward_char(&cur);
3864 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
3865 gtk_text_iter_forward_char(&cur);
3869 gtk_text_iter_forward_char(&cur);
3870 /* delete linebreak and extra spaces */
3871 while (gtk_text_iter_backward_char(&cur)) {
3872 wc1 = gtk_text_iter_get_char(&cur);
3873 if (!g_unichar_isspace(wc1))
3878 while (!gtk_text_iter_ends_line(&cur)) {
3879 wc1 = gtk_text_iter_get_char(&cur);
3880 if (!g_unichar_isspace(wc1))
3882 gtk_text_iter_forward_char(&cur);
3885 if (!gtk_text_iter_equal(&prev, &next)) {
3888 mark = gtk_text_buffer_get_insert(buffer);
3889 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
3890 if (gtk_text_iter_equal(&prev, &cur))
3892 gtk_text_buffer_delete(buffer, &prev, &next);
3896 /* insert space if required */
3897 gtk_text_iter_backward_char(&prev);
3898 wc1 = gtk_text_iter_get_char(&prev);
3899 wc2 = gtk_text_iter_get_char(&next);
3900 gtk_text_iter_forward_char(&next);
3901 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
3902 pango_default_break(str, -1, NULL, attrs, 3);
3903 if (!attrs[1].is_line_break ||
3904 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
3905 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
3907 gtk_text_iter_backward_char(&iter_);
3908 gtk_text_buffer_place_cursor(buffer, &iter_);
3917 #define ADD_TXT_POS(bp_, ep_, pti_) \
3918 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
3919 last = last->next; \
3920 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
3921 last->next = NULL; \
3923 g_warning("alloc error scanning URIs\n"); \
3926 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
3928 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3929 GtkTextBuffer *buffer;
3930 GtkTextIter iter, break_pos, end_of_line;
3931 gchar *quote_str = NULL;
3933 gboolean wrap_quote = prefs_common.linewrap_quote;
3934 gboolean prev_autowrap = compose->autowrap;
3935 gint startq_offset = -1, noq_offset = -1;
3936 gint uri_start = -1, uri_stop = -1;
3937 gint nouri_start = -1, nouri_stop = -1;
3938 gint num_blocks = 0;
3939 gint quotelevel = -1;
3940 gboolean modified = force;
3941 gboolean removed = FALSE;
3942 gboolean modified_before_remove = FALSE;
3944 gboolean start = TRUE;
3949 if (compose->draft_timeout_tag == -2) {
3953 compose->autowrap = FALSE;
3955 buffer = gtk_text_view_get_buffer(text);
3956 undo_wrapping(compose->undostruct, TRUE);
3961 mark = gtk_text_buffer_get_insert(buffer);
3962 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3966 if (compose->draft_timeout_tag == -2) {
3967 if (gtk_text_iter_ends_line(&iter)) {
3968 while (gtk_text_iter_ends_line(&iter) &&
3969 gtk_text_iter_forward_line(&iter))
3972 while (gtk_text_iter_backward_line(&iter)) {
3973 if (gtk_text_iter_ends_line(&iter)) {
3974 gtk_text_iter_forward_line(&iter);
3980 /* move to line start */
3981 gtk_text_iter_set_line_offset(&iter, 0);
3983 /* go until paragraph end (empty line) */
3984 while (start || !gtk_text_iter_ends_line(&iter)) {
3985 gchar *scanpos = NULL;
3986 /* parse table - in order of priority */
3988 const gchar *needle; /* token */
3990 /* token search function */
3991 gchar *(*search) (const gchar *haystack,
3992 const gchar *needle);
3993 /* part parsing function */
3994 gboolean (*parse) (const gchar *start,
3995 const gchar *scanpos,
3999 /* part to URI function */
4000 gchar *(*build_uri) (const gchar *bp,
4004 static struct table parser[] = {
4005 {"http://", strcasestr, get_uri_part, make_uri_string},
4006 {"https://", strcasestr, get_uri_part, make_uri_string},
4007 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4008 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4009 {"www.", strcasestr, get_uri_part, make_http_string},
4010 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4011 {"@", strcasestr, get_email_part, make_email_string}
4013 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4014 gint last_index = PARSE_ELEMS;
4016 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4020 if (!prev_autowrap && num_blocks == 0) {
4022 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4023 G_CALLBACK(text_inserted),
4026 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4029 uri_start = uri_stop = -1;
4031 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4034 debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4035 if (startq_offset == -1)
4036 startq_offset = gtk_text_iter_get_offset(&iter);
4037 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4038 if (quotelevel > 2) {
4039 /* recycle colors */
4040 if (prefs_common.recycle_quote_colors)
4049 if (startq_offset == -1)
4050 noq_offset = gtk_text_iter_get_offset(&iter);
4054 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4057 if (gtk_text_iter_ends_line(&iter)) {
4059 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4060 prefs_common.linewrap_len,
4062 GtkTextIter prev, next, cur;
4064 if (prev_autowrap != FALSE || force) {
4065 compose->automatic_break = TRUE;
4067 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4068 compose->automatic_break = FALSE;
4069 } else if (quote_str && wrap_quote) {
4070 compose->automatic_break = TRUE;
4072 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4073 compose->automatic_break = FALSE;
4076 /* remove trailing spaces */
4078 gtk_text_iter_backward_char(&cur);
4080 while (!gtk_text_iter_starts_line(&cur)) {
4083 gtk_text_iter_backward_char(&cur);
4084 wc = gtk_text_iter_get_char(&cur);
4085 if (!g_unichar_isspace(wc))
4089 if (!gtk_text_iter_equal(&prev, &next)) {
4090 gtk_text_buffer_delete(buffer, &prev, &next);
4092 gtk_text_iter_forward_char(&break_pos);
4096 gtk_text_buffer_insert(buffer, &break_pos,
4100 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4102 /* move iter to current line start */
4103 gtk_text_iter_set_line_offset(&iter, 0);
4110 /* move iter to next line start */
4116 if (!prev_autowrap && num_blocks > 0) {
4118 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4119 G_CALLBACK(text_inserted),
4123 while (!gtk_text_iter_ends_line(&end_of_line)) {
4124 gtk_text_iter_forward_char(&end_of_line);
4126 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4128 nouri_start = gtk_text_iter_get_offset(&iter);
4129 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4131 walk_pos = gtk_text_iter_get_offset(&iter);
4132 /* FIXME: this looks phony. scanning for anything in the parse table */
4133 for (n = 0; n < PARSE_ELEMS; n++) {
4136 tmp = parser[n].search(walk, parser[n].needle);
4138 if (scanpos == NULL || tmp < scanpos) {
4147 /* check if URI can be parsed */
4148 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4149 (const gchar **)&ep, FALSE)
4150 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4154 strlen(parser[last_index].needle);
4157 uri_start = walk_pos + (bp - o_walk);
4158 uri_stop = walk_pos + (ep - o_walk);
4162 gtk_text_iter_forward_line(&iter);
4165 if (startq_offset != -1) {
4166 GtkTextIter startquote, endquote;
4167 gtk_text_buffer_get_iter_at_offset(
4168 buffer, &startquote, startq_offset);
4171 switch (quotelevel) {
4173 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4174 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4175 gtk_text_buffer_apply_tag_by_name(
4176 buffer, "quote0", &startquote, &endquote);
4177 gtk_text_buffer_remove_tag_by_name(
4178 buffer, "quote1", &startquote, &endquote);
4179 gtk_text_buffer_remove_tag_by_name(
4180 buffer, "quote2", &startquote, &endquote);
4185 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4186 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4187 gtk_text_buffer_apply_tag_by_name(
4188 buffer, "quote1", &startquote, &endquote);
4189 gtk_text_buffer_remove_tag_by_name(
4190 buffer, "quote0", &startquote, &endquote);
4191 gtk_text_buffer_remove_tag_by_name(
4192 buffer, "quote2", &startquote, &endquote);
4197 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4198 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4199 gtk_text_buffer_apply_tag_by_name(
4200 buffer, "quote2", &startquote, &endquote);
4201 gtk_text_buffer_remove_tag_by_name(
4202 buffer, "quote0", &startquote, &endquote);
4203 gtk_text_buffer_remove_tag_by_name(
4204 buffer, "quote1", &startquote, &endquote);
4210 } else if (noq_offset != -1) {
4211 GtkTextIter startnoquote, endnoquote;
4212 gtk_text_buffer_get_iter_at_offset(
4213 buffer, &startnoquote, noq_offset);
4216 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4217 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4218 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4219 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4220 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4221 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4222 gtk_text_buffer_remove_tag_by_name(
4223 buffer, "quote0", &startnoquote, &endnoquote);
4224 gtk_text_buffer_remove_tag_by_name(
4225 buffer, "quote1", &startnoquote, &endnoquote);
4226 gtk_text_buffer_remove_tag_by_name(
4227 buffer, "quote2", &startnoquote, &endnoquote);
4233 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4234 GtkTextIter nouri_start_iter, nouri_end_iter;
4235 gtk_text_buffer_get_iter_at_offset(
4236 buffer, &nouri_start_iter, nouri_start);
4237 gtk_text_buffer_get_iter_at_offset(
4238 buffer, &nouri_end_iter, nouri_stop);
4239 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4240 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4241 gtk_text_buffer_remove_tag_by_name(
4242 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4243 modified_before_remove = modified;
4248 if (uri_start >= 0 && uri_stop > 0) {
4249 GtkTextIter uri_start_iter, uri_end_iter, back;
4250 gtk_text_buffer_get_iter_at_offset(
4251 buffer, &uri_start_iter, uri_start);
4252 gtk_text_buffer_get_iter_at_offset(
4253 buffer, &uri_end_iter, uri_stop);
4254 back = uri_end_iter;
4255 gtk_text_iter_backward_char(&back);
4256 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4257 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4258 gtk_text_buffer_apply_tag_by_name(
4259 buffer, "link", &uri_start_iter, &uri_end_iter);
4261 if (removed && !modified_before_remove) {
4267 debug_print("not modified, out after %d lines\n", lines);
4272 debug_print("modified, out after %d lines\n", lines);
4276 undo_wrapping(compose->undostruct, FALSE);
4277 compose->autowrap = prev_autowrap;
4282 void compose_action_cb(void *data)
4284 Compose *compose = (Compose *)data;
4285 compose_wrap_all(compose);
4288 static void compose_wrap_all(Compose *compose)
4290 compose_wrap_all_full(compose, FALSE);
4293 static void compose_wrap_all_full(Compose *compose, gboolean force)
4295 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4296 GtkTextBuffer *buffer;
4298 gboolean modified = TRUE;
4300 buffer = gtk_text_view_get_buffer(text);
4302 gtk_text_buffer_get_start_iter(buffer, &iter);
4303 while (!gtk_text_iter_is_end(&iter) && modified)
4304 modified = compose_beautify_paragraph(compose, &iter, force);
4308 static void compose_set_title(Compose *compose)
4314 edited = compose->modified ? _(" [Edited]") : "";
4316 subject = gtk_editable_get_chars(
4317 GTK_EDITABLE(compose->subject_entry), 0, -1);
4319 #ifndef GENERIC_UMPC
4320 if (subject && strlen(subject))
4321 str = g_strdup_printf(_("%s - Compose message%s"),
4324 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4326 str = g_strdup(_("Compose message"));
4329 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4335 * compose_current_mail_account:
4337 * Find a current mail account (the currently selected account, or the
4338 * default account, if a news account is currently selected). If a
4339 * mail account cannot be found, display an error message.
4341 * Return value: Mail account, or NULL if not found.
4343 static PrefsAccount *
4344 compose_current_mail_account(void)
4348 if (cur_account && cur_account->protocol != A_NNTP)
4351 ac = account_get_default();
4352 if (!ac || ac->protocol == A_NNTP) {
4353 alertpanel_error(_("Account for sending mail is not specified.\n"
4354 "Please select a mail account before sending."));
4361 #define QUOTE_IF_REQUIRED(out, str) \
4363 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4367 len = strlen(str) + 3; \
4368 if ((__tmp = alloca(len)) == NULL) { \
4369 g_warning("can't allocate memory\n"); \
4370 g_string_free(header, TRUE); \
4373 g_snprintf(__tmp, len, "\"%s\"", str); \
4378 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4379 g_warning("can't allocate memory\n"); \
4380 g_string_free(header, TRUE); \
4383 strcpy(__tmp, str); \
4389 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4391 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4395 len = strlen(str) + 3; \
4396 if ((__tmp = alloca(len)) == NULL) { \
4397 g_warning("can't allocate memory\n"); \
4400 g_snprintf(__tmp, len, "\"%s\"", str); \
4405 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4406 g_warning("can't allocate memory\n"); \
4409 strcpy(__tmp, str); \
4415 static void compose_select_account(Compose *compose, PrefsAccount *account,
4420 g_return_if_fail(account != NULL);
4422 compose->account = account;
4424 if (account->name && *account->name) {
4426 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4427 from = g_strdup_printf("%s <%s>",
4428 buf, account->address);
4429 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4431 from = g_strdup_printf("<%s>",
4433 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4438 compose_set_title(compose);
4440 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4441 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4443 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4444 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4445 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4447 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4449 activate_privacy_system(compose, account, FALSE);
4451 if (!init && compose->mode != COMPOSE_REDIRECT) {
4452 undo_block(compose->undostruct);
4453 compose_insert_sig(compose, TRUE);
4454 undo_unblock(compose->undostruct);
4458 /* use account's dict info if set */
4459 if (compose->gtkaspell) {
4460 if (account->enable_default_dictionary)
4461 gtkaspell_change_dict(compose->gtkaspell,
4462 account->default_dictionary, FALSE);
4463 if (account->enable_default_alt_dictionary)
4464 gtkaspell_change_alt_dict(compose->gtkaspell,
4465 account->default_alt_dictionary);
4466 if (account->enable_default_dictionary
4467 || account->enable_default_alt_dictionary)
4468 compose_spell_menu_changed(compose);
4473 gboolean compose_check_for_valid_recipient(Compose *compose) {
4474 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4475 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4476 gboolean recipient_found = FALSE;
4480 /* free to and newsgroup list */
4481 slist_free_strings(compose->to_list);
4482 g_slist_free(compose->to_list);
4483 compose->to_list = NULL;
4485 slist_free_strings(compose->newsgroup_list);
4486 g_slist_free(compose->newsgroup_list);
4487 compose->newsgroup_list = NULL;
4489 /* search header entries for to and newsgroup entries */
4490 for (list = compose->header_list; list; list = list->next) {
4493 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4494 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4497 if (entry[0] != '\0') {
4498 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4499 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4500 compose->to_list = address_list_append(compose->to_list, entry);
4501 recipient_found = TRUE;
4504 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4505 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4506 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4507 recipient_found = TRUE;
4514 return recipient_found;
4517 static gboolean compose_check_for_set_recipients(Compose *compose)
4519 if (compose->account->set_autocc && compose->account->auto_cc) {
4520 gboolean found_other = FALSE;
4522 /* search header entries for to and newsgroup entries */
4523 for (list = compose->header_list; list; list = list->next) {
4526 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4527 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4530 if (strcmp(entry, compose->account->auto_cc)
4531 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4541 if (compose->batch) {
4542 gtk_widget_show_all(compose->window);
4544 aval = alertpanel(_("Send"),
4545 _("The only recipient is the default CC address. Send anyway?"),
4546 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4547 if (aval != G_ALERTALTERNATE)
4551 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4552 gboolean found_other = FALSE;
4554 /* search header entries for to and newsgroup entries */
4555 for (list = compose->header_list; list; list = list->next) {
4558 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4559 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4562 if (strcmp(entry, compose->account->auto_bcc)
4563 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4573 if (compose->batch) {
4574 gtk_widget_show_all(compose->window);
4576 aval = alertpanel(_("Send"),
4577 _("The only recipient is the default BCC address. Send anyway?"),
4578 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4579 if (aval != G_ALERTALTERNATE)
4586 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4590 if (compose_check_for_valid_recipient(compose) == FALSE) {
4591 if (compose->batch) {
4592 gtk_widget_show_all(compose->window);
4594 alertpanel_error(_("Recipient is not specified."));
4598 if (compose_check_for_set_recipients(compose) == FALSE) {
4602 if (!compose->batch) {
4603 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4604 if (*str == '\0' && check_everything == TRUE &&
4605 compose->mode != COMPOSE_REDIRECT) {
4607 gchar *button_label;
4610 if (compose->sending)
4611 button_label = _("+_Send");
4613 button_label = _("+_Queue");
4614 message = g_strdup_printf(_("Subject is empty. %s"),
4615 compose->sending?_("Send it anyway?"):
4616 _("Queue it anyway?"));
4618 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4619 GTK_STOCK_CANCEL, button_label, NULL);
4621 if (aval != G_ALERTALTERNATE)
4626 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4632 gint compose_send(Compose *compose)
4635 FolderItem *folder = NULL;
4637 gchar *msgpath = NULL;
4638 gboolean discard_window = FALSE;
4639 gchar *errstr = NULL;
4640 gchar *tmsgid = NULL;
4641 MainWindow *mainwin = mainwindow_get_mainwindow();
4642 gboolean queued_removed = FALSE;
4644 if (prefs_common.send_dialog_invisible
4645 || compose->batch == TRUE)
4646 discard_window = TRUE;
4648 compose_allow_user_actions (compose, FALSE);
4649 compose->sending = TRUE;
4651 if (compose_check_entries(compose, TRUE) == FALSE) {
4652 if (compose->batch) {
4653 gtk_widget_show_all(compose->window);
4659 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4662 if (compose->batch) {
4663 gtk_widget_show_all(compose->window);
4666 alertpanel_error(_("Could not queue message for sending:\n\n"
4667 "Charset conversion failed."));
4668 } else if (val == -5) {
4669 alertpanel_error(_("Could not queue message for sending:\n\n"
4670 "Couldn't get recipient encryption key."));
4671 } else if (val == -6) {
4673 } else if (val == -3) {
4674 if (privacy_peek_error())
4675 alertpanel_error(_("Could not queue message for sending:\n\n"
4676 "Signature failed: %s"), privacy_get_error());
4677 } else if (val == -2 && errno != 0) {
4678 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4680 alertpanel_error(_("Could not queue message for sending."));
4685 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4686 if (discard_window) {
4687 compose->sending = FALSE;
4688 compose_close(compose);
4689 /* No more compose access in the normal codepath
4690 * after this point! */
4695 alertpanel_error(_("The message was queued but could not be "
4696 "sent.\nUse \"Send queued messages\" from "
4697 "the main window to retry."));
4698 if (!discard_window) {
4705 if (msgpath == NULL) {
4706 msgpath = folder_item_fetch_msg(folder, msgnum);
4707 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4710 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4711 claws_unlink(msgpath);
4714 if (!discard_window) {
4716 if (!queued_removed)
4717 folder_item_remove_msg(folder, msgnum);
4718 folder_item_scan(folder);
4720 /* make sure we delete that */
4721 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4723 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4724 folder_item_remove_msg(folder, tmp->msgnum);
4725 procmsg_msginfo_free(tmp);
4732 if (!queued_removed)
4733 folder_item_remove_msg(folder, msgnum);
4734 folder_item_scan(folder);
4736 /* make sure we delete that */
4737 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4739 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4740 folder_item_remove_msg(folder, tmp->msgnum);
4741 procmsg_msginfo_free(tmp);
4744 if (!discard_window) {
4745 compose->sending = FALSE;
4746 compose_allow_user_actions (compose, TRUE);
4747 compose_close(compose);
4751 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4752 "the main window to retry."), errstr);
4755 alertpanel_error_log(_("The message was queued but could not be "
4756 "sent.\nUse \"Send queued messages\" from "
4757 "the main window to retry."));
4759 if (!discard_window) {
4768 toolbar_main_set_sensitive(mainwin);
4769 main_window_set_menu_sensitive(mainwin);
4775 compose_allow_user_actions (compose, TRUE);
4776 compose->sending = FALSE;
4777 compose->modified = TRUE;
4778 toolbar_main_set_sensitive(mainwin);
4779 main_window_set_menu_sensitive(mainwin);
4784 static gboolean compose_use_attach(Compose *compose)
4786 GtkTreeModel *model = gtk_tree_view_get_model
4787 (GTK_TREE_VIEW(compose->attach_clist));
4788 return gtk_tree_model_iter_n_children(model, NULL) > 0;
4791 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
4794 gchar buf[BUFFSIZE];
4796 gboolean first_to_address;
4797 gboolean first_cc_address;
4799 ComposeHeaderEntry *headerentry;
4800 const gchar *headerentryname;
4801 const gchar *cc_hdr;
4802 const gchar *to_hdr;
4803 gboolean err = FALSE;
4805 debug_print("Writing redirect header\n");
4807 cc_hdr = prefs_common_translated_header_name("Cc:");
4808 to_hdr = prefs_common_translated_header_name("To:");
4810 first_to_address = TRUE;
4811 for (list = compose->header_list; list; list = list->next) {
4812 headerentry = ((ComposeHeaderEntry *)list->data);
4813 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4815 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
4816 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4817 Xstrdup_a(str, entstr, return -1);
4819 if (str[0] != '\0') {
4820 compose_convert_header
4821 (compose, buf, sizeof(buf), str,
4822 strlen("Resent-To") + 2, TRUE);
4824 if (first_to_address) {
4825 err |= (fprintf(fp, "Resent-To: ") < 0);
4826 first_to_address = FALSE;
4828 err |= (fprintf(fp, ",") < 0);
4830 err |= (fprintf(fp, "%s", buf) < 0);
4834 if (!first_to_address) {
4835 err |= (fprintf(fp, "\n") < 0);
4838 first_cc_address = TRUE;
4839 for (list = compose->header_list; list; list = list->next) {
4840 headerentry = ((ComposeHeaderEntry *)list->data);
4841 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4843 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
4844 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4845 Xstrdup_a(str, strg, return -1);
4847 if (str[0] != '\0') {
4848 compose_convert_header
4849 (compose, buf, sizeof(buf), str,
4850 strlen("Resent-Cc") + 2, TRUE);
4852 if (first_cc_address) {
4853 err |= (fprintf(fp, "Resent-Cc: ") < 0);
4854 first_cc_address = FALSE;
4856 err |= (fprintf(fp, ",") < 0);
4858 err |= (fprintf(fp, "%s", buf) < 0);
4862 if (!first_cc_address) {
4863 err |= (fprintf(fp, "\n") < 0);
4866 return (err ? -1:0);
4869 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
4871 gchar buf[BUFFSIZE];
4873 const gchar *entstr;
4874 /* struct utsname utsbuf; */
4875 gboolean err = FALSE;
4877 g_return_val_if_fail(fp != NULL, -1);
4878 g_return_val_if_fail(compose->account != NULL, -1);
4879 g_return_val_if_fail(compose->account->address != NULL, -1);
4882 get_rfc822_date(buf, sizeof(buf));
4883 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
4886 if (compose->account->name && *compose->account->name) {
4887 compose_convert_header
4888 (compose, buf, sizeof(buf), compose->account->name,
4889 strlen("From: "), TRUE);
4890 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
4891 buf, compose->account->address) < 0);
4893 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
4896 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4897 if (*entstr != '\0') {
4898 Xstrdup_a(str, entstr, return -1);
4901 compose_convert_header(compose, buf, sizeof(buf), str,
4902 strlen("Subject: "), FALSE);
4903 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
4907 /* Resent-Message-ID */
4908 if (compose->account->set_domain && compose->account->domain) {
4909 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
4910 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
4911 g_snprintf(buf, sizeof(buf), "%s",
4912 strchr(compose->account->address, '@') ?
4913 strchr(compose->account->address, '@')+1 :
4914 compose->account->address);
4916 g_snprintf(buf, sizeof(buf), "%s", "");
4919 if (compose->account->gen_msgid) {
4920 generate_msgid(buf, sizeof(buf));
4921 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
4922 compose->msgid = g_strdup(buf);
4924 compose->msgid = NULL;
4927 if (compose_redirect_write_headers_from_headerlist(compose, fp))
4930 /* separator between header and body */
4931 err |= (fputs("\n", fp) == EOF);
4933 return (err ? -1:0);
4936 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
4940 gchar buf[BUFFSIZE];
4942 gboolean skip = FALSE;
4943 gboolean err = FALSE;
4944 gchar *not_included[]={
4945 "Return-Path:", "Delivered-To:", "Received:",
4946 "Subject:", "X-UIDL:", "AF:",
4947 "NF:", "PS:", "SRH:",
4948 "SFN:", "DSR:", "MID:",
4949 "CFG:", "PT:", "S:",
4950 "RQ:", "SSV:", "NSV:",
4951 "SSH:", "R:", "MAID:",
4952 "NAID:", "RMID:", "FMID:",
4953 "SCF:", "RRCPT:", "NG:",
4954 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
4955 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
4956 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
4957 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
4958 "X-Claws-Auto-Wrapping:",
4961 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
4962 FILE_OP_ERROR(compose->redirect_filename, "fopen");
4966 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
4968 for (i = 0; not_included[i] != NULL; i++) {
4969 if (g_ascii_strncasecmp(buf, not_included[i],
4970 strlen(not_included[i])) == 0) {
4977 if (fputs(buf, fdest) == -1)
4980 if (!prefs_common.redirect_keep_from) {
4981 if (g_ascii_strncasecmp(buf, "From:",
4982 strlen("From:")) == 0) {
4983 err |= (fputs(" (by way of ", fdest) == EOF);
4984 if (compose->account->name
4985 && *compose->account->name) {
4986 compose_convert_header
4987 (compose, buf, sizeof(buf),
4988 compose->account->name,
4991 err |= (fprintf(fdest, "%s <%s>",
4993 compose->account->address) < 0);
4995 err |= (fprintf(fdest, "%s",
4996 compose->account->address) < 0);
4997 err |= (fputs(")", fdest) == EOF);
5001 if (fputs("\n", fdest) == -1)
5008 if (compose_redirect_write_headers(compose, fdest))
5011 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5012 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5025 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5027 GtkTextBuffer *buffer;
5028 GtkTextIter start, end;
5031 const gchar *out_codeset;
5032 EncodingType encoding = ENC_UNKNOWN;
5033 MimeInfo *mimemsg, *mimetext;
5035 const gchar *src_codeset = CS_INTERNAL;
5037 if (action == COMPOSE_WRITE_FOR_SEND)
5038 attach_parts = TRUE;
5040 /* create message MimeInfo */
5041 mimemsg = procmime_mimeinfo_new();
5042 mimemsg->type = MIMETYPE_MESSAGE;
5043 mimemsg->subtype = g_strdup("rfc822");
5044 mimemsg->content = MIMECONTENT_MEM;
5045 mimemsg->tmp = TRUE; /* must free content later */
5046 mimemsg->data.mem = compose_get_header(compose);
5048 /* Create text part MimeInfo */
5049 /* get all composed text */
5050 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5051 gtk_text_buffer_get_start_iter(buffer, &start);
5052 gtk_text_buffer_get_end_iter(buffer, &end);
5053 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5055 out_codeset = conv_get_charset_str(compose->out_encoding);
5057 if (!out_codeset && is_ascii_str(chars)) {
5058 out_codeset = CS_US_ASCII;
5059 } else if (prefs_common.outgoing_fallback_to_ascii &&
5060 is_ascii_str(chars)) {
5061 out_codeset = CS_US_ASCII;
5062 encoding = ENC_7BIT;
5066 gchar *test_conv_global_out = NULL;
5067 gchar *test_conv_reply = NULL;
5069 /* automatic mode. be automatic. */
5070 codeconv_set_strict(TRUE);
5072 out_codeset = conv_get_outgoing_charset_str();
5074 debug_print("trying to convert to %s\n", out_codeset);
5075 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5078 if (!test_conv_global_out && compose->orig_charset
5079 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5080 out_codeset = compose->orig_charset;
5081 debug_print("failure; trying to convert to %s\n", out_codeset);
5082 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5085 if (!test_conv_global_out && !test_conv_reply) {
5087 out_codeset = CS_INTERNAL;
5088 debug_print("failure; finally using %s\n", out_codeset);
5090 g_free(test_conv_global_out);
5091 g_free(test_conv_reply);
5092 codeconv_set_strict(FALSE);
5095 if (encoding == ENC_UNKNOWN) {
5096 if (prefs_common.encoding_method == CTE_BASE64)
5097 encoding = ENC_BASE64;
5098 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5099 encoding = ENC_QUOTED_PRINTABLE;
5100 else if (prefs_common.encoding_method == CTE_8BIT)
5101 encoding = ENC_8BIT;
5103 encoding = procmime_get_encoding_for_charset(out_codeset);
5106 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5107 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5109 if (action == COMPOSE_WRITE_FOR_SEND) {
5110 codeconv_set_strict(TRUE);
5111 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5112 codeconv_set_strict(FALSE);
5118 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5119 "to the specified %s charset.\n"
5120 "Send it as %s?"), out_codeset, src_codeset);
5121 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5122 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5125 if (aval != G_ALERTALTERNATE) {
5130 out_codeset = src_codeset;
5136 out_codeset = src_codeset;
5141 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5142 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5143 strstr(buf, "\nFrom ") != NULL) {
5144 encoding = ENC_QUOTED_PRINTABLE;
5148 mimetext = procmime_mimeinfo_new();
5149 mimetext->content = MIMECONTENT_MEM;
5150 mimetext->tmp = TRUE; /* must free content later */
5151 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5152 * and free the data, which we need later. */
5153 mimetext->data.mem = g_strdup(buf);
5154 mimetext->type = MIMETYPE_TEXT;
5155 mimetext->subtype = g_strdup("plain");
5156 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5157 g_strdup(out_codeset));
5159 /* protect trailing spaces when signing message */
5160 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5161 privacy_system_can_sign(compose->privacy_system)) {
5162 encoding = ENC_QUOTED_PRINTABLE;
5165 debug_print("main text: %zd bytes encoded as %s in %d\n",
5166 strlen(buf), out_codeset, encoding);
5168 /* check for line length limit */
5169 if (action == COMPOSE_WRITE_FOR_SEND &&
5170 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5171 check_line_length(buf, 1000, &line) < 0) {
5175 msg = g_strdup_printf
5176 (_("Line %d exceeds the line length limit (998 bytes).\n"
5177 "The contents of the message might be broken on the way to the delivery.\n"
5179 "Send it anyway?"), line + 1);
5180 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5182 if (aval != G_ALERTALTERNATE) {
5188 if (encoding != ENC_UNKNOWN)
5189 procmime_encode_content(mimetext, encoding);
5191 /* append attachment parts */
5192 if (compose_use_attach(compose) && attach_parts) {
5193 MimeInfo *mimempart;
5194 gchar *boundary = NULL;
5195 mimempart = procmime_mimeinfo_new();
5196 mimempart->content = MIMECONTENT_EMPTY;
5197 mimempart->type = MIMETYPE_MULTIPART;
5198 mimempart->subtype = g_strdup("mixed");
5202 boundary = generate_mime_boundary(NULL);
5203 } while (strstr(buf, boundary) != NULL);
5205 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5208 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5210 g_node_append(mimempart->node, mimetext->node);
5211 g_node_append(mimemsg->node, mimempart->node);
5213 compose_add_attachments(compose, mimempart);
5215 g_node_append(mimemsg->node, mimetext->node);
5219 /* sign message if sending */
5220 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5221 privacy_system_can_sign(compose->privacy_system))
5222 if (!privacy_sign(compose->privacy_system, mimemsg, compose->account))
5225 procmime_write_mimeinfo(mimemsg, fp);
5227 procmime_mimeinfo_free_all(mimemsg);
5232 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5234 GtkTextBuffer *buffer;
5235 GtkTextIter start, end;
5240 if ((fp = g_fopen(file, "wb")) == NULL) {
5241 FILE_OP_ERROR(file, "fopen");
5245 /* chmod for security */
5246 if (change_file_mode_rw(fp, file) < 0) {
5247 FILE_OP_ERROR(file, "chmod");
5248 g_warning("can't change file mode\n");
5251 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5252 gtk_text_buffer_get_start_iter(buffer, &start);
5253 gtk_text_buffer_get_end_iter(buffer, &end);
5254 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5256 chars = conv_codeset_strdup
5257 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5260 if (!chars) return -1;
5263 len = strlen(chars);
5264 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5265 FILE_OP_ERROR(file, "fwrite");
5274 if (fclose(fp) == EOF) {
5275 FILE_OP_ERROR(file, "fclose");
5282 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5285 MsgInfo *msginfo = compose->targetinfo;
5287 g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5288 if (!msginfo) return -1;
5290 if (!force && MSG_IS_LOCKED(msginfo->flags))
5293 item = msginfo->folder;
5294 g_return_val_if_fail(item != NULL, -1);
5296 if (procmsg_msg_exist(msginfo) &&
5297 (folder_has_parent_of_type(item, F_QUEUE) ||
5298 folder_has_parent_of_type(item, F_DRAFT)
5299 || msginfo == compose->autosaved_draft)) {
5300 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5301 g_warning("can't remove the old message\n");
5304 debug_print("removed reedit target %d\n", msginfo->msgnum);
5311 static void compose_remove_draft(Compose *compose)
5314 MsgInfo *msginfo = compose->targetinfo;
5315 drafts = account_get_special_folder(compose->account, F_DRAFT);
5317 if (procmsg_msg_exist(msginfo)) {
5318 folder_item_remove_msg(drafts, msginfo->msgnum);
5323 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5324 gboolean remove_reedit_target)
5326 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5329 static gboolean compose_warn_encryption(Compose *compose)
5331 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5332 AlertValue val = G_ALERTALTERNATE;
5334 if (warning == NULL)
5337 val = alertpanel_full(_("Encryption warning"), warning,
5338 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5339 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5340 if (val & G_ALERTDISABLE) {
5341 val &= ~G_ALERTDISABLE;
5342 if (val == G_ALERTALTERNATE)
5343 privacy_inhibit_encrypt_warning(compose->privacy_system,
5347 if (val == G_ALERTALTERNATE) {
5354 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5355 gchar **msgpath, gboolean check_subject,
5356 gboolean remove_reedit_target)
5363 static gboolean lock = FALSE;
5364 PrefsAccount *mailac = NULL, *newsac = NULL;
5365 gboolean err = FALSE;
5367 debug_print("queueing message...\n");
5368 g_return_val_if_fail(compose->account != NULL, -1);
5372 if (compose_check_entries(compose, check_subject) == FALSE) {
5374 if (compose->batch) {
5375 gtk_widget_show_all(compose->window);
5380 if (!compose->to_list && !compose->newsgroup_list) {
5381 g_warning("can't get recipient list.");
5386 if (compose->to_list) {
5387 if (compose->account->protocol != A_NNTP)
5388 mailac = compose->account;
5389 else if (cur_account && cur_account->protocol != A_NNTP)
5390 mailac = cur_account;
5391 else if (!(mailac = compose_current_mail_account())) {
5393 alertpanel_error(_("No account for sending mails available!"));
5398 if (compose->newsgroup_list) {
5399 if (compose->account->protocol == A_NNTP)
5400 newsac = compose->account;
5401 else if (!newsac->protocol != A_NNTP) {
5403 alertpanel_error(_("No account for posting news available!"));
5408 /* write queue header */
5409 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5410 G_DIR_SEPARATOR, compose, (guint) rand());
5411 debug_print("queuing to %s\n", tmp);
5412 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5413 FILE_OP_ERROR(tmp, "fopen");
5419 if (change_file_mode_rw(fp, tmp) < 0) {
5420 FILE_OP_ERROR(tmp, "chmod");
5421 g_warning("can't change file mode\n");
5424 /* queueing variables */
5425 err |= (fprintf(fp, "AF:\n") < 0);
5426 err |= (fprintf(fp, "NF:0\n") < 0);
5427 err |= (fprintf(fp, "PS:10\n") < 0);
5428 err |= (fprintf(fp, "SRH:1\n") < 0);
5429 err |= (fprintf(fp, "SFN:\n") < 0);
5430 err |= (fprintf(fp, "DSR:\n") < 0);
5432 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5434 err |= (fprintf(fp, "MID:\n") < 0);
5435 err |= (fprintf(fp, "CFG:\n") < 0);
5436 err |= (fprintf(fp, "PT:0\n") < 0);
5437 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5438 err |= (fprintf(fp, "RQ:\n") < 0);
5440 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5442 err |= (fprintf(fp, "SSV:\n") < 0);
5444 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5446 err |= (fprintf(fp, "NSV:\n") < 0);
5447 err |= (fprintf(fp, "SSH:\n") < 0);
5448 /* write recepient list */
5449 if (compose->to_list) {
5450 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5451 for (cur = compose->to_list->next; cur != NULL;
5453 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5454 err |= (fprintf(fp, "\n") < 0);
5456 /* write newsgroup list */
5457 if (compose->newsgroup_list) {
5458 err |= (fprintf(fp, "NG:") < 0);
5459 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5460 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5461 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5462 err |= (fprintf(fp, "\n") < 0);
5464 /* Sylpheed account IDs */
5466 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5468 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5471 if (compose->privacy_system != NULL) {
5472 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5473 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5474 if (compose->use_encryption) {
5476 if (!compose_warn_encryption(compose)) {
5483 if (mailac && mailac->encrypt_to_self) {
5484 GSList *tmp_list = g_slist_copy(compose->to_list);
5485 tmp_list = g_slist_append(tmp_list, compose->account->address);
5486 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5487 g_slist_free(tmp_list);
5489 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5491 if (encdata != NULL) {
5492 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5493 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5494 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5496 } /* else we finally dont want to encrypt */
5498 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5499 /* and if encdata was null, it means there's been a problem in
5511 /* Save copy folder */
5512 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5513 gchar *savefolderid;
5515 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
5516 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5517 g_free(savefolderid);
5519 /* Save copy folder */
5520 if (compose->return_receipt) {
5521 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5523 /* Message-ID of message replying to */
5524 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5527 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5528 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5531 /* Message-ID of message forwarding to */
5532 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5535 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5536 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5540 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5542 /* end of headers */
5543 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5545 if (compose->redirect_filename != NULL) {
5546 if (compose_redirect_write_to_file(compose, fp) < 0) {
5555 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5560 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5564 g_warning("failed to write queue message\n");
5571 if (fclose(fp) == EOF) {
5572 FILE_OP_ERROR(tmp, "fclose");
5579 if (item && *item) {
5582 queue = account_get_special_folder(compose->account, F_QUEUE);
5585 g_warning("can't find queue folder\n");
5591 folder_item_scan(queue);
5592 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5593 g_warning("can't queue the message\n");
5600 if (msgpath == NULL) {
5606 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5607 compose_remove_reedit_target(compose, FALSE);
5610 if ((msgnum != NULL) && (item != NULL)) {
5618 static void compose_add_attachments(Compose *compose, MimeInfo *parent)
5621 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5623 struct stat statbuf;
5624 gchar *type, *subtype;
5625 GtkTreeModel *model;
5628 model = gtk_tree_view_get_model(tree_view);
5630 if (!gtk_tree_model_get_iter_first(model, &iter))
5633 gtk_tree_model_get(model, &iter,
5637 mimepart = procmime_mimeinfo_new();
5638 mimepart->content = MIMECONTENT_FILE;
5639 mimepart->data.filename = g_strdup(ainfo->file);
5640 mimepart->tmp = FALSE; /* or we destroy our attachment */
5641 mimepart->offset = 0;
5643 stat(ainfo->file, &statbuf);
5644 mimepart->length = statbuf.st_size;
5646 type = g_strdup(ainfo->content_type);
5648 if (!strchr(type, '/')) {
5650 type = g_strdup("application/octet-stream");
5653 subtype = strchr(type, '/') + 1;
5654 *(subtype - 1) = '\0';
5655 mimepart->type = procmime_get_media_type(type);
5656 mimepart->subtype = g_strdup(subtype);
5659 if (mimepart->type == MIMETYPE_MESSAGE &&
5660 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5661 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5664 g_hash_table_insert(mimepart->typeparameters,
5665 g_strdup("name"), g_strdup(ainfo->name));
5666 g_hash_table_insert(mimepart->dispositionparameters,
5667 g_strdup("filename"), g_strdup(ainfo->name));
5668 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5672 if (compose->use_signing) {
5673 if (ainfo->encoding == ENC_7BIT)
5674 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5675 else if (ainfo->encoding == ENC_8BIT)
5676 ainfo->encoding = ENC_BASE64;
5679 procmime_encode_content(mimepart, ainfo->encoding);
5681 g_node_append(parent->node, mimepart->node);
5682 } while (gtk_tree_model_iter_next(model, &iter));
5685 #define IS_IN_CUSTOM_HEADER(header) \
5686 (compose->account->add_customhdr && \
5687 custom_header_find(compose->account->customhdr_list, header) != NULL)
5689 static void compose_add_headerfield_from_headerlist(Compose *compose,
5691 const gchar *fieldname,
5692 const gchar *seperator)
5694 gchar *str, *fieldname_w_colon;
5695 gboolean add_field = FALSE;
5697 ComposeHeaderEntry *headerentry;
5698 const gchar *headerentryname;
5699 const gchar *trans_fieldname;
5702 if (IS_IN_CUSTOM_HEADER(fieldname))
5705 debug_print("Adding %s-fields\n", fieldname);
5707 fieldstr = g_string_sized_new(64);
5709 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5710 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5712 for (list = compose->header_list; list; list = list->next) {
5713 headerentry = ((ComposeHeaderEntry *)list->data);
5714 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5716 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5717 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5719 if (str[0] != '\0') {
5721 g_string_append(fieldstr, seperator);
5722 g_string_append(fieldstr, str);
5731 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5732 compose_convert_header
5733 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
5734 strlen(fieldname) + 2, TRUE);
5735 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5739 g_free(fieldname_w_colon);
5740 g_string_free(fieldstr, TRUE);
5745 static gchar *compose_get_header(Compose *compose)
5747 gchar buf[BUFFSIZE];
5748 const gchar *entry_str;
5752 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5754 gchar *from_name = NULL, *from_address = NULL;
5757 g_return_val_if_fail(compose->account != NULL, NULL);
5758 g_return_val_if_fail(compose->account->address != NULL, NULL);
5760 header = g_string_sized_new(64);
5763 get_rfc822_date(buf, sizeof(buf));
5764 g_string_append_printf(header, "Date: %s\n", buf);
5768 if (compose->account->name && *compose->account->name) {
5770 QUOTE_IF_REQUIRED(buf, compose->account->name);
5771 tmp = g_strdup_printf("%s <%s>",
5772 buf, compose->account->address);
5774 tmp = g_strdup_printf("%s",
5775 compose->account->address);
5777 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5778 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5780 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5781 from_address = g_strdup(compose->account->address);
5783 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5784 /* extract name and address */
5785 if (strstr(spec, " <") && strstr(spec, ">")) {
5786 from_address = g_strdup(strrchr(spec, '<')+1);
5787 *(strrchr(from_address, '>')) = '\0';
5788 from_name = g_strdup(spec);
5789 *(strrchr(from_name, '<')) = '\0';
5792 from_address = g_strdup(spec);
5799 if (from_name && *from_name) {
5800 compose_convert_header
5801 (compose, buf, sizeof(buf), from_name,
5802 strlen("From: "), TRUE);
5803 QUOTE_IF_REQUIRED(name, buf);
5805 g_string_append_printf(header, "From: %s <%s>\n",
5806 name, from_address);
5808 g_string_append_printf(header, "From: %s\n", from_address);
5811 g_free(from_address);
5814 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
5817 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
5820 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
5823 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
5826 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
5828 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
5831 compose_convert_header(compose, buf, sizeof(buf), str,
5832 strlen("Subject: "), FALSE);
5833 g_string_append_printf(header, "Subject: %s\n", buf);
5839 if (compose->account->set_domain && compose->account->domain) {
5840 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5841 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5842 g_snprintf(buf, sizeof(buf), "%s",
5843 strchr(compose->account->address, '@') ?
5844 strchr(compose->account->address, '@')+1 :
5845 compose->account->address);
5847 g_snprintf(buf, sizeof(buf), "%s", "");
5850 if (compose->account->gen_msgid) {
5851 generate_msgid(buf, sizeof(buf));
5852 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
5853 compose->msgid = g_strdup(buf);
5855 compose->msgid = NULL;
5858 if (compose->remove_references == FALSE) {
5860 if (compose->inreplyto && compose->to_list)
5861 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
5864 if (compose->references)
5865 g_string_append_printf(header, "References: %s\n", compose->references);
5869 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
5872 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
5875 if (compose->account->organization &&
5876 strlen(compose->account->organization) &&
5877 !IS_IN_CUSTOM_HEADER("Organization")) {
5878 compose_convert_header(compose, buf, sizeof(buf),
5879 compose->account->organization,
5880 strlen("Organization: "), FALSE);
5881 g_string_append_printf(header, "Organization: %s\n", buf);
5884 /* Program version and system info */
5885 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
5886 !compose->newsgroup_list) {
5887 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
5889 gtk_major_version, gtk_minor_version, gtk_micro_version,
5892 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
5893 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
5895 gtk_major_version, gtk_minor_version, gtk_micro_version,
5899 /* custom headers */
5900 if (compose->account->add_customhdr) {
5903 for (cur = compose->account->customhdr_list; cur != NULL;
5905 CustomHeader *chdr = (CustomHeader *)cur->data;
5907 if (custom_header_is_allowed(chdr->name)) {
5908 compose_convert_header
5909 (compose, buf, sizeof(buf),
5910 chdr->value ? chdr->value : "",
5911 strlen(chdr->name) + 2, FALSE);
5912 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
5917 /* Automatic Faces and X-Faces */
5918 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
5919 g_string_append_printf(header, "X-Face: %s\n", buf);
5921 else if (get_default_xface (buf, sizeof(buf)) == 0) {
5922 g_string_append_printf(header, "X-Face: %s\n", buf);
5924 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
5925 g_string_append_printf(header, "Face: %s\n", buf);
5927 else if (get_default_face (buf, sizeof(buf)) == 0) {
5928 g_string_append_printf(header, "Face: %s\n", buf);
5932 switch (compose->priority) {
5933 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
5934 "X-Priority: 1 (Highest)\n");
5936 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
5937 "X-Priority: 2 (High)\n");
5939 case PRIORITY_NORMAL: break;
5940 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
5941 "X-Priority: 4 (Low)\n");
5943 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
5944 "X-Priority: 5 (Lowest)\n");
5946 default: debug_print("compose: priority unknown : %d\n",
5950 /* Request Return Receipt */
5951 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
5952 if (compose->return_receipt) {
5953 if (compose->account->name
5954 && *compose->account->name) {
5955 compose_convert_header(compose, buf, sizeof(buf),
5956 compose->account->name,
5957 strlen("Disposition-Notification-To: "),
5959 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
5961 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
5965 /* get special headers */
5966 for (list = compose->header_list; list; list = list->next) {
5967 ComposeHeaderEntry *headerentry;
5970 gchar *headername_wcolon;
5971 const gchar *headername_trans;
5974 gboolean standard_header = FALSE;
5976 headerentry = ((ComposeHeaderEntry *)list->data);
5978 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
5980 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
5985 if (!strstr(tmp, ":")) {
5986 headername_wcolon = g_strconcat(tmp, ":", NULL);
5987 headername = g_strdup(tmp);
5989 headername_wcolon = g_strdup(tmp);
5990 headername = g_strdup(strtok(tmp, ":"));
5994 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5995 Xstrdup_a(headervalue, entry_str, return NULL);
5996 subst_char(headervalue, '\r', ' ');
5997 subst_char(headervalue, '\n', ' ');
5998 string = std_headers;
5999 while (*string != NULL) {
6000 headername_trans = prefs_common_translated_header_name(*string);
6001 if (!strcmp(headername_trans, headername_wcolon))
6002 standard_header = TRUE;
6005 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6006 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6009 g_free(headername_wcolon);
6013 g_string_free(header, FALSE);
6018 #undef IS_IN_CUSTOM_HEADER
6020 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6021 gint header_len, gboolean addr_field)
6023 gchar *tmpstr = NULL;
6024 const gchar *out_codeset = NULL;
6026 g_return_if_fail(src != NULL);
6027 g_return_if_fail(dest != NULL);
6029 if (len < 1) return;
6031 tmpstr = g_strdup(src);
6033 subst_char(tmpstr, '\n', ' ');
6034 subst_char(tmpstr, '\r', ' ');
6037 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6038 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6039 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6044 codeconv_set_strict(TRUE);
6045 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6046 conv_get_charset_str(compose->out_encoding));
6047 codeconv_set_strict(FALSE);
6049 if (!dest || *dest == '\0') {
6050 gchar *test_conv_global_out = NULL;
6051 gchar *test_conv_reply = NULL;
6053 /* automatic mode. be automatic. */
6054 codeconv_set_strict(TRUE);
6056 out_codeset = conv_get_outgoing_charset_str();
6058 debug_print("trying to convert to %s\n", out_codeset);
6059 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6062 if (!test_conv_global_out && compose->orig_charset
6063 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6064 out_codeset = compose->orig_charset;
6065 debug_print("failure; trying to convert to %s\n", out_codeset);
6066 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6069 if (!test_conv_global_out && !test_conv_reply) {
6071 out_codeset = CS_INTERNAL;
6072 debug_print("finally using %s\n", out_codeset);
6074 g_free(test_conv_global_out);
6075 g_free(test_conv_reply);
6076 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6078 codeconv_set_strict(FALSE);
6083 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6087 g_return_if_fail(user_data != NULL);
6089 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6090 g_strstrip(address);
6091 if (*address != '\0') {
6092 gchar *name = procheader_get_fromname(address);
6093 extract_address(address);
6094 addressbook_add_contact(name, address, NULL, NULL);
6099 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6101 GtkWidget *menuitem;
6104 g_return_if_fail(menu != NULL);
6105 g_return_if_fail(GTK_IS_MENU_SHELL(menu));
6107 menuitem = gtk_separator_menu_item_new();
6108 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6109 gtk_widget_show(menuitem);
6111 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6112 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6114 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6115 g_strstrip(address);
6116 if (*address == '\0') {
6117 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6120 g_signal_connect(G_OBJECT(menuitem), "activate",
6121 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6122 gtk_widget_show(menuitem);
6125 static void compose_create_header_entry(Compose *compose)
6127 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6132 const gchar *header = NULL;
6133 ComposeHeaderEntry *headerentry;
6134 gboolean standard_header = FALSE;
6135 #if !(GTK_CHECK_VERSION(2,12,0))
6136 GtkTooltips *tips = compose->tooltips;
6139 headerentry = g_new0(ComposeHeaderEntry, 1);
6142 combo = gtk_combo_box_entry_new_text();
6144 while(*string != NULL) {
6145 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6146 (gchar*)prefs_common_translated_header_name(*string));
6149 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6150 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6151 G_CALLBACK(compose_grab_focus_cb), compose);
6152 gtk_widget_show(combo);
6153 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6154 compose->header_nextrow, compose->header_nextrow+1,
6155 GTK_SHRINK, GTK_FILL, 0, 0);
6156 if (compose->header_last) {
6157 const gchar *last_header_entry = gtk_entry_get_text(
6158 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6160 while (*string != NULL) {
6161 if (!strcmp(*string, last_header_entry))
6162 standard_header = TRUE;
6165 if (standard_header)
6166 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6168 if (!compose->header_last || !standard_header) {
6169 switch(compose->account->protocol) {
6171 header = prefs_common_translated_header_name("Newsgroups:");
6174 header = prefs_common_translated_header_name("To:");
6179 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6181 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6182 G_CALLBACK(compose_grab_focus_cb), compose);
6185 entry = gtk_entry_new();
6186 gtk_widget_show(entry);
6187 CLAWS_SET_TIP(entry,
6188 _("Use <tab> to autocomplete from addressbook"));
6189 gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2,
6190 compose->header_nextrow, compose->header_nextrow+1,
6191 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6193 g_signal_connect(G_OBJECT(entry), "key-press-event",
6194 G_CALLBACK(compose_headerentry_key_press_event_cb),
6196 g_signal_connect(G_OBJECT(entry), "changed",
6197 G_CALLBACK(compose_headerentry_changed_cb),
6199 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6200 G_CALLBACK(compose_grab_focus_cb), compose);
6203 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6204 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6205 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6206 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6207 G_CALLBACK(compose_header_drag_received_cb),
6209 g_signal_connect(G_OBJECT(entry), "drag-drop",
6210 G_CALLBACK(compose_drag_drop),
6212 g_signal_connect(G_OBJECT(entry), "populate-popup",
6213 G_CALLBACK(compose_entry_popup_extend),
6216 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6218 headerentry->compose = compose;
6219 headerentry->combo = combo;
6220 headerentry->entry = entry;
6221 headerentry->headernum = compose->header_nextrow;
6223 compose->header_nextrow++;
6224 compose->header_last = headerentry;
6225 compose->header_list =
6226 g_slist_append(compose->header_list,
6230 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text)
6232 ComposeHeaderEntry *last_header;
6234 last_header = compose->header_last;
6236 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((last_header->combo)))), header);
6237 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6240 static void compose_remove_header_entries(Compose *compose)
6243 for (list = compose->header_list; list; list = list->next) {
6244 ComposeHeaderEntry *headerentry =
6245 (ComposeHeaderEntry *)list->data;
6246 gtk_widget_destroy(headerentry->combo);
6247 gtk_widget_destroy(headerentry->entry);
6248 g_free(headerentry);
6250 compose->header_last = NULL;
6251 g_slist_free(compose->header_list);
6252 compose->header_list = NULL;
6253 compose->header_nextrow = 1;
6254 compose_create_header_entry(compose);
6257 static GtkWidget *compose_create_header(Compose *compose)
6259 GtkWidget *from_optmenu_hbox;
6260 GtkWidget *header_scrolledwin;
6261 GtkWidget *header_table;
6265 /* header labels and entries */
6266 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6267 gtk_widget_show(header_scrolledwin);
6268 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6270 header_table = gtk_table_new(2, 2, FALSE);
6271 gtk_widget_show(header_table);
6272 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6273 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6274 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6277 /* option menu for selecting accounts */
6278 from_optmenu_hbox = compose_account_option_menu_create(compose);
6279 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6280 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6283 compose->header_table = header_table;
6284 compose->header_list = NULL;
6285 compose->header_nextrow = count;
6287 compose_create_header_entry(compose);
6289 compose->table = NULL;
6291 return header_scrolledwin ;
6294 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6296 Compose *compose = (Compose *)data;
6297 GdkEventButton event;
6300 event.time = gtk_get_current_event_time();
6302 return attach_button_pressed(compose->attach_clist, &event, compose);
6305 static GtkWidget *compose_create_attach(Compose *compose)
6307 GtkWidget *attach_scrwin;
6308 GtkWidget *attach_clist;
6310 GtkListStore *store;
6311 GtkCellRenderer *renderer;
6312 GtkTreeViewColumn *column;
6313 GtkTreeSelection *selection;
6315 /* attachment list */
6316 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6317 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6318 GTK_POLICY_AUTOMATIC,
6319 GTK_POLICY_AUTOMATIC);
6320 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6322 store = gtk_list_store_new(N_ATTACH_COLS,
6327 G_TYPE_AUTO_POINTER,
6329 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6330 (GTK_TREE_MODEL(store)));
6331 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6332 g_object_unref(store);
6334 renderer = gtk_cell_renderer_text_new();
6335 column = gtk_tree_view_column_new_with_attributes
6336 (_("Mime type"), renderer, "text",
6337 COL_MIMETYPE, NULL);
6338 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6340 renderer = gtk_cell_renderer_text_new();
6341 column = gtk_tree_view_column_new_with_attributes
6342 (_("Size"), renderer, "text",
6344 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6346 renderer = gtk_cell_renderer_text_new();
6347 column = gtk_tree_view_column_new_with_attributes
6348 (_("Name"), renderer, "text",
6350 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6352 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6353 prefs_common.use_stripes_everywhere);
6354 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6355 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6357 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6358 G_CALLBACK(attach_selected), compose);
6359 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6360 G_CALLBACK(attach_button_pressed), compose);
6362 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6363 G_CALLBACK(popup_attach_button_pressed), compose);
6365 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6366 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6367 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6368 G_CALLBACK(popup_attach_button_pressed), compose);
6370 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6371 G_CALLBACK(attach_key_pressed), compose);
6374 gtk_drag_dest_set(attach_clist,
6375 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6376 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6377 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6378 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6379 G_CALLBACK(compose_attach_drag_received_cb),
6381 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6382 G_CALLBACK(compose_drag_drop),
6385 compose->attach_scrwin = attach_scrwin;
6386 compose->attach_clist = attach_clist;
6388 return attach_scrwin;
6391 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6392 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6394 static GtkWidget *compose_create_others(Compose *compose)
6397 GtkWidget *savemsg_checkbtn;
6398 GtkWidget *savemsg_entry;
6399 GtkWidget *savemsg_select;
6402 gchar *folderidentifier;
6404 /* Table for settings */
6405 table = gtk_table_new(3, 1, FALSE);
6406 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6407 gtk_widget_show(table);
6408 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6411 /* Save Message to folder */
6412 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6413 gtk_widget_show(savemsg_checkbtn);
6414 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6415 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6416 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6418 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6419 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6421 savemsg_entry = gtk_entry_new();
6422 gtk_widget_show(savemsg_entry);
6423 gtk_table_attach_defaults(GTK_TABLE(table), savemsg_entry, 1, 2, rowcount, rowcount + 1);
6424 gtk_editable_set_editable(GTK_EDITABLE(savemsg_entry), prefs_common.savemsg);
6425 g_signal_connect_after(G_OBJECT(savemsg_entry), "grab_focus",
6426 G_CALLBACK(compose_grab_focus_cb), compose);
6427 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6428 folderidentifier = folder_item_get_identifier(account_get_special_folder
6429 (compose->account, F_OUTBOX));
6430 gtk_entry_set_text(GTK_ENTRY(savemsg_entry), folderidentifier);
6431 g_free(folderidentifier);
6434 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6435 gtk_widget_show(savemsg_select);
6436 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6437 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6438 G_CALLBACK(compose_savemsg_select_cb),
6443 compose->savemsg_checkbtn = savemsg_checkbtn;
6444 compose->savemsg_entry = savemsg_entry;
6449 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6451 gtk_editable_set_editable(GTK_EDITABLE(compose->savemsg_entry),
6452 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6455 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6460 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6463 path = folder_item_get_identifier(dest);
6465 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), path);
6469 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6470 GdkAtom clip, GtkTextIter *insert_place);
6473 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6477 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6479 if (event->button == 3) {
6481 GtkTextIter sel_start, sel_end;
6482 gboolean stuff_selected;
6484 /* move the cursor to allow GtkAspell to check the word
6485 * under the mouse */
6486 if (event->x && event->y) {
6487 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6488 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6490 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6493 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6494 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6497 stuff_selected = gtk_text_buffer_get_selection_bounds(
6499 &sel_start, &sel_end);
6501 gtk_text_buffer_place_cursor (buffer, &iter);
6502 /* reselect stuff */
6504 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6505 gtk_text_buffer_select_range(buffer,
6506 &sel_start, &sel_end);
6508 return FALSE; /* pass the event so that the right-click goes through */
6511 if (event->button == 2) {
6516 /* get the middle-click position to paste at the correct place */
6517 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6518 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6520 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6523 entry_paste_clipboard(compose, text,
6524 prefs_common.linewrap_pastes,
6525 GDK_SELECTION_PRIMARY, &iter);
6533 static void compose_spell_menu_changed(void *data)
6535 Compose *compose = (Compose *)data;
6537 GtkWidget *menuitem;
6538 GtkWidget *parent_item;
6539 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6542 if (compose->gtkaspell == NULL)
6545 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6546 "/Menu/Spelling/Options");
6548 /* setting the submenu removes /Spelling/Options from the factory
6549 * so we need to save it */
6551 if (parent_item == NULL) {
6552 parent_item = compose->aspell_options_menu;
6553 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6555 compose->aspell_options_menu = parent_item;
6557 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6559 spell_menu = g_slist_reverse(spell_menu);
6560 for (items = spell_menu;
6561 items; items = items->next) {
6562 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6563 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6564 gtk_widget_show(GTK_WIDGET(menuitem));
6566 g_slist_free(spell_menu);
6568 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6573 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6575 Compose *compose = (Compose *)data;
6576 GdkEventButton event;
6579 event.time = gtk_get_current_event_time();
6583 return text_clicked(compose->text, &event, compose);
6586 static gboolean compose_force_window_origin = TRUE;
6587 static Compose *compose_create(PrefsAccount *account,
6596 GtkWidget *handlebox;
6598 GtkWidget *notebook;
6600 GtkWidget *attach_hbox;
6601 GtkWidget *attach_lab1;
6602 GtkWidget *attach_lab2;
6607 GtkWidget *subject_hbox;
6608 GtkWidget *subject_frame;
6609 GtkWidget *subject_entry;
6613 GtkWidget *edit_vbox;
6614 GtkWidget *ruler_hbox;
6616 GtkWidget *scrolledwin;
6618 GtkTextBuffer *buffer;
6619 GtkClipboard *clipboard;
6622 UndoMain *undostruct;
6624 gchar *titles[N_ATTACH_COLS];
6625 GtkWidget *popupmenu;
6626 GtkWidget *tmpl_menu;
6627 GtkActionGroup *action_group = NULL;
6630 GtkAspell * gtkaspell = NULL;
6633 static GdkGeometry geometry;
6635 g_return_val_if_fail(account != NULL, NULL);
6637 debug_print("Creating compose window...\n");
6638 compose = g_new0(Compose, 1);
6640 titles[COL_MIMETYPE] = _("MIME type");
6641 titles[COL_SIZE] = _("Size");
6642 titles[COL_NAME] = _("Name");
6644 compose->batch = batch;
6645 compose->account = account;
6646 compose->folder = folder;
6648 compose->mutex = g_mutex_new();
6649 compose->set_cursor_pos = -1;
6651 #if !(GTK_CHECK_VERSION(2,12,0))
6652 compose->tooltips = tips;
6655 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6657 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6658 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6660 if (!geometry.max_width) {
6661 geometry.max_width = gdk_screen_width();
6662 geometry.max_height = gdk_screen_height();
6665 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6666 &geometry, GDK_HINT_MAX_SIZE);
6667 if (!geometry.min_width) {
6668 geometry.min_width = 600;
6669 geometry.min_height = 480;
6671 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6672 &geometry, GDK_HINT_MIN_SIZE);
6674 #ifndef GENERIC_UMPC
6675 if (compose_force_window_origin)
6676 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
6677 prefs_common.compose_y);
6679 g_signal_connect(G_OBJECT(window), "delete_event",
6680 G_CALLBACK(compose_delete_cb), compose);
6681 MANAGE_WINDOW_SIGNALS_CONNECT(window);
6682 gtk_widget_realize(window);
6684 gtkut_widget_set_composer_icon(window);
6686 vbox = gtk_vbox_new(FALSE, 0);
6687 gtk_container_add(GTK_CONTAINER(window), vbox);
6689 compose->ui_manager = gtk_ui_manager_new();
6690 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
6691 G_N_ELEMENTS(compose_entries), (gpointer)compose);
6692 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
6693 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
6694 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
6695 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
6696 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
6697 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
6698 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
6699 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
6702 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
6704 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENU)
6707 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
6708 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
6710 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
6712 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
6713 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
6714 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
6717 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
6718 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
6719 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6720 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
6721 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
6722 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
6723 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6724 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
6725 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6726 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
6729 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
6730 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
6731 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6733 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
6734 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
6735 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
6737 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
6738 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
6739 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
6740 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
6742 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
6744 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
6745 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
6746 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
6747 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
6748 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
6749 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
6750 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
6751 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
6752 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
6753 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
6754 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
6755 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
6756 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
6757 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
6758 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
6760 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6762 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
6763 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
6764 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
6765 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
6767 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6769 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
6773 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
6774 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
6775 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
6776 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
6777 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
6778 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
6782 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
6783 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
6784 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
6785 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
6786 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
6788 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6789 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
6790 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6791 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
6792 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
6795 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6796 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
6797 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
6798 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
6799 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
6800 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
6801 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
6803 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6804 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
6805 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6806 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
6807 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6809 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
6811 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
6812 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6813 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
6814 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
6815 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6817 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
6818 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)
6819 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)
6820 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
6822 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
6824 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
6825 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)
6826 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)
6828 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
6830 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
6831 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)
6832 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
6834 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
6835 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)
6836 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
6838 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
6840 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
6841 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)
6842 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
6843 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
6844 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
6846 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
6847 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)
6848 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)
6849 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
6850 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
6852 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
6853 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
6854 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
6855 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
6856 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
6858 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
6859 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
6860 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)
6862 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
6863 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
6864 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
6868 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
6869 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
6870 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
6871 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6872 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
6873 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6876 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
6878 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
6879 gtk_widget_show_all(menubar);
6881 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
6883 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
6885 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
6888 if (prefs_common.toolbar_detachable) {
6889 handlebox = gtk_handle_box_new();
6891 handlebox = gtk_hbox_new(FALSE, 0);
6893 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
6895 gtk_widget_realize(handlebox);
6897 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
6900 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
6904 vbox2 = gtk_vbox_new(FALSE, 2);
6905 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
6906 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
6909 notebook = gtk_notebook_new();
6910 gtk_widget_set_size_request(notebook, -1, 130);
6911 gtk_widget_show(notebook);
6913 /* header labels and entries */
6914 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6915 compose_create_header(compose),
6916 gtk_label_new_with_mnemonic(_("Hea_der")));
6917 /* attachment list */
6918 attach_hbox = gtk_hbox_new(FALSE, 0);
6919 gtk_widget_show(attach_hbox);
6921 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
6922 gtk_widget_show(attach_lab1);
6923 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
6925 attach_lab2 = gtk_label_new("");
6926 gtk_widget_show(attach_lab2);
6927 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
6929 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6930 compose_create_attach(compose),
6933 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6934 compose_create_others(compose),
6935 gtk_label_new_with_mnemonic(_("Othe_rs")));
6938 subject_hbox = gtk_hbox_new(FALSE, 0);
6939 gtk_widget_show(subject_hbox);
6941 subject_frame = gtk_frame_new(NULL);
6942 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
6943 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
6944 gtk_widget_show(subject_frame);
6946 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
6947 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
6948 gtk_widget_show(subject);
6950 label = gtk_label_new(_("Subject:"));
6951 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
6952 gtk_widget_show(label);
6954 subject_entry = gtk_entry_new();
6955 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
6956 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
6957 G_CALLBACK(compose_grab_focus_cb), compose);
6958 gtk_widget_show(subject_entry);
6959 compose->subject_entry = subject_entry;
6960 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
6962 edit_vbox = gtk_vbox_new(FALSE, 0);
6964 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
6967 ruler_hbox = gtk_hbox_new(FALSE, 0);
6968 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
6970 ruler = gtk_shruler_new();
6971 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
6972 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
6976 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6977 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
6978 GTK_POLICY_AUTOMATIC,
6979 GTK_POLICY_AUTOMATIC);
6980 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
6982 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
6983 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
6985 text = gtk_text_view_new();
6986 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
6987 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
6988 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
6989 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
6990 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
6992 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
6994 g_signal_connect_after(G_OBJECT(text), "size_allocate",
6995 G_CALLBACK(compose_edit_size_alloc),
6997 g_signal_connect(G_OBJECT(buffer), "changed",
6998 G_CALLBACK(compose_changed_cb), compose);
6999 g_signal_connect(G_OBJECT(text), "grab_focus",
7000 G_CALLBACK(compose_grab_focus_cb), compose);
7001 g_signal_connect(G_OBJECT(buffer), "insert_text",
7002 G_CALLBACK(text_inserted), compose);
7003 g_signal_connect(G_OBJECT(text), "button_press_event",
7004 G_CALLBACK(text_clicked), compose);
7006 g_signal_connect(G_OBJECT(text), "popup-menu",
7007 G_CALLBACK(compose_popup_menu), compose);
7009 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7010 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7011 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7012 G_CALLBACK(compose_popup_menu), compose);
7014 g_signal_connect(G_OBJECT(subject_entry), "changed",
7015 G_CALLBACK(compose_changed_cb), compose);
7018 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7019 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7020 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7021 g_signal_connect(G_OBJECT(text), "drag_data_received",
7022 G_CALLBACK(compose_insert_drag_received_cb),
7024 g_signal_connect(G_OBJECT(text), "drag-drop",
7025 G_CALLBACK(compose_drag_drop),
7027 gtk_widget_show_all(vbox);
7029 /* pane between attach clist and text */
7030 paned = gtk_vpaned_new();
7031 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7033 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7034 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7036 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7038 gtk_paned_add1(GTK_PANED(paned), notebook);
7039 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7040 gtk_widget_show_all(paned);
7043 if (prefs_common.textfont) {
7044 PangoFontDescription *font_desc;
7046 font_desc = pango_font_description_from_string
7047 (prefs_common.textfont);
7049 gtk_widget_modify_font(text, font_desc);
7050 pango_font_description_free(font_desc);
7054 gtk_action_group_add_actions(action_group, compose_popup_entries,
7055 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7056 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7057 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7058 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7059 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7060 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7061 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7063 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7065 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7066 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7067 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7069 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7071 undostruct = undo_init(text);
7072 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7075 address_completion_start(window);
7077 compose->window = window;
7078 compose->vbox = vbox;
7079 compose->menubar = menubar;
7080 compose->handlebox = handlebox;
7082 compose->vbox2 = vbox2;
7084 compose->paned = paned;
7086 compose->attach_label = attach_lab2;
7088 compose->notebook = notebook;
7089 compose->edit_vbox = edit_vbox;
7090 compose->ruler_hbox = ruler_hbox;
7091 compose->ruler = ruler;
7092 compose->scrolledwin = scrolledwin;
7093 compose->text = text;
7095 compose->focused_editable = NULL;
7097 compose->popupmenu = popupmenu;
7099 compose->tmpl_menu = tmpl_menu;
7101 compose->mode = mode;
7102 compose->rmode = mode;
7104 compose->targetinfo = NULL;
7105 compose->replyinfo = NULL;
7106 compose->fwdinfo = NULL;
7108 compose->replyto = NULL;
7110 compose->bcc = NULL;
7111 compose->followup_to = NULL;
7113 compose->ml_post = NULL;
7115 compose->inreplyto = NULL;
7116 compose->references = NULL;
7117 compose->msgid = NULL;
7118 compose->boundary = NULL;
7120 compose->autowrap = prefs_common.autowrap;
7122 compose->use_signing = FALSE;
7123 compose->use_encryption = FALSE;
7124 compose->privacy_system = NULL;
7126 compose->modified = FALSE;
7128 compose->return_receipt = FALSE;
7130 compose->to_list = NULL;
7131 compose->newsgroup_list = NULL;
7133 compose->undostruct = undostruct;
7135 compose->sig_str = NULL;
7137 compose->exteditor_file = NULL;
7138 compose->exteditor_pid = -1;
7139 compose->exteditor_tag = -1;
7140 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7143 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7144 if (mode != COMPOSE_REDIRECT) {
7145 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7146 strcmp(prefs_common.dictionary, "")) {
7147 gtkaspell = gtkaspell_new(prefs_common.aspell_path,
7148 prefs_common.dictionary,
7149 prefs_common.alt_dictionary,
7150 conv_get_locale_charset_str(),
7151 prefs_common.misspelled_col,
7152 prefs_common.check_while_typing,
7153 prefs_common.recheck_when_changing_dict,
7154 prefs_common.use_alternate,
7155 prefs_common.use_both_dicts,
7156 GTK_TEXT_VIEW(text),
7157 GTK_WINDOW(compose->window),
7158 compose_spell_menu_changed,
7161 alertpanel_error(_("Spell checker could not "
7163 gtkaspell_checkers_strerror());
7164 gtkaspell_checkers_reset_error();
7166 if (!gtkaspell_set_sug_mode(gtkaspell,
7167 prefs_common.aspell_sugmode)) {
7168 debug_print("Aspell: could not set "
7169 "suggestion mode %s\n",
7170 gtkaspell_checkers_strerror());
7171 gtkaspell_checkers_reset_error();
7174 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7178 compose->gtkaspell = gtkaspell;
7179 compose_spell_menu_changed(compose);
7182 compose_select_account(compose, account, TRUE);
7184 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7186 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7187 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7189 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7190 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7192 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7193 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7195 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7196 if (account->protocol != A_NNTP)
7197 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7198 prefs_common_translated_header_name("To:"));
7200 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7201 prefs_common_translated_header_name("Newsgroups:"));
7203 addressbook_set_target_compose(compose);
7205 if (mode != COMPOSE_REDIRECT)
7206 compose_set_template_menu(compose);
7208 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7211 compose_list = g_list_append(compose_list, compose);
7213 if (!prefs_common.show_ruler)
7214 gtk_widget_hide(ruler_hbox);
7216 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7219 compose->priority = PRIORITY_NORMAL;
7220 compose_update_priority_menu_item(compose);
7222 compose_set_out_encoding(compose);
7225 compose_update_actions_menu(compose);
7227 /* Privacy Systems menu */
7228 compose_update_privacy_systems_menu(compose);
7230 activate_privacy_system(compose, account, TRUE);
7231 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7233 gtk_widget_realize(window);
7235 gtk_widget_show(window);
7237 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7238 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7245 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7250 GtkWidget *optmenubox;
7253 GtkWidget *from_name = NULL;
7254 #if !(GTK_CHECK_VERSION(2,12,0))
7255 GtkTooltips *tips = compose->tooltips;
7258 gint num = 0, def_menu = 0;
7260 accounts = account_get_list();
7261 g_return_val_if_fail(accounts != NULL, NULL);
7263 optmenubox = gtk_event_box_new();
7264 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7265 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7267 hbox = gtk_hbox_new(FALSE, 6);
7268 from_name = gtk_entry_new();
7270 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7271 G_CALLBACK(compose_grab_focus_cb), compose);
7273 for (; accounts != NULL; accounts = accounts->next, num++) {
7274 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7275 gchar *name, *from = NULL;
7277 if (ac == compose->account) def_menu = num;
7279 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7282 if (ac == compose->account) {
7283 if (ac->name && *ac->name) {
7285 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7286 from = g_strdup_printf("%s <%s>",
7288 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7290 from = g_strdup_printf("%s",
7292 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7295 COMBOBOX_ADD(menu, name, ac->account_id);
7300 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7302 g_signal_connect(G_OBJECT(optmenu), "changed",
7303 G_CALLBACK(account_activated),
7305 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7306 G_CALLBACK(compose_entry_popup_extend),
7309 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7310 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7312 CLAWS_SET_TIP(optmenubox,
7313 _("Account to use for this email"));
7314 CLAWS_SET_TIP(from_name,
7315 _("Sender address to be used"));
7317 compose->from_name = from_name;
7322 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7324 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7325 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7326 Compose *compose = (Compose *) data;
7328 compose->priority = value;
7332 static void compose_reply_change_mode(Compose *compose,
7335 gboolean was_modified = compose->modified;
7337 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7339 g_return_if_fail(compose->replyinfo != NULL);
7341 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7343 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7345 if (action == COMPOSE_REPLY_TO_ALL)
7347 if (action == COMPOSE_REPLY_TO_SENDER)
7349 if (action == COMPOSE_REPLY_TO_LIST)
7352 compose_remove_header_entries(compose);
7353 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7354 if (compose->account->set_autocc && compose->account->auto_cc)
7355 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7357 if (compose->account->set_autobcc && compose->account->auto_bcc)
7358 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7360 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7361 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7362 compose_show_first_last_header(compose, TRUE);
7363 compose->modified = was_modified;
7364 compose_set_title(compose);
7367 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7369 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7370 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7371 Compose *compose = (Compose *) data;
7374 compose_reply_change_mode(compose, value);
7377 static void compose_update_priority_menu_item(Compose * compose)
7379 GtkWidget *menuitem = NULL;
7380 switch (compose->priority) {
7381 case PRIORITY_HIGHEST:
7382 menuitem = gtk_ui_manager_get_widget
7383 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7386 menuitem = gtk_ui_manager_get_widget
7387 (compose->ui_manager, "/Menu/Options/Priority/High");
7389 case PRIORITY_NORMAL:
7390 menuitem = gtk_ui_manager_get_widget
7391 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7394 menuitem = gtk_ui_manager_get_widget
7395 (compose->ui_manager, "/Menu/Options/Priority/Low");
7397 case PRIORITY_LOWEST:
7398 menuitem = gtk_ui_manager_get_widget
7399 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7402 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7405 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7407 Compose *compose = (Compose *) data;
7409 gboolean can_sign = FALSE, can_encrypt = FALSE;
7411 g_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7413 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7416 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7417 g_free(compose->privacy_system);
7418 compose->privacy_system = NULL;
7419 if (systemid != NULL) {
7420 compose->privacy_system = g_strdup(systemid);
7422 can_sign = privacy_system_can_sign(systemid);
7423 can_encrypt = privacy_system_can_encrypt(systemid);
7426 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7428 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7429 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7432 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7434 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7435 GtkWidget *menuitem = NULL;
7437 gboolean can_sign = FALSE, can_encrypt = FALSE;
7438 gboolean found = FALSE;
7440 if (compose->privacy_system != NULL) {
7442 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7443 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7444 g_return_if_fail(menuitem != NULL);
7446 amenu = GTK_MENU_SHELL(menuitem)->children;
7448 while (amenu != NULL) {
7449 GList *alist = amenu->next;
7451 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7452 if (systemid != NULL) {
7453 if (strcmp(systemid, compose->privacy_system) == 0 &&
7454 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7455 menuitem = GTK_WIDGET(amenu->data);
7457 can_sign = privacy_system_can_sign(systemid);
7458 can_encrypt = privacy_system_can_encrypt(systemid);
7462 } else if (strlen(compose->privacy_system) == 0 &&
7463 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7464 menuitem = GTK_WIDGET(amenu->data);
7467 can_encrypt = FALSE;
7474 if (menuitem != NULL)
7475 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7477 if (warn && !found && strlen(compose->privacy_system)) {
7478 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7479 "will not be able to sign or encrypt this message."),
7480 compose->privacy_system);
7484 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7485 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7488 static void compose_set_out_encoding(Compose *compose)
7490 CharSet out_encoding;
7491 const gchar *branch = NULL;
7492 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7494 switch(out_encoding) {
7495 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7496 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7497 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7498 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7499 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7500 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7501 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7502 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7503 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7504 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7505 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7506 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7507 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7508 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7509 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7510 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7511 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7512 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7513 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7514 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7515 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7516 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7517 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7518 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7519 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7520 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7521 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7522 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7523 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7524 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7525 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7526 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7528 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7531 static void compose_set_template_menu(Compose *compose)
7533 GSList *tmpl_list, *cur;
7537 tmpl_list = template_get_config();
7539 menu = gtk_menu_new();
7541 gtk_menu_set_accel_group (GTK_MENU (menu),
7542 gtk_ui_manager_get_accel_group(compose->ui_manager));
7543 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7544 Template *tmpl = (Template *)cur->data;
7545 gchar *accel_path = NULL;
7546 item = gtk_menu_item_new_with_label(tmpl->name);
7547 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7548 g_signal_connect(G_OBJECT(item), "activate",
7549 G_CALLBACK(compose_template_activate_cb),
7551 g_object_set_data(G_OBJECT(item), "template", tmpl);
7552 gtk_widget_show(item);
7553 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7554 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7558 gtk_widget_show(menu);
7559 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7562 void compose_update_actions_menu(Compose *compose)
7564 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7567 static void compose_update_privacy_systems_menu(Compose *compose)
7569 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7570 GSList *systems, *cur;
7572 GtkWidget *system_none;
7574 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7575 GtkWidget *privacy_menu = gtk_menu_new();
7577 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7578 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7580 g_signal_connect(G_OBJECT(system_none), "activate",
7581 G_CALLBACK(compose_set_privacy_system_cb), compose);
7583 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7584 gtk_widget_show(system_none);
7586 systems = privacy_get_system_ids();
7587 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7588 gchar *systemid = cur->data;
7590 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7591 widget = gtk_radio_menu_item_new_with_label(group,
7592 privacy_system_get_name(systemid));
7593 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7594 g_strdup(systemid), g_free);
7595 g_signal_connect(G_OBJECT(widget), "activate",
7596 G_CALLBACK(compose_set_privacy_system_cb), compose);
7598 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7599 gtk_widget_show(widget);
7602 g_slist_free(systems);
7603 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7604 gtk_widget_show_all(privacy_menu);
7605 gtk_widget_show_all(privacy_menuitem);
7608 void compose_reflect_prefs_all(void)
7613 for (cur = compose_list; cur != NULL; cur = cur->next) {
7614 compose = (Compose *)cur->data;
7615 compose_set_template_menu(compose);
7619 void compose_reflect_prefs_pixmap_theme(void)
7624 for (cur = compose_list; cur != NULL; cur = cur->next) {
7625 compose = (Compose *)cur->data;
7626 toolbar_update(TOOLBAR_COMPOSE, compose);
7630 static const gchar *compose_quote_char_from_context(Compose *compose)
7632 const gchar *qmark = NULL;
7634 g_return_val_if_fail(compose != NULL, NULL);
7636 switch (compose->mode) {
7637 /* use forward-specific quote char */
7638 case COMPOSE_FORWARD:
7639 case COMPOSE_FORWARD_AS_ATTACH:
7640 case COMPOSE_FORWARD_INLINE:
7641 if (compose->folder && compose->folder->prefs &&
7642 compose->folder->prefs->forward_with_format)
7643 qmark = compose->folder->prefs->forward_quotemark;
7644 else if (compose->account->forward_with_format)
7645 qmark = compose->account->forward_quotemark;
7647 qmark = prefs_common.fw_quotemark;
7650 /* use reply-specific quote char in all other modes */
7652 if (compose->folder && compose->folder->prefs &&
7653 compose->folder->prefs->reply_with_format)
7654 qmark = compose->folder->prefs->reply_quotemark;
7655 else if (compose->account->reply_with_format)
7656 qmark = compose->account->reply_quotemark;
7658 qmark = prefs_common.quotemark;
7662 if (qmark == NULL || *qmark == '\0')
7668 static void compose_template_apply(Compose *compose, Template *tmpl,
7672 GtkTextBuffer *buffer;
7676 gchar *parsed_str = NULL;
7677 gint cursor_pos = 0;
7678 const gchar *err_msg = _("Template body format error at line %d.");
7681 /* process the body */
7683 text = GTK_TEXT_VIEW(compose->text);
7684 buffer = gtk_text_view_get_buffer(text);
7687 qmark = compose_quote_char_from_context(compose);
7689 if (compose->replyinfo != NULL) {
7692 gtk_text_buffer_set_text(buffer, "", -1);
7693 mark = gtk_text_buffer_get_insert(buffer);
7694 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7696 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7697 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7699 } else if (compose->fwdinfo != NULL) {
7702 gtk_text_buffer_set_text(buffer, "", -1);
7703 mark = gtk_text_buffer_get_insert(buffer);
7704 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7706 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7707 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7710 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7712 GtkTextIter start, end;
7715 gtk_text_buffer_get_start_iter(buffer, &start);
7716 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7717 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7719 /* clear the buffer now */
7721 gtk_text_buffer_set_text(buffer, "", -1);
7723 parsed_str = compose_quote_fmt(compose, dummyinfo,
7724 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7725 procmsg_msginfo_free( dummyinfo );
7731 gtk_text_buffer_set_text(buffer, "", -1);
7732 mark = gtk_text_buffer_get_insert(buffer);
7733 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7736 if (replace && parsed_str && compose->account->auto_sig)
7737 compose_insert_sig(compose, FALSE);
7739 if (replace && parsed_str) {
7740 gtk_text_buffer_get_start_iter(buffer, &iter);
7741 gtk_text_buffer_place_cursor(buffer, &iter);
7745 cursor_pos = quote_fmt_get_cursor_pos();
7746 compose->set_cursor_pos = cursor_pos;
7747 if (cursor_pos == -1)
7749 gtk_text_buffer_get_start_iter(buffer, &iter);
7750 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7751 gtk_text_buffer_place_cursor(buffer, &iter);
7754 /* process the other fields */
7756 compose_template_apply_fields(compose, tmpl);
7757 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7758 quote_fmt_reset_vartable();
7759 compose_changed_cb(NULL, compose);
7762 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7764 MsgInfo* dummyinfo = NULL;
7765 MsgInfo *msginfo = NULL;
7768 if (compose->replyinfo != NULL)
7769 msginfo = compose->replyinfo;
7770 else if (compose->fwdinfo != NULL)
7771 msginfo = compose->fwdinfo;
7773 dummyinfo = compose_msginfo_new_from_compose(compose);
7774 msginfo = dummyinfo;
7777 if (tmpl->from && *tmpl->from != '\0') {
7779 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7780 compose->gtkaspell);
7782 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7784 quote_fmt_scan_string(tmpl->from);
7787 buf = quote_fmt_get_buffer();
7789 alertpanel_error(_("Template From format error."));
7791 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
7795 if (tmpl->to && *tmpl->to != '\0') {
7797 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7798 compose->gtkaspell);
7800 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7802 quote_fmt_scan_string(tmpl->to);
7805 buf = quote_fmt_get_buffer();
7807 alertpanel_error(_("Template To format error."));
7809 compose_entry_append(compose, buf, COMPOSE_TO);
7813 if (tmpl->cc && *tmpl->cc != '\0') {
7815 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7816 compose->gtkaspell);
7818 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7820 quote_fmt_scan_string(tmpl->cc);
7823 buf = quote_fmt_get_buffer();
7825 alertpanel_error(_("Template Cc format error."));
7827 compose_entry_append(compose, buf, COMPOSE_CC);
7831 if (tmpl->bcc && *tmpl->bcc != '\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->bcc);
7841 buf = quote_fmt_get_buffer();
7843 alertpanel_error(_("Template Bcc format error."));
7845 compose_entry_append(compose, buf, COMPOSE_BCC);
7849 /* process the subject */
7850 if (tmpl->subject && *tmpl->subject != '\0') {
7852 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7853 compose->gtkaspell);
7855 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7857 quote_fmt_scan_string(tmpl->subject);
7860 buf = quote_fmt_get_buffer();
7862 alertpanel_error(_("Template subject format error."));
7864 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
7868 procmsg_msginfo_free( dummyinfo );
7871 static void compose_destroy(Compose *compose)
7873 GtkTextBuffer *buffer;
7874 GtkClipboard *clipboard;
7876 compose_list = g_list_remove(compose_list, compose);
7878 if (compose->updating) {
7879 debug_print("danger, not destroying anything now\n");
7880 compose->deferred_destroy = TRUE;
7883 /* NOTE: address_completion_end() does nothing with the window
7884 * however this may change. */
7885 address_completion_end(compose->window);
7887 slist_free_strings(compose->to_list);
7888 g_slist_free(compose->to_list);
7889 slist_free_strings(compose->newsgroup_list);
7890 g_slist_free(compose->newsgroup_list);
7891 slist_free_strings(compose->header_list);
7892 g_slist_free(compose->header_list);
7894 procmsg_msginfo_free(compose->targetinfo);
7895 procmsg_msginfo_free(compose->replyinfo);
7896 procmsg_msginfo_free(compose->fwdinfo);
7898 g_free(compose->replyto);
7899 g_free(compose->cc);
7900 g_free(compose->bcc);
7901 g_free(compose->newsgroups);
7902 g_free(compose->followup_to);
7904 g_free(compose->ml_post);
7906 g_free(compose->inreplyto);
7907 g_free(compose->references);
7908 g_free(compose->msgid);
7909 g_free(compose->boundary);
7911 g_free(compose->redirect_filename);
7912 if (compose->undostruct)
7913 undo_destroy(compose->undostruct);
7915 g_free(compose->sig_str);
7917 g_free(compose->exteditor_file);
7919 g_free(compose->orig_charset);
7921 g_free(compose->privacy_system);
7923 if (addressbook_get_target_compose() == compose)
7924 addressbook_set_target_compose(NULL);
7927 if (compose->gtkaspell) {
7928 gtkaspell_delete(compose->gtkaspell);
7929 compose->gtkaspell = NULL;
7933 prefs_common.compose_width = compose->scrolledwin->allocation.width;
7934 prefs_common.compose_height = compose->window->allocation.height;
7936 if (!gtk_widget_get_parent(compose->paned))
7937 gtk_widget_destroy(compose->paned);
7938 gtk_widget_destroy(compose->popupmenu);
7940 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
7941 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7942 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
7944 gtk_widget_destroy(compose->window);
7945 toolbar_destroy(compose->toolbar);
7946 g_free(compose->toolbar);
7947 g_mutex_free(compose->mutex);
7951 static void compose_attach_info_free(AttachInfo *ainfo)
7953 g_free(ainfo->file);
7954 g_free(ainfo->content_type);
7955 g_free(ainfo->name);
7959 static void compose_attach_update_label(Compose *compose)
7964 GtkTreeModel *model;
7969 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
7970 if(!gtk_tree_model_get_iter_first(model, &iter)) {
7971 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
7975 while(gtk_tree_model_iter_next(model, &iter))
7978 text = g_strdup_printf("(%d)", i);
7979 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
7983 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
7985 Compose *compose = (Compose *)data;
7986 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
7987 GtkTreeSelection *selection;
7989 GtkTreeModel *model;
7991 selection = gtk_tree_view_get_selection(tree_view);
7992 sel = gtk_tree_selection_get_selected_rows(selection, &model);
7997 for (cur = sel; cur != NULL; cur = cur->next) {
7998 GtkTreePath *path = cur->data;
7999 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8002 gtk_tree_path_free(path);
8005 for (cur = sel; cur != NULL; cur = cur->next) {
8006 GtkTreeRowReference *ref = cur->data;
8007 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8010 if (gtk_tree_model_get_iter(model, &iter, path))
8011 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8013 gtk_tree_path_free(path);
8014 gtk_tree_row_reference_free(ref);
8018 compose_attach_update_label(compose);
8021 static struct _AttachProperty
8024 GtkWidget *mimetype_entry;
8025 GtkWidget *encoding_optmenu;
8026 GtkWidget *path_entry;
8027 GtkWidget *filename_entry;
8029 GtkWidget *cancel_btn;
8032 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8034 gtk_tree_path_free((GtkTreePath *)ptr);
8037 static void compose_attach_property(GtkAction *action, gpointer data)
8039 Compose *compose = (Compose *)data;
8040 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8042 GtkComboBox *optmenu;
8043 GtkTreeSelection *selection;
8045 GtkTreeModel *model;
8048 static gboolean cancelled;
8050 /* only if one selected */
8051 selection = gtk_tree_view_get_selection(tree_view);
8052 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8055 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8059 path = (GtkTreePath *) sel->data;
8060 gtk_tree_model_get_iter(model, &iter, path);
8061 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8064 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8070 if (!attach_prop.window)
8071 compose_attach_property_create(&cancelled);
8072 gtk_widget_grab_focus(attach_prop.ok_btn);
8073 gtk_widget_show(attach_prop.window);
8074 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8076 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8077 if (ainfo->encoding == ENC_UNKNOWN)
8078 combobox_select_by_data(optmenu, ENC_BASE64);
8080 combobox_select_by_data(optmenu, ainfo->encoding);
8082 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8083 ainfo->content_type ? ainfo->content_type : "");
8084 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8085 ainfo->file ? ainfo->file : "");
8086 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8087 ainfo->name ? ainfo->name : "");
8090 const gchar *entry_text;
8092 gchar *cnttype = NULL;
8099 gtk_widget_hide(attach_prop.window);
8104 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8105 if (*entry_text != '\0') {
8108 text = g_strstrip(g_strdup(entry_text));
8109 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8110 cnttype = g_strdup(text);
8113 alertpanel_error(_("Invalid MIME type."));
8119 ainfo->encoding = combobox_get_active_data(optmenu);
8121 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8122 if (*entry_text != '\0') {
8123 if (is_file_exist(entry_text) &&
8124 (size = get_file_size(entry_text)) > 0)
8125 file = g_strdup(entry_text);
8128 (_("File doesn't exist or is empty."));
8134 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8135 if (*entry_text != '\0') {
8136 g_free(ainfo->name);
8137 ainfo->name = g_strdup(entry_text);
8141 g_free(ainfo->content_type);
8142 ainfo->content_type = cnttype;
8145 g_free(ainfo->file);
8149 ainfo->size = (goffset)size;
8151 /* update tree store */
8152 text = to_human_readable(ainfo->size);
8153 gtk_tree_model_get_iter(model, &iter, path);
8154 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8155 COL_MIMETYPE, ainfo->content_type,
8157 COL_NAME, ainfo->name,
8163 gtk_tree_path_free(path);
8166 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8168 label = gtk_label_new(str); \
8169 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8170 GTK_FILL, 0, 0, 0); \
8171 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8173 entry = gtk_entry_new(); \
8174 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8175 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8178 static void compose_attach_property_create(gboolean *cancelled)
8184 GtkWidget *mimetype_entry;
8187 GtkListStore *optmenu_menu;
8188 GtkWidget *path_entry;
8189 GtkWidget *filename_entry;
8192 GtkWidget *cancel_btn;
8193 GList *mime_type_list, *strlist;
8196 debug_print("Creating attach_property window...\n");
8198 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8199 gtk_widget_set_size_request(window, 480, -1);
8200 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8201 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8202 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8203 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8204 g_signal_connect(G_OBJECT(window), "delete_event",
8205 G_CALLBACK(attach_property_delete_event),
8207 g_signal_connect(G_OBJECT(window), "key_press_event",
8208 G_CALLBACK(attach_property_key_pressed),
8211 vbox = gtk_vbox_new(FALSE, 8);
8212 gtk_container_add(GTK_CONTAINER(window), vbox);
8214 table = gtk_table_new(4, 2, FALSE);
8215 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8216 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8217 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8219 label = gtk_label_new(_("MIME type"));
8220 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8222 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8223 mimetype_entry = gtk_combo_box_entry_new_text();
8224 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8225 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8227 /* stuff with list */
8228 mime_type_list = procmime_get_mime_type_list();
8230 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8231 MimeType *type = (MimeType *) mime_type_list->data;
8234 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8236 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8239 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8240 (GCompareFunc)strcmp2);
8243 for (mime_type_list = strlist; mime_type_list != NULL;
8244 mime_type_list = mime_type_list->next) {
8245 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8246 g_free(mime_type_list->data);
8248 g_list_free(strlist);
8249 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8250 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8252 label = gtk_label_new(_("Encoding"));
8253 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8255 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8257 hbox = gtk_hbox_new(FALSE, 0);
8258 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8259 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8261 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8262 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8264 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8265 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8266 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8267 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8268 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8270 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8272 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8273 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8275 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8276 &ok_btn, GTK_STOCK_OK,
8278 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8279 gtk_widget_grab_default(ok_btn);
8281 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8282 G_CALLBACK(attach_property_ok),
8284 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8285 G_CALLBACK(attach_property_cancel),
8288 gtk_widget_show_all(vbox);
8290 attach_prop.window = window;
8291 attach_prop.mimetype_entry = mimetype_entry;
8292 attach_prop.encoding_optmenu = optmenu;
8293 attach_prop.path_entry = path_entry;
8294 attach_prop.filename_entry = filename_entry;
8295 attach_prop.ok_btn = ok_btn;
8296 attach_prop.cancel_btn = cancel_btn;
8299 #undef SET_LABEL_AND_ENTRY
8301 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8307 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8313 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8314 gboolean *cancelled)
8322 static gboolean attach_property_key_pressed(GtkWidget *widget,
8324 gboolean *cancelled)
8326 if (event && event->keyval == GDK_Escape) {
8333 static void compose_exec_ext_editor(Compose *compose)
8340 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8341 G_DIR_SEPARATOR, compose);
8343 if (pipe(pipe_fds) < 0) {
8349 if ((pid = fork()) < 0) {
8356 /* close the write side of the pipe */
8359 compose->exteditor_file = g_strdup(tmp);
8360 compose->exteditor_pid = pid;
8362 compose_set_ext_editor_sensitive(compose, FALSE);
8364 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8365 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8369 } else { /* process-monitoring process */
8375 /* close the read side of the pipe */
8378 if (compose_write_body_to_file(compose, tmp) < 0) {
8379 fd_write_all(pipe_fds[1], "2\n", 2);
8383 pid_ed = compose_exec_ext_editor_real(tmp);
8385 fd_write_all(pipe_fds[1], "1\n", 2);
8389 /* wait until editor is terminated */
8390 waitpid(pid_ed, NULL, 0);
8392 fd_write_all(pipe_fds[1], "0\n", 2);
8399 #endif /* G_OS_UNIX */
8403 static gint compose_exec_ext_editor_real(const gchar *file)
8410 g_return_val_if_fail(file != NULL, -1);
8412 if ((pid = fork()) < 0) {
8417 if (pid != 0) return pid;
8419 /* grandchild process */
8421 if (setpgid(0, getppid()))
8424 if (prefs_common_get_ext_editor_cmd() &&
8425 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8426 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8427 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8429 if (prefs_common_get_ext_editor_cmd())
8430 g_warning("External editor command line is invalid: '%s'\n",
8431 prefs_common_get_ext_editor_cmd());
8432 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8435 cmdline = strsplit_with_quote(buf, " ", 1024);
8436 execvp(cmdline[0], cmdline);
8439 g_strfreev(cmdline);
8444 static gboolean compose_ext_editor_kill(Compose *compose)
8446 pid_t pgid = compose->exteditor_pid * -1;
8449 ret = kill(pgid, 0);
8451 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8455 msg = g_strdup_printf
8456 (_("The external editor is still working.\n"
8457 "Force terminating the process?\n"
8458 "process group id: %d"), -pgid);
8459 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8460 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8464 if (val == G_ALERTALTERNATE) {
8465 g_source_remove(compose->exteditor_tag);
8466 g_io_channel_shutdown(compose->exteditor_ch,
8468 g_io_channel_unref(compose->exteditor_ch);
8470 if (kill(pgid, SIGTERM) < 0) perror("kill");
8471 waitpid(compose->exteditor_pid, NULL, 0);
8473 g_warning("Terminated process group id: %d", -pgid);
8474 g_warning("Temporary file: %s",
8475 compose->exteditor_file);
8477 compose_set_ext_editor_sensitive(compose, TRUE);
8479 g_free(compose->exteditor_file);
8480 compose->exteditor_file = NULL;
8481 compose->exteditor_pid = -1;
8482 compose->exteditor_ch = NULL;
8483 compose->exteditor_tag = -1;
8491 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8495 Compose *compose = (Compose *)data;
8498 debug_print(_("Compose: input from monitoring process\n"));
8500 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8502 g_io_channel_shutdown(source, FALSE, NULL);
8503 g_io_channel_unref(source);
8505 waitpid(compose->exteditor_pid, NULL, 0);
8507 if (buf[0] == '0') { /* success */
8508 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8509 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8511 gtk_text_buffer_set_text(buffer, "", -1);
8512 compose_insert_file(compose, compose->exteditor_file);
8513 compose_changed_cb(NULL, compose);
8515 if (claws_unlink(compose->exteditor_file) < 0)
8516 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8517 } else if (buf[0] == '1') { /* failed */
8518 g_warning("Couldn't exec external editor\n");
8519 if (claws_unlink(compose->exteditor_file) < 0)
8520 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8521 } else if (buf[0] == '2') {
8522 g_warning("Couldn't write to file\n");
8523 } else if (buf[0] == '3') {
8524 g_warning("Pipe read failed\n");
8527 compose_set_ext_editor_sensitive(compose, TRUE);
8529 g_free(compose->exteditor_file);
8530 compose->exteditor_file = NULL;
8531 compose->exteditor_pid = -1;
8532 compose->exteditor_ch = NULL;
8533 compose->exteditor_tag = -1;
8538 static void compose_set_ext_editor_sensitive(Compose *compose,
8541 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8542 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8543 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8544 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8545 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8546 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8547 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8549 gtk_widget_set_sensitive(compose->text, sensitive);
8550 if (compose->toolbar->send_btn)
8551 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8552 if (compose->toolbar->sendl_btn)
8553 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8554 if (compose->toolbar->draft_btn)
8555 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8556 if (compose->toolbar->insert_btn)
8557 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8558 if (compose->toolbar->sig_btn)
8559 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8560 if (compose->toolbar->exteditor_btn)
8561 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8562 if (compose->toolbar->linewrap_current_btn)
8563 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8564 if (compose->toolbar->linewrap_all_btn)
8565 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8567 #endif /* G_OS_UNIX */
8570 * compose_undo_state_changed:
8572 * Change the sensivity of the menuentries undo and redo
8574 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8575 gint redo_state, gpointer data)
8577 Compose *compose = (Compose *)data;
8579 switch (undo_state) {
8580 case UNDO_STATE_TRUE:
8581 if (!undostruct->undo_state) {
8582 undostruct->undo_state = TRUE;
8583 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8586 case UNDO_STATE_FALSE:
8587 if (undostruct->undo_state) {
8588 undostruct->undo_state = FALSE;
8589 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8592 case UNDO_STATE_UNCHANGED:
8594 case UNDO_STATE_REFRESH:
8595 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8598 g_warning("Undo state not recognized");
8602 switch (redo_state) {
8603 case UNDO_STATE_TRUE:
8604 if (!undostruct->redo_state) {
8605 undostruct->redo_state = TRUE;
8606 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8609 case UNDO_STATE_FALSE:
8610 if (undostruct->redo_state) {
8611 undostruct->redo_state = FALSE;
8612 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8615 case UNDO_STATE_UNCHANGED:
8617 case UNDO_STATE_REFRESH:
8618 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8621 g_warning("Redo state not recognized");
8626 /* callback functions */
8628 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8629 * includes "non-client" (windows-izm) in calculation, so this calculation
8630 * may not be accurate.
8632 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8633 GtkAllocation *allocation,
8634 GtkSHRuler *shruler)
8636 if (prefs_common.show_ruler) {
8637 gint char_width = 0, char_height = 0;
8638 gint line_width_in_chars;
8640 gtkut_get_font_size(GTK_WIDGET(widget),
8641 &char_width, &char_height);
8642 line_width_in_chars =
8643 (allocation->width - allocation->x) / char_width;
8645 /* got the maximum */
8646 gtk_ruler_set_range(GTK_RULER(shruler),
8647 0.0, line_width_in_chars, 0,
8648 /*line_width_in_chars*/ char_width);
8654 static void account_activated(GtkComboBox *optmenu, gpointer data)
8656 Compose *compose = (Compose *)data;
8659 gchar *folderidentifier;
8660 gint account_id = 0;
8664 /* Get ID of active account in the combo box */
8665 menu = gtk_combo_box_get_model(optmenu);
8666 gtk_combo_box_get_active_iter(optmenu, &iter);
8667 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8669 ac = account_find_from_id(account_id);
8670 g_return_if_fail(ac != NULL);
8672 if (ac != compose->account)
8673 compose_select_account(compose, ac, FALSE);
8675 /* Set message save folder */
8676 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8677 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8679 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8680 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8682 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8683 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8684 folderidentifier = folder_item_get_identifier(account_get_special_folder
8685 (compose->account, F_OUTBOX));
8686 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
8687 g_free(folderidentifier);
8691 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8692 GtkTreeViewColumn *column, Compose *compose)
8694 compose_attach_property(NULL, compose);
8697 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8700 Compose *compose = (Compose *)data;
8701 GtkTreeSelection *attach_selection;
8702 gint attach_nr_selected;
8704 if (!event) return FALSE;
8706 if (event->button == 3) {
8707 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8708 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8710 if (attach_nr_selected > 0)
8712 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8713 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8715 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8716 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8719 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8720 NULL, NULL, event->button, event->time);
8727 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8730 Compose *compose = (Compose *)data;
8732 if (!event) return FALSE;
8734 switch (event->keyval) {
8736 compose_attach_remove_selected(NULL, compose);
8742 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8744 toolbar_comp_set_sensitive(compose, allow);
8745 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8746 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8748 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8750 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8751 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8752 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8754 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8758 static void compose_send_cb(GtkAction *action, gpointer data)
8760 Compose *compose = (Compose *)data;
8762 if (prefs_common.work_offline &&
8763 !inc_offline_should_override(TRUE,
8764 _("Claws Mail needs network access in order "
8765 "to send this email.")))
8768 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8769 g_source_remove(compose->draft_timeout_tag);
8770 compose->draft_timeout_tag = -1;
8773 compose_send(compose);
8776 static void compose_send_later_cb(GtkAction *action, gpointer data)
8778 Compose *compose = (Compose *)data;
8782 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8786 compose_close(compose);
8787 } else if (val == -1) {
8788 alertpanel_error(_("Could not queue message."));
8789 } else if (val == -2) {
8790 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8791 } else if (val == -3) {
8792 if (privacy_peek_error())
8793 alertpanel_error(_("Could not queue message for sending:\n\n"
8794 "Signature failed: %s"), privacy_get_error());
8795 } else if (val == -4) {
8796 alertpanel_error(_("Could not queue message for sending:\n\n"
8797 "Charset conversion failed."));
8798 } else if (val == -5) {
8799 alertpanel_error(_("Could not queue message for sending:\n\n"
8800 "Couldn't get recipient encryption key."));
8801 } else if (val == -6) {
8804 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
8807 #define DRAFTED_AT_EXIT "drafted_at_exit"
8808 static void compose_register_draft(MsgInfo *info)
8810 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8811 DRAFTED_AT_EXIT, NULL);
8812 FILE *fp = fopen(filepath, "ab");
8815 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
8823 gboolean compose_draft (gpointer data, guint action)
8825 Compose *compose = (Compose *)data;
8829 MsgFlags flag = {0, 0};
8830 static gboolean lock = FALSE;
8831 MsgInfo *newmsginfo;
8833 gboolean target_locked = FALSE;
8834 gboolean err = FALSE;
8836 if (lock) return FALSE;
8838 if (compose->sending)
8841 draft = account_get_special_folder(compose->account, F_DRAFT);
8842 g_return_val_if_fail(draft != NULL, FALSE);
8844 if (!g_mutex_trylock(compose->mutex)) {
8845 /* we don't want to lock the mutex once it's available,
8846 * because as the only other part of compose.c locking
8847 * it is compose_close - which means once unlocked,
8848 * the compose struct will be freed */
8849 debug_print("couldn't lock mutex, probably sending\n");
8855 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
8856 G_DIR_SEPARATOR, compose);
8857 if ((fp = g_fopen(tmp, "wb")) == NULL) {
8858 FILE_OP_ERROR(tmp, "fopen");
8862 /* chmod for security */
8863 if (change_file_mode_rw(fp, tmp) < 0) {
8864 FILE_OP_ERROR(tmp, "chmod");
8865 g_warning("can't change file mode\n");
8868 /* Save draft infos */
8869 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
8870 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
8872 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
8873 gchar *savefolderid;
8875 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8876 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
8877 g_free(savefolderid);
8879 if (compose->return_receipt) {
8880 err |= (fprintf(fp, "RRCPT:1\n") < 0);
8882 if (compose->privacy_system) {
8883 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
8884 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
8885 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
8888 /* Message-ID of message replying to */
8889 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
8892 folderid = folder_item_get_identifier(compose->replyinfo->folder);
8893 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
8896 /* Message-ID of message forwarding to */
8897 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
8900 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
8901 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
8905 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
8907 /* end of headers */
8908 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
8915 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
8919 if (fclose(fp) == EOF) {
8923 if (compose->targetinfo) {
8924 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
8925 flag.perm_flags = target_locked?MSG_LOCKED:0;
8927 flag.tmp_flags = MSG_DRAFT;
8929 folder_item_scan(draft);
8930 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
8931 MsgInfo *tmpinfo = NULL;
8932 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
8933 if (compose->msgid) {
8934 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
8937 msgnum = tmpinfo->msgnum;
8938 procmsg_msginfo_free(tmpinfo);
8939 debug_print("got draft msgnum %d from scanning\n", msgnum);
8941 debug_print("didn't get draft msgnum after scanning\n");
8944 debug_print("got draft msgnum %d from adding\n", msgnum);
8950 if (action != COMPOSE_AUTO_SAVE) {
8951 if (action != COMPOSE_DRAFT_FOR_EXIT)
8952 alertpanel_error(_("Could not save draft."));
8955 gtkut_window_popup(compose->window);
8956 val = alertpanel_full(_("Could not save draft"),
8957 _("Could not save draft.\n"
8958 "Do you want to cancel exit or discard this email?"),
8959 _("_Cancel exit"), _("_Discard email"), NULL,
8960 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
8961 if (val == G_ALERTALTERNATE) {
8963 g_mutex_unlock(compose->mutex); /* must be done before closing */
8964 compose_close(compose);
8968 g_mutex_unlock(compose->mutex); /* must be done before closing */
8977 if (compose->mode == COMPOSE_REEDIT) {
8978 compose_remove_reedit_target(compose, TRUE);
8981 newmsginfo = folder_item_get_msginfo(draft, msgnum);
8984 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
8986 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
8988 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
8989 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
8990 procmsg_msginfo_set_flags(newmsginfo, 0,
8991 MSG_HAS_ATTACHMENT);
8993 if (action == COMPOSE_DRAFT_FOR_EXIT) {
8994 compose_register_draft(newmsginfo);
8996 procmsg_msginfo_free(newmsginfo);
8999 folder_item_scan(draft);
9001 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9003 g_mutex_unlock(compose->mutex); /* must be done before closing */
9004 compose_close(compose);
9010 path = folder_item_fetch_msg(draft, msgnum);
9012 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9015 if (g_stat(path, &s) < 0) {
9016 FILE_OP_ERROR(path, "stat");
9022 procmsg_msginfo_free(compose->targetinfo);
9023 compose->targetinfo = procmsg_msginfo_new();
9024 compose->targetinfo->msgnum = msgnum;
9025 compose->targetinfo->size = (goffset)s.st_size;
9026 compose->targetinfo->mtime = s.st_mtime;
9027 compose->targetinfo->folder = draft;
9029 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9030 compose->mode = COMPOSE_REEDIT;
9032 if (action == COMPOSE_AUTO_SAVE) {
9033 compose->autosaved_draft = compose->targetinfo;
9035 compose->modified = FALSE;
9036 compose_set_title(compose);
9040 g_mutex_unlock(compose->mutex);
9044 void compose_clear_exit_drafts(void)
9046 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9047 DRAFTED_AT_EXIT, NULL);
9048 if (is_file_exist(filepath))
9049 claws_unlink(filepath);
9054 void compose_reopen_exit_drafts(void)
9056 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9057 DRAFTED_AT_EXIT, NULL);
9058 FILE *fp = fopen(filepath, "rb");
9062 while (fgets(buf, sizeof(buf), fp)) {
9063 gchar **parts = g_strsplit(buf, "\t", 2);
9064 const gchar *folder = parts[0];
9065 int msgnum = parts[1] ? atoi(parts[1]):-1;
9067 if (folder && *folder && msgnum > -1) {
9068 FolderItem *item = folder_find_item_from_identifier(folder);
9069 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9071 compose_reedit(info, FALSE);
9078 compose_clear_exit_drafts();
9081 static void compose_save_cb(GtkAction *action, gpointer data)
9083 compose_draft(data, COMPOSE_KEEP_EDITING);
9086 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9088 if (compose && file_list) {
9091 for ( tmp = file_list; tmp; tmp = tmp->next) {
9092 gchar *file = (gchar *) tmp->data;
9093 gchar *utf8_filename = conv_filename_to_utf8(file);
9094 compose_attach_append(compose, file, utf8_filename, NULL);
9095 compose_changed_cb(NULL, compose);
9100 g_free(utf8_filename);
9105 static void compose_attach_cb(GtkAction *action, gpointer data)
9107 Compose *compose = (Compose *)data;
9110 if (compose->redirect_filename != NULL)
9113 file_list = filesel_select_multiple_files_open(_("Select file"));
9116 compose_attach_from_list(compose, file_list, TRUE);
9117 g_list_free(file_list);
9121 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9123 Compose *compose = (Compose *)data;
9126 file_list = filesel_select_multiple_files_open(_("Select file"));
9131 for ( tmp = file_list; tmp; tmp = tmp->next) {
9132 gchar *file = (gchar *) tmp->data;
9133 gchar *filedup = g_strdup(file);
9134 gchar *shortfile = g_path_get_basename(filedup);
9135 ComposeInsertResult res;
9137 res = compose_insert_file(compose, file);
9138 if (res == COMPOSE_INSERT_READ_ERROR) {
9139 alertpanel_error(_("File '%s' could not be read."), shortfile);
9140 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9141 alertpanel_error(_("File '%s' contained invalid characters\n"
9142 "for the current encoding, insertion may be incorrect."), shortfile);
9148 g_list_free(file_list);
9152 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9154 Compose *compose = (Compose *)data;
9156 compose_insert_sig(compose, FALSE);
9159 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9163 Compose *compose = (Compose *)data;
9165 gtkut_widget_get_uposition(widget, &x, &y);
9166 prefs_common.compose_x = x;
9167 prefs_common.compose_y = y;
9169 if (compose->sending || compose->updating)
9171 compose_close_cb(NULL, compose);
9175 void compose_close_toolbar(Compose *compose)
9177 compose_close_cb(NULL, compose);
9180 static void compose_close_cb(GtkAction *action, gpointer data)
9182 Compose *compose = (Compose *)data;
9186 if (compose->exteditor_tag != -1) {
9187 if (!compose_ext_editor_kill(compose))
9192 if (compose->modified) {
9193 if (!g_mutex_trylock(compose->mutex)) {
9194 /* we don't want to lock the mutex once it's available,
9195 * because as the only other part of compose.c locking
9196 * it is compose_close - which means once unlocked,
9197 * the compose struct will be freed */
9198 debug_print("couldn't lock mutex, probably sending\n");
9201 val = alertpanel(_("Discard message"),
9202 _("This message has been modified. Discard it?"),
9203 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9204 g_mutex_unlock(compose->mutex);
9206 case G_ALERTDEFAULT:
9207 if (prefs_common.autosave)
9208 compose_remove_draft(compose);
9210 case G_ALERTALTERNATE:
9211 compose_draft(data, COMPOSE_QUIT_EDITING);
9218 compose_close(compose);
9221 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9223 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9224 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9225 Compose *compose = (Compose *) data;
9228 compose->out_encoding = (CharSet)value;
9231 static void compose_address_cb(GtkAction *action, gpointer data)
9233 Compose *compose = (Compose *)data;
9235 addressbook_open(compose);
9238 static void about_show_cb(GtkAction *action, gpointer data)
9243 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9245 Compose *compose = (Compose *)data;
9250 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9251 g_return_if_fail(tmpl != NULL);
9253 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9255 val = alertpanel(_("Apply template"), msg,
9256 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9259 if (val == G_ALERTDEFAULT)
9260 compose_template_apply(compose, tmpl, TRUE);
9261 else if (val == G_ALERTALTERNATE)
9262 compose_template_apply(compose, tmpl, FALSE);
9265 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9267 Compose *compose = (Compose *)data;
9269 compose_exec_ext_editor(compose);
9272 static void compose_undo_cb(GtkAction *action, gpointer data)
9274 Compose *compose = (Compose *)data;
9275 gboolean prev_autowrap = compose->autowrap;
9277 compose->autowrap = FALSE;
9278 undo_undo(compose->undostruct);
9279 compose->autowrap = prev_autowrap;
9282 static void compose_redo_cb(GtkAction *action, gpointer data)
9284 Compose *compose = (Compose *)data;
9285 gboolean prev_autowrap = compose->autowrap;
9287 compose->autowrap = FALSE;
9288 undo_redo(compose->undostruct);
9289 compose->autowrap = prev_autowrap;
9292 static void entry_cut_clipboard(GtkWidget *entry)
9294 if (GTK_IS_EDITABLE(entry))
9295 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9296 else if (GTK_IS_TEXT_VIEW(entry))
9297 gtk_text_buffer_cut_clipboard(
9298 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9299 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9303 static void entry_copy_clipboard(GtkWidget *entry)
9305 if (GTK_IS_EDITABLE(entry))
9306 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9307 else if (GTK_IS_TEXT_VIEW(entry))
9308 gtk_text_buffer_copy_clipboard(
9309 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9310 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9313 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9314 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9316 if (GTK_IS_TEXT_VIEW(entry)) {
9317 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9318 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9319 GtkTextIter start_iter, end_iter;
9321 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9323 if (contents == NULL)
9326 /* we shouldn't delete the selection when middle-click-pasting, or we
9327 * can't mid-click-paste our own selection */
9328 if (clip != GDK_SELECTION_PRIMARY) {
9329 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9330 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9333 if (insert_place == NULL) {
9334 /* if insert_place isn't specified, insert at the cursor.
9335 * used for Ctrl-V pasting */
9336 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9337 start = gtk_text_iter_get_offset(&start_iter);
9338 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9340 /* if insert_place is specified, paste here.
9341 * used for mid-click-pasting */
9342 start = gtk_text_iter_get_offset(insert_place);
9343 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9347 /* paste unwrapped: mark the paste so it's not wrapped later */
9348 end = start + strlen(contents);
9349 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9350 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9351 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9352 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9353 /* rewrap paragraph now (after a mid-click-paste) */
9354 mark_start = gtk_text_buffer_get_insert(buffer);
9355 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9356 gtk_text_iter_backward_char(&start_iter);
9357 compose_beautify_paragraph(compose, &start_iter, TRUE);
9359 } else if (GTK_IS_EDITABLE(entry))
9360 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9362 compose->modified = TRUE;
9365 static void entry_allsel(GtkWidget *entry)
9367 if (GTK_IS_EDITABLE(entry))
9368 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9369 else if (GTK_IS_TEXT_VIEW(entry)) {
9370 GtkTextIter startiter, enditer;
9371 GtkTextBuffer *textbuf;
9373 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9374 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9375 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9377 gtk_text_buffer_move_mark_by_name(textbuf,
9378 "selection_bound", &startiter);
9379 gtk_text_buffer_move_mark_by_name(textbuf,
9380 "insert", &enditer);
9384 static void compose_cut_cb(GtkAction *action, gpointer data)
9386 Compose *compose = (Compose *)data;
9387 if (compose->focused_editable
9388 #ifndef GENERIC_UMPC
9389 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9392 entry_cut_clipboard(compose->focused_editable);
9395 static void compose_copy_cb(GtkAction *action, gpointer data)
9397 Compose *compose = (Compose *)data;
9398 if (compose->focused_editable
9399 #ifndef GENERIC_UMPC
9400 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9403 entry_copy_clipboard(compose->focused_editable);
9406 static void compose_paste_cb(GtkAction *action, gpointer data)
9408 Compose *compose = (Compose *)data;
9410 GtkTextBuffer *buffer;
9412 if (compose->focused_editable &&
9413 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9414 entry_paste_clipboard(compose, compose->focused_editable,
9415 prefs_common.linewrap_pastes,
9416 GDK_SELECTION_CLIPBOARD, NULL);
9420 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9422 Compose *compose = (Compose *)data;
9423 gint wrap_quote = prefs_common.linewrap_quote;
9424 if (compose->focused_editable
9425 #ifndef GENERIC_UMPC
9426 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9429 /* let text_insert() (called directly or at a later time
9430 * after the gtk_editable_paste_clipboard) know that
9431 * text is to be inserted as a quotation. implemented
9432 * by using a simple refcount... */
9433 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9434 G_OBJECT(compose->focused_editable),
9435 "paste_as_quotation"));
9436 g_object_set_data(G_OBJECT(compose->focused_editable),
9437 "paste_as_quotation",
9438 GINT_TO_POINTER(paste_as_quotation + 1));
9439 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9440 entry_paste_clipboard(compose, compose->focused_editable,
9441 prefs_common.linewrap_pastes,
9442 GDK_SELECTION_CLIPBOARD, NULL);
9443 prefs_common.linewrap_quote = wrap_quote;
9447 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9449 Compose *compose = (Compose *)data;
9451 GtkTextBuffer *buffer;
9453 if (compose->focused_editable
9454 #ifndef GENERIC_UMPC
9455 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9458 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9459 GDK_SELECTION_CLIPBOARD, NULL);
9463 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9465 Compose *compose = (Compose *)data;
9467 GtkTextBuffer *buffer;
9469 if (compose->focused_editable
9470 #ifndef GENERIC_UMPC
9471 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9474 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9475 GDK_SELECTION_CLIPBOARD, NULL);
9479 static void compose_allsel_cb(GtkAction *action, gpointer data)
9481 Compose *compose = (Compose *)data;
9482 if (compose->focused_editable
9483 #ifndef GENERIC_UMPC
9484 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9487 entry_allsel(compose->focused_editable);
9490 static void textview_move_beginning_of_line (GtkTextView *text)
9492 GtkTextBuffer *buffer;
9496 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9498 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9499 mark = gtk_text_buffer_get_insert(buffer);
9500 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9501 gtk_text_iter_set_line_offset(&ins, 0);
9502 gtk_text_buffer_place_cursor(buffer, &ins);
9505 static void textview_move_forward_character (GtkTextView *text)
9507 GtkTextBuffer *buffer;
9511 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9513 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9514 mark = gtk_text_buffer_get_insert(buffer);
9515 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9516 if (gtk_text_iter_forward_cursor_position(&ins))
9517 gtk_text_buffer_place_cursor(buffer, &ins);
9520 static void textview_move_backward_character (GtkTextView *text)
9522 GtkTextBuffer *buffer;
9526 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9528 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9529 mark = gtk_text_buffer_get_insert(buffer);
9530 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9531 if (gtk_text_iter_backward_cursor_position(&ins))
9532 gtk_text_buffer_place_cursor(buffer, &ins);
9535 static void textview_move_forward_word (GtkTextView *text)
9537 GtkTextBuffer *buffer;
9542 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9544 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9545 mark = gtk_text_buffer_get_insert(buffer);
9546 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9547 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9548 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9549 gtk_text_iter_backward_word_start(&ins);
9550 gtk_text_buffer_place_cursor(buffer, &ins);
9554 static void textview_move_backward_word (GtkTextView *text)
9556 GtkTextBuffer *buffer;
9561 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9563 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9564 mark = gtk_text_buffer_get_insert(buffer);
9565 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9566 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9567 if (gtk_text_iter_backward_word_starts(&ins, 1))
9568 gtk_text_buffer_place_cursor(buffer, &ins);
9571 static void textview_move_end_of_line (GtkTextView *text)
9573 GtkTextBuffer *buffer;
9577 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9579 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9580 mark = gtk_text_buffer_get_insert(buffer);
9581 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9582 if (gtk_text_iter_forward_to_line_end(&ins))
9583 gtk_text_buffer_place_cursor(buffer, &ins);
9586 static void textview_move_next_line (GtkTextView *text)
9588 GtkTextBuffer *buffer;
9593 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9595 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9596 mark = gtk_text_buffer_get_insert(buffer);
9597 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9598 offset = gtk_text_iter_get_line_offset(&ins);
9599 if (gtk_text_iter_forward_line(&ins)) {
9600 gtk_text_iter_set_line_offset(&ins, offset);
9601 gtk_text_buffer_place_cursor(buffer, &ins);
9605 static void textview_move_previous_line (GtkTextView *text)
9607 GtkTextBuffer *buffer;
9612 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9614 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9615 mark = gtk_text_buffer_get_insert(buffer);
9616 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9617 offset = gtk_text_iter_get_line_offset(&ins);
9618 if (gtk_text_iter_backward_line(&ins)) {
9619 gtk_text_iter_set_line_offset(&ins, offset);
9620 gtk_text_buffer_place_cursor(buffer, &ins);
9624 static void textview_delete_forward_character (GtkTextView *text)
9626 GtkTextBuffer *buffer;
9628 GtkTextIter ins, end_iter;
9630 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9632 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9633 mark = gtk_text_buffer_get_insert(buffer);
9634 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9636 if (gtk_text_iter_forward_char(&end_iter)) {
9637 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9641 static void textview_delete_backward_character (GtkTextView *text)
9643 GtkTextBuffer *buffer;
9645 GtkTextIter ins, end_iter;
9647 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9649 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9650 mark = gtk_text_buffer_get_insert(buffer);
9651 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9653 if (gtk_text_iter_backward_char(&end_iter)) {
9654 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9658 static void textview_delete_forward_word (GtkTextView *text)
9660 GtkTextBuffer *buffer;
9662 GtkTextIter ins, end_iter;
9664 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9666 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9667 mark = gtk_text_buffer_get_insert(buffer);
9668 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9670 if (gtk_text_iter_forward_word_end(&end_iter)) {
9671 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9675 static void textview_delete_backward_word (GtkTextView *text)
9677 GtkTextBuffer *buffer;
9679 GtkTextIter ins, end_iter;
9681 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9683 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9684 mark = gtk_text_buffer_get_insert(buffer);
9685 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9687 if (gtk_text_iter_backward_word_start(&end_iter)) {
9688 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9692 static void textview_delete_line (GtkTextView *text)
9694 GtkTextBuffer *buffer;
9696 GtkTextIter ins, start_iter, end_iter;
9698 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9700 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9701 mark = gtk_text_buffer_get_insert(buffer);
9702 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9705 gtk_text_iter_set_line_offset(&start_iter, 0);
9708 if (gtk_text_iter_ends_line(&end_iter)){
9709 if (!gtk_text_iter_forward_char(&end_iter))
9710 gtk_text_iter_backward_char(&start_iter);
9713 gtk_text_iter_forward_to_line_end(&end_iter);
9714 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9717 static void textview_delete_to_line_end (GtkTextView *text)
9719 GtkTextBuffer *buffer;
9721 GtkTextIter ins, end_iter;
9723 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9725 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9726 mark = gtk_text_buffer_get_insert(buffer);
9727 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9729 if (gtk_text_iter_ends_line(&end_iter))
9730 gtk_text_iter_forward_char(&end_iter);
9732 gtk_text_iter_forward_to_line_end(&end_iter);
9733 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9736 #define DO_ACTION(name, act) { \
9737 if(!strcmp(name, a_name)) { \
9741 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
9743 const gchar *a_name = gtk_action_get_name(action);
9744 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
9745 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
9746 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
9747 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
9748 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
9749 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
9750 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
9751 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
9752 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
9753 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
9754 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
9755 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
9756 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
9757 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
9761 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
9763 Compose *compose = (Compose *)data;
9764 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9765 ComposeCallAdvancedAction action = -1;
9767 action = compose_call_advanced_action_from_path(gaction);
9770 void (*do_action) (GtkTextView *text);
9771 } action_table[] = {
9772 {textview_move_beginning_of_line},
9773 {textview_move_forward_character},
9774 {textview_move_backward_character},
9775 {textview_move_forward_word},
9776 {textview_move_backward_word},
9777 {textview_move_end_of_line},
9778 {textview_move_next_line},
9779 {textview_move_previous_line},
9780 {textview_delete_forward_character},
9781 {textview_delete_backward_character},
9782 {textview_delete_forward_word},
9783 {textview_delete_backward_word},
9784 {textview_delete_line},
9785 {textview_delete_to_line_end}
9788 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
9790 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
9791 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
9792 if (action_table[action].do_action)
9793 action_table[action].do_action(text);
9795 g_warning("Not implemented yet.");
9799 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
9803 if (GTK_IS_EDITABLE(widget)) {
9804 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
9805 gtk_editable_set_position(GTK_EDITABLE(widget),
9808 if (widget->parent && widget->parent->parent
9809 && widget->parent->parent->parent) {
9810 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
9811 gint y = widget->allocation.y;
9812 gint height = widget->allocation.height;
9813 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
9814 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
9816 if (y < (int)shown->value) {
9817 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
9819 if (y + height > (int)shown->value + (int)shown->page_size) {
9820 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
9821 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9822 y + height - (int)shown->page_size - 1);
9824 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9825 (int)shown->upper - (int)shown->page_size - 1);
9832 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
9833 compose->focused_editable = widget;
9836 if (GTK_IS_TEXT_VIEW(widget)
9837 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
9838 g_object_ref(compose->notebook);
9839 g_object_ref(compose->edit_vbox);
9840 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9841 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9842 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
9843 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
9844 g_object_unref(compose->notebook);
9845 g_object_unref(compose->edit_vbox);
9846 g_signal_handlers_block_by_func(G_OBJECT(widget),
9847 G_CALLBACK(compose_grab_focus_cb),
9849 gtk_widget_grab_focus(widget);
9850 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9851 G_CALLBACK(compose_grab_focus_cb),
9853 } else if (!GTK_IS_TEXT_VIEW(widget)
9854 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
9855 g_object_ref(compose->notebook);
9856 g_object_ref(compose->edit_vbox);
9857 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9858 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9859 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
9860 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
9861 g_object_unref(compose->notebook);
9862 g_object_unref(compose->edit_vbox);
9863 g_signal_handlers_block_by_func(G_OBJECT(widget),
9864 G_CALLBACK(compose_grab_focus_cb),
9866 gtk_widget_grab_focus(widget);
9867 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9868 G_CALLBACK(compose_grab_focus_cb),
9874 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
9876 compose->modified = TRUE;
9877 #ifndef GENERIC_UMPC
9878 compose_set_title(compose);
9882 static void compose_wrap_cb(GtkAction *action, gpointer data)
9884 Compose *compose = (Compose *)data;
9885 compose_beautify_paragraph(compose, NULL, TRUE);
9888 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
9890 Compose *compose = (Compose *)data;
9891 compose_wrap_all_full(compose, TRUE);
9894 static void compose_find_cb(GtkAction *action, gpointer data)
9896 Compose *compose = (Compose *)data;
9898 message_search_compose(compose);
9901 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
9904 Compose *compose = (Compose *)data;
9905 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9906 if (compose->autowrap)
9907 compose_wrap_all_full(compose, TRUE);
9908 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9911 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
9913 Compose *compose = (Compose *)data;
9915 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9918 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
9920 Compose *compose = (Compose *)data;
9922 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9925 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
9927 g_free(compose->privacy_system);
9929 compose->privacy_system = g_strdup(account->default_privacy_system);
9930 compose_update_privacy_system_menu_item(compose, warn);
9933 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
9935 Compose *compose = (Compose *)data;
9937 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
9938 gtk_widget_show(compose->ruler_hbox);
9939 prefs_common.show_ruler = TRUE;
9941 gtk_widget_hide(compose->ruler_hbox);
9942 gtk_widget_queue_resize(compose->edit_vbox);
9943 prefs_common.show_ruler = FALSE;
9947 static void compose_attach_drag_received_cb (GtkWidget *widget,
9948 GdkDragContext *context,
9951 GtkSelectionData *data,
9956 Compose *compose = (Compose *)user_data;
9959 if (gdk_atom_name(data->type) &&
9960 !strcmp(gdk_atom_name(data->type), "text/uri-list")
9961 && gtk_drag_get_source_widget(context) !=
9962 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
9963 list = uri_list_extract_filenames((const gchar *)data->data);
9964 for (tmp = list; tmp != NULL; tmp = tmp->next) {
9965 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
9966 compose_attach_append
9967 (compose, (const gchar *)tmp->data,
9968 utf8_filename, NULL);
9969 g_free(utf8_filename);
9971 if (list) compose_changed_cb(NULL, compose);
9972 list_free_strings(list);
9974 } else if (gtk_drag_get_source_widget(context)
9975 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
9976 /* comes from our summaryview */
9977 SummaryView * summaryview = NULL;
9978 GSList * list = NULL, *cur = NULL;
9980 if (mainwindow_get_mainwindow())
9981 summaryview = mainwindow_get_mainwindow()->summaryview;
9984 list = summary_get_selected_msg_list(summaryview);
9986 for (cur = list; cur; cur = cur->next) {
9987 MsgInfo *msginfo = (MsgInfo *)cur->data;
9990 file = procmsg_get_message_file_full(msginfo,
9993 compose_attach_append(compose, (const gchar *)file,
9994 (const gchar *)file, "message/rfc822");
10002 static gboolean compose_drag_drop(GtkWidget *widget,
10003 GdkDragContext *drag_context,
10005 guint time, gpointer user_data)
10007 /* not handling this signal makes compose_insert_drag_received_cb
10012 static void compose_insert_drag_received_cb (GtkWidget *widget,
10013 GdkDragContext *drag_context,
10016 GtkSelectionData *data,
10019 gpointer user_data)
10021 Compose *compose = (Compose *)user_data;
10024 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10026 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10027 AlertValue val = G_ALERTDEFAULT;
10029 list = uri_list_extract_filenames((const gchar *)data->data);
10031 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10032 /* Assume a list of no files, and data has ://, is a remote link */
10033 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10034 gchar *tmpfile = get_tmp_file();
10035 str_write_to_file(tmpdata, tmpfile);
10037 compose_insert_file(compose, tmpfile);
10038 claws_unlink(tmpfile);
10040 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10041 compose_beautify_paragraph(compose, NULL, TRUE);
10044 switch (prefs_common.compose_dnd_mode) {
10045 case COMPOSE_DND_ASK:
10046 val = alertpanel_full(_("Insert or attach?"),
10047 _("Do you want to insert the contents of the file(s) "
10048 "into the message body, or attach it to the email?"),
10049 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10050 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10052 case COMPOSE_DND_INSERT:
10053 val = G_ALERTALTERNATE;
10055 case COMPOSE_DND_ATTACH:
10056 val = G_ALERTOTHER;
10059 /* unexpected case */
10060 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10063 if (val & G_ALERTDISABLE) {
10064 val &= ~G_ALERTDISABLE;
10065 /* remember what action to perform by default, only if we don't click Cancel */
10066 if (val == G_ALERTALTERNATE)
10067 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10068 else if (val == G_ALERTOTHER)
10069 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10072 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10073 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10074 list_free_strings(list);
10077 } else if (val == G_ALERTOTHER) {
10078 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10079 list_free_strings(list);
10084 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10085 compose_insert_file(compose, (const gchar *)tmp->data);
10087 list_free_strings(list);
10089 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10092 #if GTK_CHECK_VERSION(2, 8, 0)
10093 /* do nothing, handled by GTK */
10095 gchar *tmpfile = get_tmp_file();
10096 str_write_to_file((const gchar *)data->data, tmpfile);
10097 compose_insert_file(compose, tmpfile);
10098 claws_unlink(tmpfile);
10100 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10104 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10107 static void compose_header_drag_received_cb (GtkWidget *widget,
10108 GdkDragContext *drag_context,
10111 GtkSelectionData *data,
10114 gpointer user_data)
10116 GtkEditable *entry = (GtkEditable *)user_data;
10117 gchar *email = (gchar *)data->data;
10119 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10122 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10123 gchar *decoded=g_new(gchar, strlen(email));
10126 email += strlen("mailto:");
10127 decode_uri(decoded, email); /* will fit */
10128 gtk_editable_delete_text(entry, 0, -1);
10129 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10130 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10134 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10137 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10139 Compose *compose = (Compose *)data;
10141 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10142 compose->return_receipt = TRUE;
10144 compose->return_receipt = FALSE;
10147 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10149 Compose *compose = (Compose *)data;
10151 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10152 compose->remove_references = TRUE;
10154 compose->remove_references = FALSE;
10157 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10158 GdkEventKey *event,
10159 ComposeHeaderEntry *headerentry)
10161 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10162 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10163 !(event->state & GDK_MODIFIER_MASK) &&
10164 (event->keyval == GDK_BackSpace) &&
10165 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10166 gtk_container_remove
10167 (GTK_CONTAINER(headerentry->compose->header_table),
10168 headerentry->combo);
10169 gtk_container_remove
10170 (GTK_CONTAINER(headerentry->compose->header_table),
10171 headerentry->entry);
10172 headerentry->compose->header_list =
10173 g_slist_remove(headerentry->compose->header_list,
10175 g_free(headerentry);
10176 } else if (event->keyval == GDK_Tab) {
10177 if (headerentry->compose->header_last == headerentry) {
10178 /* Override default next focus, and give it to subject_entry
10179 * instead of notebook tabs
10181 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10182 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10189 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10190 ComposeHeaderEntry *headerentry)
10192 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10193 compose_create_header_entry(headerentry->compose);
10194 g_signal_handlers_disconnect_matched
10195 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10196 0, 0, NULL, NULL, headerentry);
10198 /* Automatically scroll down */
10199 compose_show_first_last_header(headerentry->compose, FALSE);
10205 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10207 GtkAdjustment *vadj;
10209 g_return_if_fail(compose);
10210 g_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10211 g_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10213 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10214 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10215 gtk_adjustment_changed(vadj);
10218 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10219 const gchar *text, gint len, Compose *compose)
10221 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10222 (G_OBJECT(compose->text), "paste_as_quotation"));
10225 g_return_if_fail(text != NULL);
10227 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10228 G_CALLBACK(text_inserted),
10230 if (paste_as_quotation) {
10232 const gchar *qmark;
10234 GtkTextIter start_iter;
10237 len = strlen(text);
10239 new_text = g_strndup(text, len);
10241 qmark = compose_quote_char_from_context(compose);
10243 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10244 gtk_text_buffer_place_cursor(buffer, iter);
10246 pos = gtk_text_iter_get_offset(iter);
10248 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10249 _("Quote format error at line %d."));
10250 quote_fmt_reset_vartable();
10252 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10253 GINT_TO_POINTER(paste_as_quotation - 1));
10255 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10256 gtk_text_buffer_place_cursor(buffer, iter);
10257 gtk_text_buffer_delete_mark(buffer, mark);
10259 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10260 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10261 compose_beautify_paragraph(compose, &start_iter, FALSE);
10262 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10263 gtk_text_buffer_delete_mark(buffer, mark);
10265 if (strcmp(text, "\n") || compose->automatic_break
10266 || gtk_text_iter_starts_line(iter))
10267 gtk_text_buffer_insert(buffer, iter, text, len);
10269 /* check if the preceding is just whitespace or quote */
10270 GtkTextIter start_line;
10271 gchar *tmp = NULL, *quote = NULL;
10272 gint quote_len = 0, is_normal = 0;
10273 start_line = *iter;
10274 gtk_text_iter_set_line_offset(&start_line, 0);
10275 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10277 if (*tmp == '\0') {
10280 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10288 gtk_text_buffer_insert(buffer, iter, text, len);
10290 gtk_text_buffer_insert_with_tags_by_name(buffer,
10291 iter, text, len, "no_join", NULL);
10296 if (!paste_as_quotation) {
10297 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10298 compose_beautify_paragraph(compose, iter, FALSE);
10299 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10300 gtk_text_buffer_delete_mark(buffer, mark);
10303 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10304 G_CALLBACK(text_inserted),
10306 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10308 if (prefs_common.autosave &&
10309 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10310 compose->draft_timeout_tag != -2 /* disabled while loading */)
10311 compose->draft_timeout_tag = g_timeout_add
10312 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10314 static gint compose_defer_auto_save_draft(Compose *compose)
10316 compose->draft_timeout_tag = -1;
10317 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10322 static void compose_check_all(GtkAction *action, gpointer data)
10324 Compose *compose = (Compose *)data;
10325 if (compose->gtkaspell)
10326 gtkaspell_check_all(compose->gtkaspell);
10329 static void compose_highlight_all(GtkAction *action, gpointer data)
10331 Compose *compose = (Compose *)data;
10332 if (compose->gtkaspell)
10333 gtkaspell_highlight_all(compose->gtkaspell);
10336 static void compose_check_backwards(GtkAction *action, gpointer data)
10338 Compose *compose = (Compose *)data;
10339 if (compose->gtkaspell)
10340 gtkaspell_check_backwards(compose->gtkaspell);
10342 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10346 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10348 Compose *compose = (Compose *)data;
10349 if (compose->gtkaspell)
10350 gtkaspell_check_forwards_go(compose->gtkaspell);
10352 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10358 *\brief Guess originating forward account from MsgInfo and several
10359 * "common preference" settings. Return NULL if no guess.
10361 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10363 PrefsAccount *account = NULL;
10365 g_return_val_if_fail(msginfo, NULL);
10366 g_return_val_if_fail(msginfo->folder, NULL);
10367 g_return_val_if_fail(msginfo->folder->prefs, NULL);
10369 if (msginfo->folder->prefs->enable_default_account)
10370 account = account_find_from_id(msginfo->folder->prefs->default_account);
10373 account = msginfo->folder->folder->account;
10375 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10377 Xstrdup_a(to, msginfo->to, return NULL);
10378 extract_address(to);
10379 account = account_find_from_address(to, FALSE);
10382 if (!account && prefs_common.forward_account_autosel) {
10383 gchar cc[BUFFSIZE];
10384 if (!procheader_get_header_from_msginfo
10385 (msginfo, cc,sizeof cc , "Cc:")) {
10386 gchar *buf = cc + strlen("Cc:");
10387 extract_address(buf);
10388 account = account_find_from_address(buf, FALSE);
10392 if (!account && prefs_common.forward_account_autosel) {
10393 gchar deliveredto[BUFFSIZE];
10394 if (!procheader_get_header_from_msginfo
10395 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10396 gchar *buf = deliveredto + strlen("Delivered-To:");
10397 extract_address(buf);
10398 account = account_find_from_address(buf, FALSE);
10405 gboolean compose_close(Compose *compose)
10409 if (!g_mutex_trylock(compose->mutex)) {
10410 /* we have to wait for the (possibly deferred by auto-save)
10411 * drafting to be done, before destroying the compose under
10413 debug_print("waiting for drafting to finish...\n");
10414 compose_allow_user_actions(compose, FALSE);
10415 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10418 g_return_val_if_fail(compose, FALSE);
10419 gtkut_widget_get_uposition(compose->window, &x, &y);
10420 prefs_common.compose_x = x;
10421 prefs_common.compose_y = y;
10422 g_mutex_unlock(compose->mutex);
10423 compose_destroy(compose);
10428 * Add entry field for each address in list.
10429 * \param compose E-Mail composition object.
10430 * \param listAddress List of (formatted) E-Mail addresses.
10432 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10435 node = listAddress;
10437 addr = ( gchar * ) node->data;
10438 compose_entry_append( compose, addr, COMPOSE_TO );
10439 node = g_list_next( node );
10443 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10444 guint action, gboolean opening_multiple)
10446 gchar *body = NULL;
10447 GSList *new_msglist = NULL;
10448 MsgInfo *tmp_msginfo = NULL;
10449 gboolean originally_enc = FALSE;
10450 Compose *compose = NULL;
10452 g_return_if_fail(msgview != NULL);
10454 g_return_if_fail(msginfo_list != NULL);
10456 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10457 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10458 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10460 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10461 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10462 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10463 orig_msginfo, mimeinfo);
10464 if (tmp_msginfo != NULL) {
10465 new_msglist = g_slist_append(NULL, tmp_msginfo);
10467 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10468 tmp_msginfo->folder = orig_msginfo->folder;
10469 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10470 if (orig_msginfo->tags)
10471 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10476 if (!opening_multiple)
10477 body = messageview_get_selection(msgview);
10480 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10481 procmsg_msginfo_free(tmp_msginfo);
10482 g_slist_free(new_msglist);
10484 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10486 if (compose && originally_enc) {
10487 compose_force_encryption(compose, compose->account, FALSE);
10493 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10496 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
10497 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10498 GSList *cur = msginfo_list;
10499 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10500 "messages. Opening the windows "
10501 "could take some time. Do you "
10502 "want to continue?"),
10503 g_slist_length(msginfo_list));
10504 if (g_slist_length(msginfo_list) > 9
10505 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10506 != G_ALERTALTERNATE) {
10511 /* We'll open multiple compose windows */
10512 /* let the WM place the next windows */
10513 compose_force_window_origin = FALSE;
10514 for (; cur; cur = cur->next) {
10516 tmplist.data = cur->data;
10517 tmplist.next = NULL;
10518 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10520 compose_force_window_origin = TRUE;
10522 /* forwarding multiple mails as attachments is done via a
10523 * single compose window */
10524 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10528 void compose_set_position(Compose *compose, gint pos)
10530 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10532 gtkut_text_view_set_position(text, pos);
10535 gboolean compose_search_string(Compose *compose,
10536 const gchar *str, gboolean case_sens)
10538 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10540 return gtkut_text_view_search_string(text, str, case_sens);
10543 gboolean compose_search_string_backward(Compose *compose,
10544 const gchar *str, gboolean case_sens)
10546 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10548 return gtkut_text_view_search_string_backward(text, str, case_sens);
10551 /* allocate a msginfo structure and populate its data from a compose data structure */
10552 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10554 MsgInfo *newmsginfo;
10556 gchar buf[BUFFSIZE];
10558 g_return_val_if_fail( compose != NULL, NULL );
10560 newmsginfo = procmsg_msginfo_new();
10563 get_rfc822_date(buf, sizeof(buf));
10564 newmsginfo->date = g_strdup(buf);
10567 if (compose->from_name) {
10568 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10569 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10573 if (compose->subject_entry)
10574 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10576 /* to, cc, reply-to, newsgroups */
10577 for (list = compose->header_list; list; list = list->next) {
10578 gchar *header = gtk_editable_get_chars(
10580 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
10581 gchar *entry = gtk_editable_get_chars(
10582 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10584 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10585 if ( newmsginfo->to == NULL ) {
10586 newmsginfo->to = g_strdup(entry);
10587 } else if (entry && *entry) {
10588 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10589 g_free(newmsginfo->to);
10590 newmsginfo->to = tmp;
10593 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10594 if ( newmsginfo->cc == NULL ) {
10595 newmsginfo->cc = g_strdup(entry);
10596 } else if (entry && *entry) {
10597 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10598 g_free(newmsginfo->cc);
10599 newmsginfo->cc = tmp;
10602 if ( strcasecmp(header,
10603 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10604 if ( newmsginfo->newsgroups == NULL ) {
10605 newmsginfo->newsgroups = g_strdup(entry);
10606 } else if (entry && *entry) {
10607 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10608 g_free(newmsginfo->newsgroups);
10609 newmsginfo->newsgroups = tmp;
10617 /* other data is unset */
10623 /* update compose's dictionaries from folder dict settings */
10624 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10625 FolderItem *folder_item)
10627 g_return_if_fail(compose != NULL);
10629 if (compose->gtkaspell && folder_item && folder_item->prefs) {
10630 FolderItemPrefs *prefs = folder_item->prefs;
10632 if (prefs->enable_default_dictionary)
10633 gtkaspell_change_dict(compose->gtkaspell,
10634 prefs->default_dictionary, FALSE);
10635 if (folder_item->prefs->enable_default_alt_dictionary)
10636 gtkaspell_change_alt_dict(compose->gtkaspell,
10637 prefs->default_alt_dictionary);
10638 if (prefs->enable_default_dictionary
10639 || prefs->enable_default_alt_dictionary)
10640 compose_spell_menu_changed(compose);